Fields & validation
Every field with its exact validation rule, nullability, and the database column that stores it.
server (apiRequest / function)
| Field | Rules (verbatim) |
server.url | nullable|url|max:2000 |
server.method | nullable|string|in:GET,POST,PUT,PATCH,DELETE |
server.timeoutSeconds | nullable|integer|min:1|max:300 |
server.headers | rows of {key, value} — blank keys are dropped |
server.bodyProperties | recursive tree of {name, type, description, required, enum, value, itemType, children}; type in string,number,integer,boolean,object,array; children only for object/array-of-object nodes |
server.staticFields | rows of {key, type, value}, type in string,number,boolean,object |
server.lockSchema | nullable|boolean |
server.encryptedFields.* | string|max:200 |
server.backoffPlan.type | nullable|string|in:fixed,exponential |
server.backoffPlan.maxRetries | nullable|integer|min:0|max:10 |
server.backoffPlan.baseDelaySeconds | nullable|integer|min:0|max:300 |
server.backoffPlan.excludedStatusCodes.* | integer|min:100|max:599 |
server.credentialId | nullable|string|max:100 — a personal credential's public id |
server.staticIpAddressesEnabled | nullable|boolean |
server.serverEncryptedPaths.* | string|max:200 |
server.sipInfoDtmfEnabled | nullable|boolean — dtmf tools only |
server.beepDetectionEnabled | nullable|boolean — voicemail tools only |
destinations (transferCall / handoff)
| Field | Rules (verbatim) |
destinations.*.type | nullable|string|in:number,sip,assistant,dynamic,squad |
destinations.*.number | nullable|string|max:50 |
destinations.*.extension | nullable|string|max:20 |
destinations.*.callerId | nullable|string|max:50 |
destinations.*.sipUri | nullable|string|max:255 |
destinations.*.assistantId / .assistantName | nullable|string|max:100 / max:255 |
destinations.*.squadId | nullable|string|max:100 |
destinations.*.entryAssistantName | nullable|string|max:255 |
destinations.*.transferMode | nullable|string|in:rolling-history,swap-system-message-in-history,swap-system-message-in-history-and-remove-transfer-tool-messages,delete-history |
destinations.*.message | nullable|string|max:1000 |
destinations.*.description | nullable|string|max:2000 |
destinations.*.numberE164CheckEnabled | nullable|boolean |
destinations.*.sipHeaders | rows of {key, value} |
destinations.*.server.url | nullable|url|max:2000 — handoff dynamic destinations only |
destinations.*.server.timeoutSeconds | nullable|integer|min:1|max:300 |
destinations.*.contextEngineeringPlan.type | nullable|string|in:none,all,lastNMessages,userAndAssistantMessages,previousAssistantMessages |
destinations.*.contextEngineeringPlan.maxMessages | nullable|integer|min:1|max:1000 |
destinations.*.transferPlan.mode | nullable|string|max:80 |
destinations.*.transferPlan.message | nullable|string|max:1000 |
destinations.*.transferPlan.sipVerb | nullable|string|in:refer,bye,dial |
destinations.*.transferPlan.timeout / .dialTimeout | nullable|integer|min:1|max:600 |
destinations.*.transferPlan.holdAudioUrl / .transferCompleteAudioUrl | nullable|string|max:2000 |
destinations.*.transferPlan.twiml | nullable|string|max:4000 |
destinations.*.transferPlan.sipHeadersInReferToEnabled | nullable|boolean |
A destination is only kept if it carries a real target: a number, sipUri, assistantName/assistantId, squadId, or dynamic server.url.
rejectionPlan & messages
| Field | Rules (verbatim) |
rejectionPlan.conditions.*.type | nullable|string|in:regex,liquid |
rejectionPlan.conditions.*.value | nullable|string|max:2000 |
messages.*.type | required_with:messages|string|in:request-start,request-complete,request-failed,request-response-delayed |
messages.*.mode | nullable|string|in:default,none,custom — request-start only |
messages.*.content | nullable|string|max:1000 |
messages.*.contents.*.text | nullable|string|max:1000 — one of several variants, picked at random |
messages.*.blocking | nullable|boolean |
messages.*.role | nullable|string|in:assistant,system |
messages.*.endCallAfterSpokenEnabled | nullable|boolean |
messages.*.timingMilliseconds | nullable|integer|min:100|max:120000 — request-response-delayed only |
messages.*.conditions.*.operator | nullable|string|in:eq,neq,gt,gte,lt,lte |
Only the fields each message type actually supports upstream are kept — e.g. role is dropped from a request-start message.
Credential (Credentials group)
| Field | Required | Rules (verbatim) |
provider | yes* | required|string|in:custom-credential,webhook — *defaults to custom-credential when omitted |
name | no | nullable|string|max:40 |
authenticationPlan | yes | required|array |
authenticationPlan.type | yes | required|string|in:bearer,oauth2,hmac — immutable after creation |
authenticationPlan.token | bearer | "Token is required." when missing |
authenticationPlan.url, .clientId, .clientSecret | oauth2 | "Token URL, Client ID and Client Secret are required." when any is missing |
authenticationPlan.secretKey, .algorithm | hmac | "Secret Key and Algorithm are required." when either is missing |
encryptionPlan.publicKey | no | optional PEM public key — encrypts the stored secret |
Constraints the rules cannot express
type is fixed at creation — the update endpoint validates it but strips it before writing to the live platform, so it can never change an existing tool's type.
function_def.name (the callable name) always exists, even for apiRequest tools: it's the explicit functionName, or a sanitised slug of name, capped at 64 characters.
- Drafts (
POST /tools/draft, PUT /tools/{id}/draft) accept any partial shape — the strict ruleset above only applies on publish (store/update/publish).
- A destination is dropped entirely if it has no real target after normalisation.
- Only super admins can set
isGlobal or assign reseller_id; only content admins/super admins can assign user_id to someone else.
- A credential's
authenticationPlan.type cannot change across an update — a differing type is rejected with 422.
- The live platform is written first on publish; on failure nothing is saved locally.