Skip to main content

Custom Endpoints

Custom endpoints are the agent's callable tools: an outbound HTTP request the agent makes mid-call, for example "create a lead in the CRM" or "look up an order status". The agent decides when to call one from its description, VOCALS makes the request, and the response is fed back into the same turn. For the behaviour and the dashboard editor, see Custom Endpoints in the user guide.

Endpoints are scoped to a single agent and live under /agents/{agent_id}/custom-endpoints. Every route accepts the same authentication as the rest of the public API: send your key in the X-API-Key header (a Bearer JWT also works), so tools can be registered and updated from a script rather than only from the dashboard. The key's workspace is the only workspace it can reach - an agent owned by anyone else answers 404 Not Found. The base URL is https://api.usevocals.com/api/v1.

Endpoints

MethodPathPurpose
GET/agents/{agent_id}/custom-endpointsList the agent's endpoints (oldest first)
POST/agents/{agent_id}/custom-endpointsCreate an endpoint
GET/agents/{agent_id}/custom-endpoints/{endpoint_id}Fetch a single endpoint
PATCH/agents/{agent_id}/custom-endpoints/{endpoint_id}Update an endpoint
DELETE/agents/{agent_id}/custom-endpoints/{endpoint_id}Delete an endpoint

Each agent can hold up to 20 endpoints; creating another beyond the cap returns 422 Unprocessable Entity.

Endpoint Schema

CustomEndpointResponse is returned by the list, get, create, and update routes.

FieldTypeDescription
iduuidEndpoint ID
agent_iduuidOwning agent ID
namestringTool name the AI uses to refer to this endpoint
descriptionstringTells the AI when and why to call the tool
http_methodstringGET, POST, PUT, PATCH, or DELETE
url_templatestringTarget URL, may contain {{variable}} placeholders
headersarrayConfigured headers as { "name": ..., "has_value": true }. Stored values are never returned.
body_templatestring | nullRequest body, may contain {{variable}} placeholders
parametersarrayParameter definitions the AI fills in from the conversation
timeout_secondsintegerRequest timeout, 1 to 60
is_enabledbooleanWhether the agent is offered the tool
created_atdatetimeCreation timestamp
updated_atdatetimeLast update timestamp

Header values are write-only

A header value is a secret. It is encrypted on save and never returned by any route: reads show only has_value, so there is no API call that reads a stored value back. To rotate one, send the header again with a new value.

Parameters

Each entry in parameters describes one value the AI collects during the conversation and supplies when it invokes the tool:

FieldTypeRequiredDescription
namestringYesLowercase letters, digits, and underscores, starting with a letter. Must be unique within the endpoint and must not collide with a built-in variable name.
descriptionstringYesWhat the AI should collect
typestringYesstring, number, integer, or boolean
requiredbooleanNoWhether the AI must supply it (default false)

Reference a parameter as {{name}} in url_template, any header value, or body_template. The same placeholders also resolve built-in and per-agent custom variables - see Dynamic Variables.

List Endpoints

GET /agents/{agent_id}/custom-endpoints

Returns the agent's endpoints, oldest first.

curl -H "X-API-Key: voc_a1b2c3d4e5f6..." \
https://api.usevocals.com/api/v1/agents/{agent_id}/custom-endpoints

Response

[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"agent_id": "9f8b7c6d-1234-4a5b-9c8d-abcdef012345",
"name": "create_lead",
"description": "Create a lead in the CRM once the caller gives their name.",
"http_method": "POST",
"url_template": "https://api.example.com/v1/leads",
"headers": [{ "name": "Authorization", "has_value": true }],
"body_template": "{\"name\": \"{{customer_name}}\", \"phone\": \"{{caller_number}}\"}",
"parameters": [
{
"name": "customer_name",
"description": "The caller's full name",
"type": "string",
"required": true
}
],
"timeout_seconds": 8,
"is_enabled": true,
"created_at": "2026-06-01T10:30:00Z",
"updated_at": "2026-06-01T10:30:00Z"
}
]

Create Endpoint

POST /agents/{agent_id}/custom-endpoints

Request Body

{
"name": "create_lead",
"description": "Create a lead in the CRM once the caller gives their name.",
"http_method": "POST",
"url_template": "https://api.example.com/v1/leads",
"headers": [{ "name": "Authorization", "value": "Bearer crm-token" }],
"body_template": "{\"name\": \"{{customer_name}}\"}",
"parameters": [
{
"name": "customer_name",
"description": "The caller's full name",
"type": "string",
"required": true
}
],
"timeout_seconds": 8,
"is_enabled": true
}

Request Fields

FieldTypeRequiredDefaultDescription
namestringYes--Tool name. Lowercase letters, digits, and underscores, starting with a letter, up to 64 characters. Must be unique on the agent and must not start with hubspot_, calendar_, shopify_, or custom_.
descriptionstringYes--When and why the AI should call the tool
http_methodstringYes--GET, POST, PUT, PATCH, or DELETE
url_templatestringYes--Target URL. Must use http or https and resolve to a public address.
headersarrayNo[]Request headers, each { "name": ..., "value": ... }. A value is required on create.
body_templatestringNonullRequest body. Sent as application/json unless you configure your own Content-Type header.
parametersarrayNo[]Parameter definitions (see above)
timeout_secondsintegerNo10Request timeout, 1 to 60
is_enabledbooleanNotrueWhether the agent is offered the tool
curl -X POST https://api.usevocals.com/api/v1/agents/{agent_id}/custom-endpoints \
-H "X-API-Key: voc_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{
"name": "create_lead",
"description": "Create a lead in the CRM once the caller gives their name.",
"http_method": "POST",
"url_template": "https://api.example.com/v1/leads",
"headers": [{ "name": "Authorization", "value": "Bearer crm-token" }],
"body_template": "{\"name\": \"{{customer_name}}\"}",
"parameters": [
{ "name": "customer_name", "description": "The caller'\''s full name", "type": "string", "required": true }
],
"timeout_seconds": 8
}'

Response

201 Created - Returns the CustomEndpointResponse. The header value you sent is not echoed back; it shows as has_value: true.

The API returns 422 Unprocessable Entity when the name is malformed, already used on this agent, or starts with a reserved prefix; when the method is unsupported; when the URL is not a public http/https address; when a parameter is malformed, duplicated, or collides with a built-in variable name; when timeout_seconds is outside 1 to 60; or when the agent is already at the 20-endpoint cap. It returns 404 Not Found when the agent does not exist in your workspace.

Get Endpoint

GET /agents/{agent_id}/custom-endpoints/{endpoint_id}
curl -H "X-API-Key: voc_a1b2c3d4e5f6..." \
https://api.usevocals.com/api/v1/agents/{agent_id}/custom-endpoints/{endpoint_id}

Response

200 OK - Returns the CustomEndpointResponse, or 404 Not Found.

Update Endpoint

PATCH /agents/{agent_id}/custom-endpoints/{endpoint_id}

Partial update: send only the fields you want to change. Toggling a tool off, for example, is a one-field call.

curl -X PATCH https://api.usevocals.com/api/v1/agents/{agent_id}/custom-endpoints/{endpoint_id} \
-H "X-API-Key: voc_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{"is_enabled": false}'

Updating headers

headers is replaced wholesale, not merged: the list you send becomes the endpoint's complete header set, and any header you leave out is removed. Within that list, each entry either carries a new value (which replaces the stored secret) or omits value entirely (which keeps the stored secret). Omitting value for a header that has none stored returns 422.

{
"headers": [
{ "name": "Authorization" },
{ "name": "X-Account", "value": "acct_42" }
]
}

That request keeps the stored Authorization secret untouched and sets a new X-Account value.

Response

200 OK - Returns the updated CustomEndpointResponse. The same 422 validation rules as create apply.

Delete Endpoint

DELETE /agents/{agent_id}/custom-endpoints/{endpoint_id}

Deletes an endpoint. The agent stops being offered the tool from the next call onward; calls already in progress are unaffected.

curl -X DELETE https://api.usevocals.com/api/v1/agents/{agent_id}/custom-endpoints/{endpoint_id} \
-H "X-API-Key: voc_a1b2c3d4e5f6..."

Response

204 No Content - No response body.