Open in
Integrations
Define reusable HTTP tool integrations with endpoint, auth, and parameter schemas that any of your agents can call mid-conversation to reach external systems.
Integrations are reusable tool definitions — an endpoint URL + method + headers + a JSON schema for arguments — that you can attach to one or more agents. During a call the agent uses the tool's schema to decide when to call out; ThunderPhone makes the outbound HTTP request on the agent's behalf using the integration's saved configuration.
See also Function Tools for the shape of the JSON
schema an integration's spec must follow.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /v1/integrations | List integrations |
POST | /v1/integrations | Create an integration |
GET | /v1/integrations/{integration_id} | Retrieve an integration |
PATCH | /v1/integrations/{integration_id} | Update an integration |
DELETE | /v1/integrations/{integration_id} | Delete an integration |
POST | /v1/integrations/{integration_id}/transfer | Copy / move to another org |
GET | /v1/integrations/{integration_id}/versions | List prior configuration revisions |
POST | /v1/integrations/test-request | Probe an integration endpoint from a sandbox |
Integration object
{
"id": "f9b5a1a4-...",
"display_name": "Weather API",
"spec": {
"type": "function",
"function": {
"name": "get_weather",
"description": "Return the current weather for a zip code.",
"parameters": { /* JSON Schema */ }
}
},
"endpoint_url": "https://api.example.com/weather",
"endpoint_method": "GET",
"request_format": "standard",
"headers": [
{ "key": "X-Api-Key" }
],
"wizard_input": "Use https://api.example.com/weather with X-Api-Key header.",
"validation_status": "validated",
"warnings": [],
"created_at": "2026-04-20T18:24:10.113Z",
"updated_at": "2026-04-20T18:24:10.113Z"
}| Field | Type | Description |
|---|---|---|
id | UUID | Integration id |
display_name | string | 1–255 chars |
spec | object | OpenAI-style function tool schema (see Function Tools) |
endpoint_url | string | Outbound HTTP URL the tool calls. Empty string when the integration is still being configured |
endpoint_method | string | GET, POST, PUT, PATCH, or DELETE. Defaults to POST |
request_format | string | standard (default), vapi, or retell. Controls the body envelope for POST, PUT, and PATCH; GET and DELETE always use query parameters |
headers | array | Headers sent with every tool invocation. Key names only are returned: header values are write-only, encrypted at rest, and never appear in API responses, version history, or call records |
wizard_input | string | Free-form text from the "describe this integration" wizard; kept for audit only |
validation_status | string | untested, validated, error — updated by test-request |
created_at, updated_at | timestamp | ISO 8601 UTC |
warnings | array of object | Saved settings worth a second look, each {code, field, detail} (read-only). tool_has_no_inputs (field is spec.function.parameters) when the tool declares no input properties, so the agent may call it without the data the endpoint needs. Never blocks a save. Empty when there is nothing to flag |
List integrations
curl https://api.thunderphone.com/v1/integrations \
-H "Authorization: Bearer sk_live_YOUR_API_KEY"Returns an array of Integration objects.
Create an integration
curl -X POST https://api.thunderphone.com/v1/integrations \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Weather API",
"spec": {
"type": "function",
"function": {
"name": "get_weather",
"description": "Return the current weather for a zip code.",
"parameters": {
"type": "object",
"properties": {
"zip": { "type": "string" }
},
"required": ["zip"]
}
}
},
"endpoint_url": "https://api.example.com/weather",
"endpoint_method": "GET",
"request_format": "standard",
"headers": [
{ "key": "X-Api-Key", "value": "abc..." }
]
}'Request fields
| Field | Type | Required | Description |
|---|---|---|---|
display_name | string | yes | 1–255 chars |
spec | object | yes | Function-tool JSON schema. spec.type must be "function" |
endpoint_url | string | no | HTTPS URL; defaults to empty string if omitted |
endpoint_method | string | no | GET, POST (default), PUT, PATCH, DELETE |
request_format | string | no | standard (default), vapi, or retell; see Function Tools request formats |
headers | array | no | [{key, value}] pairs. Each header needs a value. Names must be unique (case-insensitive) |
copy_header_values_from | UUID | no | Another integration in the same organization. A header sent without a value takes that integration's stored value for the same name (how the dashboard's Clone copies secrets it cannot read) |
wizard_input | string | no | Free-form notes |
Returns 201 Created with the new Integration object in
validation_status="untested".
Retrieve / update / delete
GET, PATCH, and DELETE behave conventionally. PATCH accepts any
subset of the create fields. Deleting an integration removes it from
every agent it was linked to.
headers on PATCH replaces the whole list, with the same rules as
MCP server headers:
- An entry with an empty or omitted
valuekeeps the stored value for that header name (matched case-insensitively), so you can save an integration you read back without re-sending its secrets. - An entry with a
valuesets or replaces it. - A header left out of the list is removed.
- A new header name without a
valueis rejected with400.
Transfer an integration
Copy or move an integration to another organization where you are an admin.
An sk_live_ API key is limited to its own organization: with a key,
target_org_id must be the key's organization, and any other target
returns 403. Copy or move between organizations from a signed-in session
(Authorization: Token <session-token> plus X-ThunderPhone-Org for the
source org), as below.
curl -X POST https://api.thunderphone.com/v1/integrations/f9b5a1a4-.../transfer \
-H "Authorization: Token YOUR_SESSION_TOKEN" \
-H "X-ThunderPhone-Org: 42" \
-H "Content-Type: application/json" \
-d '{"target_org_id": 77, "mode": "copy"}'| Field | Type | Required | Description |
|---|---|---|---|
target_org_id | integer | yes | |
mode | string | yes | copy or move |
name | string | no | Override the copy's display name |
Returns 201 Created for copy / 200 OK for move:
{ "mode": "copy", "target_org_id": 77, "integration": { /* ... */ } }Version history
curl 'https://api.thunderphone.com/v1/integrations/f9b5a1a4-.../versions?limit=20' \
-H "Authorization: Bearer sk_live_YOUR_API_KEY"Response is analogous to Agent versions:
{count, results[]} with each result containing id, revision,
created_by, snapshot, and changed_fields. Snapshots record header
names only, so a change to a header's value alone does not create a new
revision.
Test integration request
Execute a one-off HTTP request against the integration endpoint (from ThunderPhone's servers) to validate connectivity before linking the integration to an agent. The sandbox enforces SSRF guards — no requests to localhost or private IP ranges.
curl -X POST https://api.thunderphone.com/v1/integrations/test-request \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.example.com/weather?zip=94110",
"method": "GET",
"tool_name": "get_weather",
"request_format": "standard",
"headers": { "X-Api-Key": "abc..." }
}'| Field | Type | Required | Description |
|---|---|---|---|
url | string | yes | HTTPS; http:// is only allowed for localhost during local dev |
method | string | yes | GET, POST, PUT, PATCH, DELETE |
headers | object | no | Header-name → value map |
body | string | object | no | For standard, a JSON body or raw string. For vapi/retell, a JSON object of tool arguments |
tool_name | string | no | Tool name used in a Vapi or Retell envelope; defaults to test_tool |
request_format | string | no | standard (default), vapi, or retell. Body methods use the selected envelope; GET/DELETE are unchanged |
timeout_seconds | integer | no | 1–60 (default 20) |
integration_id | UUID | no | A saved integration in your organization. Any header sent with an empty value is filled with that integration's stored value for the same name, so you can test a saved integration without re-entering its secrets. Requires admin or owner |
{
"ok": true,
"status_code": 200,
"latency_ms": 187,
"response_headers": { "content-type": "application/json" },
"response_body_preview": "{\"temperature_f\": 64, ...}",
"response_truncated": false
}Response bodies are truncated at 6 kB. SSRF-blocked requests return
400 with code="url_not_allowed".