For Developers
Agents

Custom functions

Read and replace the functions an agent can call on your API during a conversation.

A custom function lets an agent call your HTTPS endpoint during a conversation and use the result. An agent can have up to 8. For a step-by-step setup, see Give agents custom functions.

A missing or invalid key returns 401. :id must be the agent's UUID, or the route returns 400. An agent in another account returns 404.

A body or :id that fails validation returns 400 with the code VALIDATION_ERROR, and details.issues lists the problems. A rejected address or header returns the same code with the reason in error. See Errors.

The save route accepts a body up to 2 MB. Every other route has a 256 KB limit.

A key with write, delete or admin permission qualifies. A key whose owner has the viewer role gets 403 on both routes, whatever its permissions.

EndpointRequest
Get custom functionsGET/v1/agents/:id/custom-tools
Replace custom functionsPUT/v1/agents/:id/custom-tools

The custom functions object

Attributes

versionstring | nullrequired
UUID of the current list. null until the first save. Send it as expectedVersion on your next save.
syncStatusstringrequired
synced or pending. Functions don't run on calls while this is pending. See Sync status.
toolsarrayrequired
The agent's functions.

The function object

All fields are required when you save, and other fields are rejected.

Attributes

idstringrequired
A UUID you choose. It must be unique within the list. Keep it stable, because stored header values are matched by this ID.
namestringrequired
Up to 40 letters, numbers and underscores, starting with a letter. It must be unique within the list.
descriptionstringrequired
What the function does and when to use it, from 1 to 2000 characters.
urlstringrequired
Your endpoint, up to 2048 characters. It must use HTTPS on port 443, with no credentials or fragment.
methodstringrequired
GET, POST, PUT, PATCH or DELETE.
timeoutSecondsintegerrequired
Time allowed for the request, from 1 to 20 seconds.
maxRetriesintegerrequired
From 0 to 2. Any value above 0 is allowed only for GET.
headersarrayrequired
Up to 16 request headers. See Header values.
queryParametersarrayrequired
Up to 20 fixed parameters added to the address. Each has a name of 1 to 128 characters and a value of up to 4096. Names must be unique.
fieldsarrayrequired
Up to 20 inputs the agent fills in. See below.
messagestringrequired
What the agent says when the function starts, up to 1000 characters. Use an empty string for none.
enabledbooleanrequired
Whether the agent can use the function. Disabled functions stay saved.

fields[]

namestringrequired
Up to 40 letters, numbers and underscores, starting with a letter. It must be unique within the function. constructor and prototype are reserved.
typestringrequired
string, number, integer or boolean.
descriptionstringrequired
What the agent should provide, up to 1000 characters. It can be empty.
requiredbooleanrequired
Whether the agent must provide this field.

For GET functions, a query parameter can't share a name with a field.

Header values

Header values are encrypted when saved and never returned. Responses show each stored header as { "name": "Authorization", "value": null, "hasValue": true }.

ToSend
Add a header or change its value{ "name": "Authorization", "value": "Bearer YOUR_TOKEN" }
Keep a stored value{ "name": "Authorization", "value": null }
Remove a headerLeave it out of the list.

Header names must be valid HTTP header names of up to 128 characters. They are unique regardless of case, and matched to stored values the same way. Values must be 1 to 4096 characters and can't contain line breaks or null characters.

Changing a function's endpoint to a different host requires a new value for every header. This stops stored credentials going to another service.

You can't set these headers, because Synthesys manages them: Host, Content-Length, Connection, Transfer-Encoding, Content-Type, Accept-Encoding, Cookie, Set-Cookie, Proxy-Authorization, Idempotency-Key and anything starting with X-Synthesys-. Values you save for Accept and User-Agent are replaced when the request is sent.

Get custom functions

GET/v1/agents/:id/custom-toolswrite key

Returns the agent's functions with header values masked. An agent with no saved functions returns version: null, syncStatus: "synced" and an empty tools list.

StatusWhen
403The key lacks write permission, or its owner has the viewer role.
404The agent doesn't exist or belongs to another account.
Request
curl https://backend.synthesys.app/api/v1/agents/3fa85f64-5717-4562-b3fc-2c963f66afa6/custom-tools \
  -H "SYNTH_API_KEY: YOUR_API_KEY"
Response
{
  "success": true,
  "data": {
    "version": "8c4e2a17-6f93-4b05-a2d8-1e7c9b3f5d60",
    "syncStatus": "synced",
    "tools": [
      {
        "id": "7f3c1e52-9a4b-4d6e-8c21-5b0e7d3a1f64",
        "name": "check_order_status",
        "description": "Look up an order's delivery status when the caller asks about an order.",
        "url": "https://example.com/api/orders/status",
        "method": "GET",
        "timeoutSeconds": 10,
        "maxRetries": 1,
        "headers": [
          { "name": "Authorization", "value": null, "hasValue": true }
        ],
        "queryParameters": [
          { "name": "source", "value": "voice-agent" }
        ],
        "fields": [
          {
            "name": "order_number",
            "type": "string",
            "description": "The order number the caller gives.",
            "required": true
          }
        ],
        "message": "One moment while I check that order.",
        "enabled": true
      }
    ]
  }
}

Replace custom functions

PUT/v1/agents/:id/custom-toolswrite key

Replaces the agent's whole list and applies it to the agent. Any function you leave out is removed, and an empty tools list removes them all. Returns the same shape as Get custom functions.

Body

expectedVersionstring | nullrequired
null on the first save. After that, the version from your latest response.
toolsarrayrequired
The complete list, up to 8 functions. Names and IDs must be unique.

The address of every function must pass the format rules. Enabled functions with a new address, and functions you enable, must also resolve only to public internet addresses.

StatusWhen
400The body is invalid, an address fails its checks, a new header has value: null, or a host changed while a header has value: null.
403The key lacks write permission, or its owner has the viewer role.
404The agent doesn't exist or belongs to another account.
409expectedVersion is out of date. Read the list again and retry.
Request
curl -X PUT https://backend.synthesys.app/api/v1/agents/3fa85f64-5717-4562-b3fc-2c963f66afa6/custom-tools \
  -H "SYNTH_API_KEY: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "expectedVersion": null,
    "tools": [
      {
        "id": "7f3c1e52-9a4b-4d6e-8c21-5b0e7d3a1f64",
        "name": "check_order_status",
        "description": "Look up the delivery status when the caller asks about an order.",
        "url": "https://example.com/api/orders/status",
        "method": "GET",
        "timeoutSeconds": 10,
        "maxRetries": 1,
        "headers": [{ "name": "Authorization", "value": "Bearer YOUR_TOKEN" }],
        "queryParameters": [{ "name": "source", "value": "voice-agent" }],
        "fields": [
          {
            "name": "order_number",
            "type": "string",
            "description": "The order number the caller gives.",
            "required": true
          }
        ],
        "message": "One moment while I check that order.",
        "enabled": true
      }
    ]
  }'
Response
{
  "success": true,
  "data": {
    "version": "8c4e2a17-6f93-4b05-a2d8-1e7c9b3f5d60",
    "syncStatus": "synced",
    "tools": [
      {
        "id": "7f3c1e52-9a4b-4d6e-8c21-5b0e7d3a1f64",
        "name": "check_order_status",
        "description": "Look up the delivery status when the caller asks about an order.",
        "url": "https://example.com/api/orders/status",
        "method": "GET",
        "timeoutSeconds": 10,
        "maxRetries": 1,
        "headers": [
          { "name": "Authorization", "value": null, "hasValue": true }
        ],
        "queryParameters": [
          { "name": "source", "value": "voice-agent" }
        ],
        "fields": [
          {
            "name": "order_number",
            "type": "string",
            "description": "The order number the caller gives.",
            "required": true
          }
        ],
        "message": "One moment while I check that order.",
        "enabled": true
      }
    ]
  }
}
Stale version (409)
{
  "success": false,
  "error": "Tools changed. Reload before saving.",
  "code": "CONFLICT",
  "requestId": "0f6b2c9e-1d47-4a83-b5e2-7c3a9d1f4e60",
  "timestamp": "2026-10-31T12:00:00.000Z"
}

Sync status

A save applies the list to the agent in the same request, so there is no separate sync route. If that step fails, the save is kept, the route still returns 200, and syncStatus is pending.

While it is pending, all of the agent's custom functions fail on calls. Check syncStatus after every save. To retry, send the same list again with the latest version, using value: null for stored headers.

What your endpoint receives

When the agent uses a function, Synthesys checks its arguments against fields. Unknown fields, missing required fields and wrong types are rejected without calling your endpoint. Arguments are limited to 16 KB.

MethodWhere arguments go
GETAdded to the query string, with queryParameters.
POST, PUT, PATCH, DELETESent as a JSON body. queryParameters are added to the address.

Requests include your saved headers, plus these:

HeaderValue
Idempotency-KeyA hash unique to this function call. It stays the same across retries.
Content-Typeapplication/json
Acceptapplication/json, text/plain
Accept-Encodingidentity
User-AgentSynthesys-Custom-Tool/1.0

How your response is handled

Return a 2xx status with a JSON or plain text body of up to 16 KB. Don't compress the response. The agent receives the status and the parsed body.

Any other status counts as a failure, and the agent is told the status code but not the body. Redirects are not followed. A body that is too large, or a compressed response, also counts as a failure.

Requests must finish within timeoutSeconds, including retries. Only GET functions retry, up to maxRetries times, after a network failure or 5xx response. Other methods are never retried, because a write may already have taken effect.

Each attempt checks that the address still resolves only to public internet addresses.