Custom APIs
Any HTTP endpoint can become a tool. You describe the request Postman-style, declare which variables fill it in, and Platica derives the schema the model sees from that.
The mental model
A custom API has two parts:
request— the HTTP call to make. Method, URL, headers, body and auth. Wherever you want a dynamic value, you write{{name}}.variables— who fills each{{name}}. Themodefield decides the source:
mode | Who provides the value | Does the model see it? |
|---|---|---|
ai (default) | The model writes it when calling the tool | Yes, it appears in the schema |
constant | Fixed value in constantValue | No |
context | Taken from the conversation, per contextField | No |
The JSON Schema the model receives is derived from the ai variables when you enable the tool. You never write it by hand, which is why each variable's description matters: it is literally what the model reads to decide what to send.
The lifecycle
draft ──enable──> enabled ──pause──> paused ──enable──> enabled Only in enabled does the tool have an entry in the workspace catalog and become connectable to an agent. In draft and paused the configuration exists but nobody sees it.
The integrationId and the catalog toolId are the same identifier, so once enabled you can use it directly in POST /v1/agents/{agentId}/tools .
Calls may only resolve to public addresses. Platica blocks localhost, private networks, link-local and cloud-metadata addresses, and redirects to those targets, both in tests and during agent execution.
List APIs
GET https://api.platica.mx/v1/tools/apis Response
{
"count": 1,
"apis": [
{
"id": "RuzoaswTBBrhutHyKYZn",
"name": "order_lookup",
"toolName": "api_order_lookup",
"description": "Look up an order status by its reference number.",
"status": "enabled",
"method": "GET",
"url": "https://api.store.com/orders/{{orderId}}",
"variableCount": 1,
"lastTestOutcome": "success",
"lastTestAt": "2026-07-12T09:15:22.109Z",
"createdAt": "2026-07-10T15:41:03.902Z",
"updatedAt": "2026-07-12T09:18:44.117Z"
}
]
} | Field | Type | Description |
|---|---|---|
name | string | The short name you defined. |
toolName | string | Full name the model sees: always api_<name>. |
status | string | draft, enabled or paused. |
variableCount | number | Total variables, including those hidden from the model. |
lastTestOutcome | string | null | Result of the last test: success, http-error, network-error, schema-error or mapping-error. |
Create API
Creates the tool as a draft. It is not available to any agent until you enable it.
POST https://api.platica.mx/v1/tools/apis Request Body
{
"name": "order_lookup",
"description": "Look up a store order status by its reference number. Use it when the customer asks where their order is.",
"request": {
"method": "GET",
"url": "https://api.store.com/orders/{{orderId}}",
"headers": [
{ "key": "Accept", "value": "application/json" }
],
"auth": {
"type": "bearer",
"token": "sk_live_xxx"
}
},
"variables": [
{
"name": "orderId",
"type": "string",
"description": "Order reference, as shown in the confirmation email.",
"required": true
}
],
"response": {
"path": "data",
"exclude": ["internal_notes"]
}
} | Parameter | Type | Description | Required |
|---|---|---|---|
name | string | Lowercase, starts with a letter or digit, only letters, digits and _ (≤ 62 characters). The model will see it as api_<name> | ✓ |
description | string | Between 10 and 1024 characters. Explain when the agent should call it | ✓ |
request | object | The HTTP call. See below | ✓ |
variables | array | What fills each {{name}}. Maximum 50 | — |
passContext | boolean | Required to use variables in context mode | — |
context | object | { "lastMessagesN": 10 } — how many messages {{ctx.lastMessages}} exposes | — |
mapping | object | bodyStrategy (merge, replace, none) and extraBody with fixed pairs | — |
response | object | Trims the response before handing it to the model | — |
periodicAuth | object | Injects a token obtained by a cron job | — |
encryptPayload | object | Signs the outbound body as a JWT | — |
{
"method": "POST",
"url": "https://api.store.com/orders",
"params": [
{ "key": "locale", "value": "en-US" }
],
"headers": [
{ "key": "Content-Type", "value": "application/json" }
],
"body": {
"mode": "json",
"raw": "{ \"customer\": \"{{customerId}}\", \"note\": \"{{note}}\" }"
},
"auth": { "type": "bearer", "token": "sk_live_xxx" },
"settings": { "timeoutMs": 30000 }
} | Field | Type | Description |
|---|---|---|
method | string | GET, POST, PUT, PATCH or DELETE. |
url | string | Target URL. Accepts {{variable}} anywhere. |
params | array | Query params as { key, value, enabled? }. Maximum 25. |
headers | array | Same as params. Maximum 25. |
body | object | { "mode": "none" }, { "mode": "json", "raw": "..." }, { "mode": "text", "raw": "..." } or { "mode": "form-urlencoded", "pairs": [...] }. |
auth | object | { "type": "none" }, { "type": "bearer", "token" }, { "type": "basic", "username", "password" } or { "type": "apikey", "key", "value", "in": "header" \| "query" }. |
settings.timeoutMs | number | Between 1000 and 60000. Default 30000. |
Any value accepts {{variable}}, not just the URL.
{
"name": "orderId",
"type": "string",
"description": "Order reference, as shown in the confirmation email.",
"required": true,
"mode": "ai"
} | Field | Type | Description |
|---|---|---|
name | string | Referenced as {{name}} inside request. |
type | string | string, number, integer, boolean, array or object. |
description | string | What the model reads. Mandatory for ai variables at enable time. |
required | boolean | Whether the model must always send it. |
mode | string | ai (default), constant or context. |
constantValue | any | Only with mode: "constant". |
contextField | string | Only with mode: "context". See below. |
enum, format, pattern, minLength, maxLength, minimum, maximum, items, default | various | Refinements copied into the model's JSON Schema. |
Values accepted in contextField, available when passContext is true:
contextField | Value |
|---|---|
phoneNumber | The customer's number in the conversation |
workspaceId | Workspace ID |
conversationId | Conversation ID |
agentId | ID of the calling agent |
agentName | Name of the calling agent |
channelId | Channel the conversation arrived through |
lastMessages | Last N messages, per context.lastMessagesN |
Large responses burn model context without adding anything. response lets you keep only what matters:
| Field | Type | Description |
|---|---|---|
path | string | Path to the useful fragment, e.g. data.items. |
strictPath | boolean | When true, fails if path is missing instead of returning everything. |
include | string[] | Only these fields. |
exclude | string[] | Everything except these. |
maxBytes | number | Truncate the response to this size. Default 32000. |
Response
{
"status": "success",
"message": "API tool created successfully",
"data": {
"id": "RuzoaswTBBrhutHyKYZn",
"name": "order_lookup",
"toolName": "api_order_lookup",
"status": "draft"
}
} Errors
| Status | Cause |
|---|---|
400 | The name does not match the format, the description is too short, or the request is invalid |
409 | Another tool in the workspace already uses that name |
Get API
GET https://api.platica.mx/v1/tools/apis/{integrationId} URL parameters
| Parameter | Type | Description | Required |
|---|---|---|---|
integrationId | string | Custom API identifier | ✓ |
Response
Returns the definition plus the derived toolName. Write-only values are replaced with "[REDACTED]": authentication, sensitive headers/params, constant variables, and the encryptPayload key.
{
"id": "RuzoaswTBBrhutHyKYZn",
"toolName": "api_order_lookup",
"name": "order_lookup",
"description": "Look up a store order status by its reference number.",
"status": "enabled",
"request": {
"method": "GET",
"url": "https://api.store.com/orders/{{orderId}}",
"headers": [{ "key": "Accept", "value": "application/json" }],
"auth": { "type": "bearer", "token": "[REDACTED]" }
},
"variables": [
{
"name": "orderId",
"type": "string",
"description": "Order reference, as shown in the confirmation email.",
"required": true,
"mode": "ai"
}
],
"version": 3,
"enabledFunctionRef": "RuzoaswTBBrhutHyKYZn",
"secretsRedacted": true,
"createdAt": "2026-07-10T15:41:03.902Z",
"updatedAt": "2026-07-12T09:18:44.117Z"
} version increases with every write and is used for concurrency control on update.
You may send "[REDACTED]" back in a PATCH: Platica keeps the current value. Send a new value to replace a credential, or omit the field to preserve it.
Errors
| Status | Cause |
|---|---|
404 | The custom API does not exist in the workspace |
Update API
Partial update: only the fields you send are modified, the rest are kept.
PATCH https://api.platica.mx/v1/tools/apis/{integrationId} URL parameters
| Parameter | Type | Description | Required |
|---|---|---|---|
integrationId | string | Custom API identifier | ✓ |
Request Body
{
"description": "Look up the status and estimated delivery date of an order.",
"expectedVersion": 3
} Accepts the same fields as create , all optional, plus:
| Parameter | Type | Description |
|---|---|---|
expectedVersion | number | If the stored version does not match, responds 409 instead of overwriting someone else's changes |
variables and the request arrays (headers, params) are replaced wholesale, not merged element by element. To add a variable, send the entire array with the new one included.
If the tool is enabled, name, description and variable changes propagate to the catalog immediately. status does not change here — use enable and pause for that.
Response
{
"status": "success",
"message": "API tool updated successfully",
"data": {
"id": "RuzoaswTBBrhutHyKYZn",
"version": 4
}
} Errors
| Status | Cause |
|---|---|
400 | A field is invalid, or the body is empty |
404 | The custom API does not exist |
409 | expectedVersion mismatch, or the new name is already taken |
Test API
Runs the tool with sample values, whether it is a draft or enabled. Every run is recorded in the integration logs.
POST https://api.platica.mx/v1/tools/apis/{integrationId}/test URL parameters
| Parameter | Type | Description | Required |
|---|---|---|---|
integrationId | string | Custom API identifier | ✓ |
Request Body
{
"dryRun": false,
"input": {
"orderId": "MX-48210"
},
"context": {
"phoneNumber": "+5215512345678"
}
} | Parameter | Type | Description | Required | Default |
|---|---|---|---|---|
dryRun | boolean | With true, assembles the request and returns it without calling the API | — | false |
input | object | Values for the ai variables | — | {} |
context | object | Test values for {{ctx.*}}: phoneNumber, workspaceId, conversationId, agentId, agentName, channelId, messages | — | — |
Always start with dryRun: true to check how the URL, headers and body look with substitutions applied, before hitting the real API.
Response
{
"status": "success",
"message": "Test executed",
"data": {
"outcome": "success",
"httpStatus": 200,
"durationMs": 412,
"request": {
"method": "GET",
"url": "https://api.store.com/orders/MX-48210"
},
"response": {
"status": "in_transit",
"eta": "2026-07-16"
}
}
} Errors
| Status | Cause |
|---|---|
404 | The custom API does not exist |
A failure from the remote API is not an error of this endpoint: it responds 200 with the detail in data.outcome and data.httpStatus.
Enable API
Publishes the tool to the workspace catalog.
POST https://api.platica.mx/v1/tools/apis/{integrationId}/enable URL parameters
| Parameter | Type | Description | Required |
|---|---|---|---|
integrationId | string | Custom API identifier | ✓ |
Response
{
"status": "success",
"message": "API tool enabled successfully",
"data": {
"id": "RuzoaswTBBrhutHyKYZn",
"toolId": "RuzoaswTBBrhutHyKYZn",
"status": "enabled"
}
} toolId is what you pass to POST /v1/agents/{agentId}/tools to give it to an agent.
Enabling an already-enabled tool re-syncs the catalog, which is handy after editing the schema.
Errors
| Status | Cause |
|---|---|
400 | A model-facing variable is missing its description, or the definition fails validation |
404 | The custom API does not exist |
409 | Another tool in the workspace already uses that name |
Pause API
Withdraws the tool from the catalog. Agents stop seeing it immediately, but the configuration is kept intact.
POST https://api.platica.mx/v1/tools/apis/{integrationId}/pause URL parameters
| Parameter | Type | Description | Required |
|---|---|---|---|
integrationId | string | Custom API identifier | ✓ |
Response
{
"status": "success",
"message": "API tool paused successfully",
"data": {
"id": "RuzoaswTBBrhutHyKYZn",
"status": "paused"
}
} Errors
| Status | Cause |
|---|---|
404 | The custom API does not exist |
Delete API
DELETE https://api.platica.mx/v1/tools/apis/{integrationId} URL parameters
| Parameter | Type | Description | Required |
|---|---|---|---|
integrationId | string | Custom API identifier | ✓ |
Response
{
"status": "success",
"message": "API tool deleted successfully",
"data": {
"id": "RuzoaswTBBrhutHyKYZn",
"removedAgentConnections": 3
}
} Removes the configuration, its catalog entry, and every agent connection. This cannot be undone — if you only want to switch it off while preserving assignments, use pause .
Errors
| Status | Cause |
|---|---|
404 | The custom API does not exist |