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
| Method | Path | Purpose |
|---|---|---|
GET | /agents/{agent_id}/custom-endpoints | List the agent's endpoints (oldest first) |
POST | /agents/{agent_id}/custom-endpoints | Create 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.
| Field | Type | Description |
|---|---|---|
id | uuid | Endpoint ID |
agent_id | uuid | Owning agent ID |
name | string | Tool name the AI uses to refer to this endpoint |
description | string | Tells the AI when and why to call the tool |
http_method | string | GET, POST, PUT, PATCH, or DELETE |
url_template | string | Target URL, may contain {{variable}} placeholders |
headers | array | Configured headers as { "name": ..., "has_value": true }. Stored values are never returned. |
body_template | string | null | Request body, may contain {{variable}} placeholders |
parameters | array | Parameter definitions the AI fills in from the conversation |
timeout_seconds | integer | Request timeout, 1 to 60 |
is_enabled | boolean | Whether the agent is offered the tool |
created_at | datetime | Creation timestamp |
updated_at | datetime | Last 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:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Lowercase letters, digits, and underscores, starting with a letter. Must be unique within the endpoint and must not collide with a built-in variable name. |
description | string | Yes | What the AI should collect |
type | string | Yes | string, number, integer, or boolean |
required | boolean | No | Whether 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
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | -- | 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_. |
description | string | Yes | -- | When and why the AI should call the tool |
http_method | string | Yes | -- | GET, POST, PUT, PATCH, or DELETE |
url_template | string | Yes | -- | Target URL. Must use http or https and resolve to a public address. |
headers | array | No | [] | Request headers, each { "name": ..., "value": ... }. A value is required on create. |
body_template | string | No | null | Request body. Sent as application/json unless you configure your own Content-Type header. |
parameters | array | No | [] | Parameter definitions (see above) |
timeout_seconds | integer | No | 10 | Request timeout, 1 to 60 |
is_enabled | boolean | No | true | Whether 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.