How tool calls run
Syncs, calls, approvals, reviews and teammate runs, as your endpoint sees them.
Sync
Median keeps a copy of each endpoint’s manifest. The agent and your team only see tools from the last successful sync.
When a sync runs
| Trigger | From | Rate limit |
|---|---|---|
| Connecting an endpoint or changing its URL | Opening the route with Authorization: Bearer $MEDIAN_KEY, Add endpoint or Change endpoint on Agent → Tools, median tools endpoint add <url>, PUT /v1/tool-endpoints |
No sync limit. Over the API, the request counts toward rate limits |
| A new conversation | Runs before the agent’s first reply. Every endpoint syncs in parallel | None |
| Asking for one | Sync in the menu at the end of the endpoint’s row, median tools endpoint sync, POST /v1/tool-endpoints/sync, POST /v1/tools/sync on the management API |
60 an hour per organization, bursts of 10 |
A deploy needs no hook. The next new conversation picks up the new manifest.
Conversations already open use the last stored sync until one of the triggers
above runs. Over the rate limit, a sync fails with rate_limited and “Too many
requests. Wait a moment and try again.”
Each sync times out after 10 seconds. The first reply waits for it, so an endpoint that never answers delays the first reply of every new conversation.
What a sync changes
| Manifest change | Result |
|---|---|
| New tool | Added and switched on |
| Same name as before | Description, risk and input updated. The on or off switch stays as it was |
| Changed description or input | The label and summary on Agent → Tools are written again |
| Tool left out | Deleted. A request for it that is approved later fails with “The tool is no longer available.” |
| Tool renamed | Treated as a deleted tool plus a new one, switched on |
| Sync fails | The last good tool set stays in use. The endpoint row shows Sync failed: and the reason |
Failure reasons and fixes are in Tool errors.
A tool you switch off on Agent → Tools or with
median tools disable <name> leaves the agent’s next turn and your team’s run
lists. A request for it that is approved later fails with “The tool is no
longer available.”
Remove endpoint deletes the endpoint’s tools. Requests waiting on them expire, with a line in each thread.
One call
The agent sends low and medium calls straight to your endpoint as a
signed POST. median() verifies the signature, validates the input and
runs execute.
{
"tool": "orderStatus",
"toolCallId": "call_9f2c41d07b3e4a6f8c1d2e3f4a5b6c7d",
"input": { "orderNumber": "ORD-1042" },
"context": {
"conversationId": "k57c2x9vx8p4q1m0z3b6n7r2hd6s8t4w",
"risk": "low",
"visitor": { "verified": true, "externalId": "user_123", "email": "ana@example.com" }
}
}
| Rule | Value |
|---|---|
| Signature header | median-signature: t=<ms>,v1=<hex>. v1 is the HMAC-SHA256 of the timestamp, a period and the raw body |
| Timeout | 10 seconds |
| Redirects | Never followed |
| Response body | 100 KB. A larger answer fails the call |
| Result the agent reads | First 4,000 characters |
Tools run in every channel: the widget, email and Discord. The wire format is
in the tool endpoint reference. What execute receives
and may return is in the config reference.
Per turn
A turn is one reply from the agent.
| Rule | Detail |
|---|---|
| 3 calls per turn | Counts low and medium calls sent to your endpoints. After the third, the agent answers with what it has or hands off |
| Identical calls | The same tool with the same input is sent once per turn. The agent reuses the first result |
| One tool at a time | Calls to one tool run in order. Calls to different tools can run in parallel |
| Pages and highlights | A tool whose result offered a page or highlighted something does not run again that turn. See Pages and highlights |
high and reviewed |
Not sent. Each becomes a request and does not count toward the 3 |
What each risk adds to your description
Median appends a line to the description the agent reads.
| Risk | Appended line |
|---|---|
low |
None |
medium |
“Only call this after the customer has explicitly said yes to doing it, in this conversation.” |
reviewed |
The medium line, plus a line saying a reviewer checks the call first |
high |
A line saying the call asks a teammate to sign off first |
Do not write these lines into your own descriptions.
Approvals
A high or reviewed call does not reach your endpoint. Median records a
request, and the agent tells the customer the call needs sign-off or is being
checked.
| Status | Meaning |
|---|---|
reviewing |
A reviewed call the reviewer is reading |
pending |
Waiting on a teammate |
executing |
Approved. The call is out at your endpoint |
succeeded |
Your endpoint answered |
failed |
The call failed, the tool was switched off or removed, or the hourly check found no result 10 minutes after approval |
denied |
A teammate or the reviewer said no |
expired |
Unanswered for 24 hours, its endpoint was removed, or its conversation was resolved, closed or archived while it waited |
median tools approvals and GET /v1/tool-approvals return these statuses.
Rules
- One open request per tool per conversation. When the agent asks again, it is told the request is still waiting and says so to the customer.
- 3 open requests per conversation. Open means
reviewing,pendingorexecuting. - An unanswered request expires after 24 hours, at the moment it runs out.
- Only a request on an open conversation can be decided. Resolving, closing or archiving the conversation expires its open requests, and deciding one then fails with “This request expired because the conversation ended.”
- A request is decided once. A second decision fails with “This request has already been reviewed.”
- An approved call runs once. If it never reports back, the hourly check fails it once 10 minutes have passed, with “The tool returned no result. Check whether the action completed before retrying.”
- After a denial, or once an approved call finishes, the agent gets a turn to tell the customer, if it is still handling the conversation. If a teammate holds the conversation, the outcome waits until it is handed back. An expired request does not wake the agent.
Where teammates decide
Any member can approve or deny.
| Where | How |
|---|---|
| Inbox | Approve and run or Deny on the request card in the thread. See Approvals and tool runs |
| Slack and Discord | Approve and Deny buttons in the mirrored thread, for linked accounts. See Work from Slack and Discord |
| Assistant | Ask it in the app, the command palette, Slack or Discord. It waits for your confirmation before it decides. See Assistant |
| CLI | median tools approvals, median tools approve <id>, median tools deny <id>, median tools status <id>. See CLI reference |
| API and MCP | GET /v1/tool-approvals, POST /v1/tool-approvals/{id}/approve, POST /v1/tool-approvals/{id}/deny. See the management API |
Slack and Discord alerts go out for high requests and for reviewed requests
passed to your team.
Reviewed calls
A reviewed call waits for the customer’s yes, then for a reviewer. The
reviewer is a second AI model that stands in for the teammate a high call
would wait on.
It reads:
- The tool’s name, description and input schema, and the exact input
- Whether your server verified the customer, and their id
- The name, email and page details the customer or page gave
- What earlier conversations taught about the customer
- Your team’s notes on the conversation
- Every other tool call in the conversation
- The whole transcript, with pictures the customer attached
It approves only when the customer said yes to this exact action, the call acts on the customer’s own account or data, the input matches what was discussed, no note says otherwise, and nothing in the conversation reads like someone steering the agent. An unverified customer is not approved for a call that needs to know who they are.
It escalates when it cannot tell from what it was given, or when the call is unusual:
- An amount far larger than the conversation explains
- The same request over and over
- A customer who seems to be testing the agent
- A tool it does not understand
After an escalation or no verdict, the conversation turns unread. If the agent still handles the conversation, it tells the customer a teammate has to approve the call.
| Verdict | Result | Line in the thread |
|---|---|---|
| Approve | Runs. approvedBy is "The reviewer" |
The reviewer approved running <tool> |
| Deny | Denied. The card shows the reason. The agent tells the customer it cannot be done | The reviewer declined <agent>'s request to run <tool> |
| Escalate | Moves to pending for your team. The card shows the reason |
The reviewer passed <tool> to the team |
| No verdict | Moves to pending. Covers a failed review, no decision, and a review past 5 minutes |
<tool> could not be reviewed, so it is with the team |
The customer never sees the reviewer’s reason. A teammate can approve or deny while the reviewer is still reading. The teammate’s decision wins and the verdict is dropped.
Run a tool yourself
A teammate can run any switched-on tool on a conversation, at any risk level, with no sign-off.
| Where | How |
|---|---|
| Inbox | Tools in the panel beside the thread, or / in the composer. See Approvals and tool runs |
| Slack and Discord | /actions. See Work from Slack and Discord |
| Assistant | Ask it. It waits for your confirmation before it runs the tool |
| CLI | median tools run <conversation> <tool> --input '{"orderNumber":"ORD-1042"}' |
| API and MCP | POST /v1/conversations/{id}/tools/run with { "tool", "input" }. GET /v1/conversations/{id}/tools lists what can run |
- Median checks the input against the tool’s schema first. Errors name the field, like “orderNumber is needed to run this.”
- At most 3 runs are in flight per conversation. The fourth fails with “Three tools are already running in this conversation. Wait for one to finish.”
- Archived conversations refuse with “Move this conversation out of the archive before running a tool.” Resolved and closed conversations allow runs.
- The agent is not told. It gets no turn from the run and never sees the result. The result shows on the run’s card in the thread.
- The exception is a run that never reports back. Once 10 minutes have
passed, the hourly check fails it, adds “
<tool>did not run: the call never reported back” to the thread, marks the conversation unread and can start an agent turn.
What your endpoint receives
| Path | approvedBy |
toolCallId |
|---|---|---|
The agent calls a low or medium tool |
Absent | call_ and 32 hex characters |
| A teammate approves a request | The teammate’s name, or "A teammate" |
The request id |
| The reviewer approves a request | "The reviewer" |
The request id |
| A request is approved with a Median key over the API | "the API" |
The request id |
| A teammate runs a tool | The teammate’s name, or "the API" for a key |
The run id |
median tools test |
"A test from the CLI" |
test_ and a UUID |
| You ask Median support to test a tool | "Median support" |
test_ and a UUID |
context.risk is always the tool’s own risk. Treat toolCallId as opaque.
A second request for the same action gets a new id, so check your own state
before acting twice. Tests with no --conversation send the test_ id as
conversationId too. See Custom tools for median tools test.
Roles
| Action | Member | Admin and Owner |
|---|---|---|
| See the tool list on Agent → Tools | Read only | Yes |
| See endpoints on Agent → Tools | No | Yes |
Read endpoints with median tools list or GET /v1/tool-endpoints |
Yes | Yes |
| Add, change, sync and remove endpoints | No | Yes |
| Switch tools on or off | No | Yes |
| Approve or deny requests | Yes | Yes |
| Run a tool on a conversation | Yes | Yes |
median tools test |
No | Yes |
| See and settle Suggested tools | No | Yes |
| See tool events on Logs | No | Yes |
The full matrix is in roles.
Limits
| Limit | Value |
|---|---|
| Tool endpoints per organization | 10 |
| Tools per organization, across endpoints | 20 |
| Endpoint URL | 512 characters. HTTPS, or HTTP on localhost and 127.0.0.1 |
| Tool and field names | A letter, then letters, numbers and underscores, up to 64 characters |
| Description | 500 characters |
| Input fields per tool | 20 flat scalars |
| Input schema | 8,000 characters, serialized |
| Manifest | 500 KB |
| Sync timeout | 10 seconds |
| Manual syncs | 60 an hour per organization, bursts of 10 |
| Call timeout | 10 seconds |
| Response body | 100 KB. A larger answer fails the call |
| Result the agent reads | First 4,000 characters |
| Calls sent per agent turn | 3 |
| Open requests per conversation | 3 |
| Open requests per tool per conversation | 1 |
| Request lifetime | 24 hours |
| Review time | 5 minutes, then your team |
| Approved call or teammate run with no result | Failed at the first hourly check after 10 minutes |
| Teammate runs in flight per conversation | 3 |
| Signature tolerance | 5 minutes by default |
API rate limits for the CLI and API paths are in rate limits.
Logs
Tool events appear on Logs under the Tools type.
| Event | Logged when |
|---|---|
| Tool call | Median sends a call to your endpoint: the agent’s, an approved request, a teammate run or a test. Carries the outcome, error and duration |
| Approval requested | The agent asks for sign-off or a review, or the reviewer passes a call to your team |
| Tool call approved | A teammate, a key or the reviewer approves a request |
| Tool call declined | A teammate, a key or the reviewer denies a request |
| Approval expired | A request goes unanswered for 24 hours, its conversation ends, or its endpoint is removed |
| Tools synced | A sync finishes or fails. A failure repeating the last error is not logged again |
| Tool endpoint changed | An endpoint is added, moved or removed |
| Tool switched on or off | A tool’s switch changes |
| Tool suggested | The agent or a resolved-conversation review files a suggested tool |