---
title: "Integrations"
description: "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](/tools/overview) 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

```json
{
  "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](/tools/overview)) |
| `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](#test-integration-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

<CodeGroup>
```bash cURL
curl https://api.thunderphone.com/v1/integrations \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
```
</CodeGroup>

Returns an array of [Integration objects](#integration-object).

---

## Create an integration

<CodeGroup>
```bash cURL
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..." }
    ]
  }'
```
</CodeGroup>

### 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](/tools/overview#request-format) |
| `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](#integration-object) in
`validation_status="untested"`.

<Note>
  Header values are write-only. They are encrypted at rest, decrypted only to
  send the tool request, and never returned: responses list header names only.
</Note>

---

## 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](/api-reference/mcp-servers):

- An entry with an empty or omitted `value` keeps 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 `value` sets or replaces it.
- A header left out of the list is removed.
- A new header name without a `value` is rejected with `400`.

---

## 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.

<CodeGroup>
```bash cURL
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"}'
```
</CodeGroup>

| 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`:

```json
{ "mode": "copy", "target_org_id": 77, "integration": { /* ... */ } }
```

---

## Version history

<CodeGroup>
```bash cURL
curl 'https://api.thunderphone.com/v1/integrations/f9b5a1a4-.../versions?limit=20' \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
```
</CodeGroup>

Response is analogous to [Agent versions](/api-reference/agents#version-history):
`{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.

<CodeGroup>
```bash cURL
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..." }
  }'
```
</CodeGroup>

| 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` |

```json Response
{
  "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"`.

---

## Related

<CardGroup cols={2}>
  <Card title="Agents" icon="robot" href="/api-reference/agents">
    Attach integrations to agents via `integration_ids`.
  </Card>
  <Card title="Function Tools" icon="screwdriver-wrench" href="/tools/overview">
    The JSON schema shape for `spec`.
  </Card>
</CardGroup>
