> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getnexor.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Test Workflow Tool

> Run a custom tool once, exactly as the agent calls it, and see the request, the response, and what the agent reads.

This endpoint answers "does this tool work the way the agent will call it?". It runs one of a task's custom HTTP tools once, through the same path the agent uses in a real conversation, and returns what was sent, what came back, and what the agent would read. Use it right after you set up or change a tool, before you turn the task live.

## What the test does

* It builds the request exactly like the agent: `{{env.KEY}}` secrets, context placeholders, `{argument}` path parameters, the remaining arguments as query parameters (GET) or as the body (other methods), and the tool's saved headers.
* It works on inactive tools, so you can test a tool before you turn it on.
* It calls your external system for real.
* Unlike a conversation, it only reaches public hosts, does not follow redirects, and runs at most once. A URL that points to a loopback, private, link-local, or cloud metadata address is refused before anything is sent.
* Nothing changes in Nexor: no lead, stage, field, or conversation is touched, and the run does not appear in [Tool health](/docs/en/api/reports/get-tool-health).

## Before you start

* MCP keys need `workflows:write`, because the test reaches your system for real. Full-access REST keys need nothing else.
* Get `toolId` from [List workflow tools](/docs/en/api/workflow-tools/list-workflow-tools). Only the task's custom tools can be tested.
* Nexor platform tools return 400 `internal_tool_not_testable`. Test those in Playground.

## Tools that change data

GET tools run directly. A tool whose method is POST, PUT, PATCH, or DELETE changes data in your system, so the test only runs with `confirm_write: true`. Without it the answer is 409 `write_requires_confirmation` and nothing is sent.

<Warning>
  Send `confirm_write: true` only after the person who owns the external system agreed, use test data, and define the cleanup before the call. Cancel an appointment created by a booking test. For rescheduling and cancellation, use a disposable fixture or a restore operation supported by the provider.
</Warning>

## What to send

| Field | Detail |
| - | - |
| `args` | The arguments the agent would pass, checked against the tool's `parameters`. Optional, default `{}`. |
| `lead_metadata` | Sample lead metadata, used to fill `{{lead.metadata.X}}` and `{X}` placeholders as a real lead's metadata would. Optional, default `{}`. |
| `confirm_write` | `true` to run a tool that changes data. Ignored for GET tools. |

A test has no real lead. Placeholders that read other lead data, such as `{{lead.phone}}`, stay unresolved and appear in `checks.unresolved_placeholders`. `{{lead_id}}` is sent empty.

## Placeholders in the tool URL

| Write | For | Example |
| - | - | - |
| `{name}` (single braces) | A tool argument. Nexor puts the argument's value in the URL and does not send it again in the query or body. When the lead's metadata has a key with the same name, that value is used instead. | `/appointments/{appointment_id}` |
| `{{...}}` (double braces) | A value Nexor fills before the call: an account secret `{{env.KEY}}`, lead data such as `{{lead.metadata.X}}`, or context such as `{{lead_id}}`. | `?token={{env.SCHEDULING_TOKEN}}` |

A tool argument in double braces, such as `/appointments/{{appointment_id}}`, never gets a value: the literal text reaches your system. Saving a tool like that is rejected with 400 `invalid_url_placeholder`, which names the arguments in `fields` and proposes the corrected URL in `suggested_url`:

```json theme={null}
{
  "error": "invalid_url_placeholder",
  "message": "The URL uses {{appointment_id}} for a tool argument. Tool arguments use single braces: {appointment_id}. Double braces are only for context values such as {{env.KEY}}, {{lead.metadata.X}} and {{lead_id}}.",
  "fields": ["appointment_id"],
  "suggested_url": "https://api.example.com/v1/appointments/{appointment_id}"
}
```

Tools saved before this check existed are not changed. Test them: a leftover argument in double braces shows up in `checks.unresolved_placeholders`.

## How to read the result

`success` only says the test could run. The verdict is `passed`, which is `true` when the request was sent, your system answered with a 2xx and a usable body, and the final URL has no unresolved placeholder.

| What you see | What it usually means | What to do |
| - | - | - |
| `called: false` | No answer came back. Usually the request never left Nexor: `args` do not match the tool's parameters, a `{{env.KEY}}` secret is missing, or the final URL points to a host that is not public. It also happens when the request timed out. | Read `agent_sees`: it names the invalid arguments, the missing keys, the refused address, or the timeout. |
| Items in `checks.unresolved_placeholders` | The final URL still has a `{{...}}` placeholder. Usually a tool argument written in double braces, or lead data that a test does not have. | Use single braces for arguments. Send the value in `lead_metadata` when it comes from the lead. |
| `response.status` 401 or 403 | Your system rejected the credential. | Check the secret or header configured on the tool. |
| `response.status` 3xx | Your system answered with a redirect. The test does not follow redirects, so `passed` is `false`. | Point the tool at the final URL. |
| `response.status` 404 | The path is wrong, or a path parameter was not filled. | Compare `request.url` with your API's documentation. |
| `response.ok: true` but the wrong data | The endpoint answers, but it is not the one the agent needs. For example, a list of booked appointments instead of free slots. | Point the tool at the endpoint that answers the agent's question. |
| `agent_sees` shorter than `body_excerpt` | Nexor cleans the response and, when the tool has `llm_response_fields`, keeps only those fields. | Check that the fields the agent needs are still there. |

## Limits

* One attempt per request, up to the tool's timeout. A test that times out is not retried, so a tool that changes data runs at most once.
* `body_excerpt` and `agent_sees` are cut at 2,000 characters.
* Values that come from `{{env.KEY}}` appear in `request.url` as `[Redacted]`. Headers are not returned. Values you send in `args` or `lead_metadata` are returned as sent.


## API Specification

The full API specification for this endpoint is available in the [documentation index](https://docs.getnexor.ai/llms.txt).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.