{
  "openapi": "3.1.0",
  "info": {
    "title": "Blue Reacher API",
    "version": "2026-08-27",
    "description": "Send and read iMessages, manage contacts, drive native iMessage signals (tapbacks, typing indicators, read state), and receive signed webhooks through your managed Blue Reacher lines.\n\n## Authentication\nEvery endpoint takes a Blue Reacher API key as a Bearer token:\n\n```\nAuthorization: Bearer brk_your_api_key\n```\n\nKeys carry `read`, `write`, or `admin` permission and are bound to one workspace. Live keys are prefixed `brk_live_`, test keys `brk_test_`.\n\n## Test mode\n`brk_test_` keys are answered by a simulator: identical request validation and response shapes, but no message is ever sent and no contact, line, or queue row is touched. Every sandbox response carries `\"sandbox\": true` plus an `X-BlueReacher-Sandbox: true` header. Simulated `sim_` message ids progress on a timer so `GET /v1/status/{id}` behaves realistically: drip goes pending then sent after about 5 seconds; instant goes sent then delivered after about 10 seconds. Test keys cover messaging, signals, MCP, `/v1/contacts` and `/v1/capability` (answered by the simulator with the live response shapes) and `/v1/usage` (a fixed sample). Two surfaces need a live key: conversation history (`GET /v1/conversations/{contact_id}/messages`, which returns `403 sandbox_key_not_supported_here` on a test key) and the calling endpoints, which are in private beta. A test key is bound to no workspace, so it can never read or write real data.\n\n## How sending works\n`POST /v1/messages` queues through the paced drip pipeline by default, the same deliverability protections the platform itself uses (per-line daily caps, warm-up, send windows). By default a message is scheduled about 10 minutes out and delivers inside the organization's local `window_start_hour` to `window_end_hour` window; tune `delay_minutes` and the window per request. `send_mode: \"instant\"` dispatches through the live pipeline immediately, built for conversational replies, capped at 10/min and 75/day per key on top of all line-level protections. An `Idempotency-Key` header (max 255 chars) makes retries safe with a 24 hour replay window.\n\n## Rate limits\n| Surface | Limit (per key) |\n|---|---|\n| All /v1 routes, global | 60/min |\n| POST /v1/messages | 30/min |\n| Instant sends (send_mode: instant) | 10/min and 75/day |\n| POST /v1/voice-memos | 10/min |\n| POST and DELETE /v1/reactions | 30/min |\n| POST /v1/typing, /v1/read, /v1/opt-out, /v1/bot | 30/min |\n| POST /v1/mcp | 60/min; tools/call also pays the target route's limit |\n| /v1/contacts (all methods) | 300/min |\n| GET /v1/usage | 60/min |\n| POST /v1/capability | 100 phones/request; probes 50/day/line |\n\nRate-limited responses return HTTP 429 with `retryAfter` (seconds).\n\n## Errors\nErrors return JSON `{ \"error\": \"<human message>\" }` with a machine-readable `error_code` and sometimes a `help` hint. Common codes: `rate_limited`, `device_not_owned`, `message_not_found`, `not_deliverable`, `audio_url_unreachable`, `no_online_device`, `idempotency_conflict`, `contact_not_found`, `resubscribe_confirmation_required`, `always_on_confirmation_required`, `invalid_status`, `invalid_api_key`, `not_found`.\n\n## Versioning\n`/v1` is current and additive: new fields and new webhook event types can appear, so parse defensively.",
    "contact": {
      "name": "Blue Reacher Support",
      "url": "https://bluereacher.com/contact"
    }
  },
  "servers": [
    {
      "url": "https://api.bluereacher.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Messaging",
      "description": "Queue outbound iMessages and voice memos, and track delivery."
    },
    {
      "name": "Conversations",
      "description": "Read message history for 1:1 and group threads."
    },
    {
      "name": "Lines",
      "description": "Your sending lines, with live capacity and warm-up state."
    },
    {
      "name": "Signals",
      "description": "Native iMessage signals: tapback reactions, the typing bubble, read state."
    },
    {
      "name": "Contacts",
      "description": "Create, search, and update contacts, including opt-out state."
    },
    {
      "name": "Utility",
      "description": "Capability lookup and usage metering."
    },
    {
      "name": "MCP",
      "description": "Hosted Model Context Protocol server for AI agents."
    },
    {
      "name": "Calling",
      "description": "FaceTime audio calling, human-confirmed. Private beta, enabled per workspace."
    },
    {
      "name": "Webhooks",
      "description": "Self-serve inbound event delivery."
    }
  ],
  "paths": {
    "/v1/messages": {
      "post": {
        "tags": [
          "Messaging"
        ],
        "operationId": "sendMessage",
        "summary": "Send a text and/or media message (paced drip by default, or instant)",
        "description": "Requires write permission. Drip (default) inserts into the paced pipeline: scheduled about 10 minutes out, delivered inside the org-local send window. Instant dispatches immediately (10/min and 75/day per key) and requires an online line, returning 409 `no_online_device` otherwise.\n\nSend target: exactly one of `to` (1:1), `group_chat_id` (an existing group thread from GET /v1/groups), or `create_group_phones` (2 to 25 numbers; creates a new group iMessage, then sends). Group sends require `send_mode: \"instant\"`.\n\nPass an `Idempotency-Key` header for safe retries: a duplicate request within 24 hours replays the original response with an `Idempotent-Replay: true` header.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            },
            "description": "Unique key for safe retries; response replayed for 24h."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Recipient in E.164 format (1:1 send). Pass exactly one of `to`, `group_chat_id`, `create_group_phones`.",
                    "examples": [
                      "+13035550101"
                    ]
                  },
                  "group_chat_id": {
                    "type": "string",
                    "description": "Existing group chat id (from GET /v1/groups), sends into that thread on its owning line. Instant only."
                  },
                  "create_group_phones": {
                    "type": "array",
                    "minItems": 2,
                    "maxItems": 25,
                    "items": {
                      "type": "string"
                    },
                    "description": "2 to 25 E.164 numbers, creates a new group iMessage, then sends. Instant only."
                  },
                  "message": {
                    "type": "string",
                    "description": "Message text. Required unless media_urls is set."
                  },
                  "media_urls": {
                    "type": "array",
                    "maxItems": 10,
                    "items": {
                      "type": "string",
                      "maxLength": 2048
                    },
                    "description": "1 to 10 https:// URLs to attach (images/files)."
                  },
                  "send_mode": {
                    "type": "string",
                    "enum": [
                      "drip",
                      "instant"
                    ],
                    "default": "drip",
                    "description": "drip = paced pipeline (default); instant = dispatch now."
                  },
                  "message_effect": {
                    "type": "string",
                    "enum": [
                      "confetti",
                      "balloons",
                      "love",
                      "lasers",
                      "fireworks",
                      "celebration",
                      "echo",
                      "spotlight",
                      "slam",
                      "loud",
                      "gentle",
                      "invisibleink"
                    ],
                    "description": "Optional iMessage effect the line arms before sending. The first eight play full screen; slam / loud / gentle / invisibleink are bubble effects. Requires `send_mode: \"instant\"`, a paced drip send cannot carry an effect and returns 400 `effect_requires_instant` rather than quietly delivering a plain message. Degrades by design: a message carrying media or a voice memo sends plain, and an SMS recipient always gets plain text."
                  },
                  "device_id": {
                    "type": "string",
                    "description": "Explicit line UUID, or `auto_load_balanced` (default) / `auto_round_robin`. Omit to use the key's default line (when set and online) or automatic load-balancing. Instant mode picks a random eligible online line when omitted."
                  },
                  "delay_minutes": {
                    "type": "number",
                    "default": 10,
                    "description": "Drip only, minutes from now to schedule the send."
                  },
                  "window_start_hour": {
                    "type": "integer",
                    "description": "Drip only, earliest org-local delivery hour. No window when both hours are omitted; defaults to 9 only when an end hour is supplied."
                  },
                  "window_end_hour": {
                    "type": "integer",
                    "description": "Drip only, latest org-local delivery hour. No window when both hours are omitted; defaults to 18 only when a start hour is supplied."
                  },
                  "lock": {
                    "type": "boolean",
                    "default": true,
                    "description": "Auto-lock the contact to the line used for this send."
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Free-form metadata stored with the send."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Queued (drip) or dispatched (instant), discriminate on `mode`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "title": "Drip queued",
                      "properties": {
                        "success": {
                          "type": "boolean"
                        },
                        "message_id": {
                          "type": "string",
                          "description": "Queue item id, poll /v1/status/{id}."
                        },
                        "mode": {
                          "type": "string",
                          "enum": [
                            "drip"
                          ]
                        },
                        "queued": {
                          "type": "boolean"
                        },
                        "scheduled_for": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "estimated_send_time": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "queue_position": {
                          "type": "integer"
                        }
                      }
                    },
                    {
                      "type": "object",
                      "title": "Instant dispatched",
                      "properties": {
                        "success": {
                          "type": "boolean"
                        },
                        "message_id": {
                          "type": "string",
                          "description": "messages row id, poll /v1/status/{id}."
                        },
                        "mode": {
                          "type": "string",
                          "enum": [
                            "instant"
                          ]
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "dispatched"
                          ]
                        },
                        "device_id": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "null on group_chat_id sends, the owning line is resolved downstream."
                        },
                        "group_chat_id": {
                          "type": "string",
                          "description": "Echoed on existing-group sends."
                        },
                        "group_created": {
                          "type": "boolean",
                          "description": "true when create_group_phones made a new group."
                        },
                        "participant_count": {
                          "type": "integer"
                        },
                        "note": {
                          "type": "string"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Line not owned by this workspace, or plan/feature gate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`no_online_device` (instant with no eligible online line) or `idempotency_conflict` (same Idempotency-Key still in flight).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/voice-memos": {
      "post": {
        "tags": [
          "Messaging"
        ],
        "operationId": "sendVoiceMemo",
        "summary": "Deliver audio as a native Apple voice memo",
        "description": "Fetches your audio (public HTTPS URL, up to 25 MB, mp3/m4a/wav/aac), converts to Apple's native voice-bubble format when possible (memo_format: native_bubble), and falls back to a media attachment otherwise. Requires write permission.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "to",
                  "audio_url"
                ],
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Recipient in E.164 format."
                  },
                  "audio_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Publicly reachable HTTPS audio URL."
                  },
                  "device_id": {
                    "type": "string",
                    "description": "Explicit line UUID (optional)."
                  },
                  "lock": {
                    "type": "boolean",
                    "default": true
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Queued for delivery.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "message_id": {
                      "type": "string"
                    },
                    "queued": {
                      "type": "boolean"
                    },
                    "memo_format": {
                      "type": "string",
                      "enum": [
                        "native_bubble",
                        "attachment"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "description": "audio_url unreachable or not an audio file.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/status/{message_id}": {
      "get": {
        "tags": [
          "Messaging"
        ],
        "operationId": "getMessageStatus",
        "summary": "Delivery status of a queued item",
        "description": "Looks the id up in the drip queue first (text sends), then in the messages table (voice memos and direct sends). Common status values: pending, sent, failed; additional lifecycle states may appear.",
        "parameters": [
          {
            "name": "message_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "message_id": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "drip_queue",
                        "message",
                        "voice_memo"
                      ]
                    },
                    "status": {
                      "type": "string"
                    },
                    "scheduled_for": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "sent_at": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "device_name": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "delivery_unconfirmed": {
                      "type": "boolean",
                      "description": "true when status is sent but nothing on the line confirmed the text went out."
                    },
                    "delivery_note": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Plain-words reason when delivery_unconfirmed is true; otherwise null."
                    },
                    "error": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No queue item or message with that id in your scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/conversations": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "operationId": "getConversationByPhone",
        "summary": "Conversation history for a phone number or group",
        "description": "Chronological history (oldest first) with per-message delivery channel (imessage or sms), tapback reactions, and AI attribution. Requires write permission. Cursor pagination via `before` plus `next_cursor`.\n\nPass exactly one of `phone` (a 1:1 thread) or `group_chat_id` (a group thread, from GET /v1/groups). Group responses swap `phone` for `group_chat_id` plus a `group` summary, and each message carries `from`, the sender's phone (null for your own outbound rows).",
        "parameters": [
          {
            "name": "phone",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "E.164 phone number (1:1 thread). Exactly one of phone / group_chat_id."
          },
          {
            "name": "group_chat_id",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Group chat id from GET /groups, reads that group thread."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          },
          {
            "name": "before",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Return messages created before this ISO timestamp."
          },
          {
            "name": "device_id",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter to one sending line (1:1 only)."
          }
        ],
        "responses": {
          "200": {
            "description": "Conversation slice.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "phone": {
                      "type": "string",
                      "description": "1:1 reads only."
                    },
                    "group_chat_id": {
                      "type": "string",
                      "description": "Group reads only."
                    },
                    "group": {
                      "type": "object",
                      "description": "Group reads only, id, display_name, participant_phones, participant_count, device_id, is_active."
                    },
                    "messages": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ConversationMessage"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "`group_not_found`, the group_chat_id is not in this key's workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/conversations/{contact_id}/messages": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "operationId": "getContactMessages",
        "summary": "Paginated message history for a contact",
        "description": "Full inbound and outbound message history for one contact, paginated, for building an inbox or syncing a thread.",
        "parameters": [
          {
            "name": "contact_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          },
          {
            "name": "before",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "after",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Messages + contact.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "messages": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MessageRecord"
                      }
                    },
                    "contact": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "phone_number": {
                          "type": "string"
                        },
                        "first_name": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "last_name": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      }
                    },
                    "page": {
                      "type": "integer"
                    },
                    "per_page": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "has_more": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Contact not found in your scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/groups": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "operationId": "listGroups",
        "summary": "List group chats",
        "description": "Every group iMessage thread in the key's workspace: id, display name, participant phones and count, last activity, active flag, and the owning line. Use a returned id as group_chat_id in POST /v1/messages or GET /v1/conversations. Create a new group by sending with create_group_phones instead. Requires write permission.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          },
          {
            "name": "active_only",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Only groups still marked active."
          }
        ],
        "responses": {
          "200": {
            "description": "Group chats, most recent activity first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "groups": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "display_name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "participant_phones": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "participant_count": {
                            "type": "integer"
                          },
                          "last_message_at": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          },
                          "last_message_preview": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "is_active": {
                            "type": "boolean"
                          },
                          "device_id": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "The owning line."
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/devices": {
      "get": {
        "tags": [
          "Lines"
        ],
        "operationId": "listDevices",
        "summary": "List sending lines with live capacity",
        "description": "Every line in the key's workspace with online state, today's cold-outreach usage against its effective daily cap (warm-up aware), and queue depth. Use it to pick a device_id or to check available capacity before a batch. A line object is called a device in the API; the two words mean the same thing.",
        "responses": {
          "200": {
            "description": "Lines.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "devices": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Line"
                      }
                    },
                    "fleet_summary": {
                      "type": "object",
                      "description": "Present only when fleet warm-up is enabled for the org."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/reactions": {
      "post": {
        "tags": [
          "Signals"
        ],
        "operationId": "sendReaction",
        "summary": "Send a native iMessage tapback",
        "description": "Queues a tapback on a message identified by message_id, or by phone plus external_id (iMessage GUID). The six classic tapbacks are supported (❤️ 👍 👎 😂 ‼️ ❓); any other emoji is rejected not_deliverable. Idempotent: re-sending an already-delivered tapback returns already_delivered true. SMS bubbles cannot take tapbacks.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "emoji"
                ],
                "properties": {
                  "emoji": {
                    "type": "string",
                    "description": "Any single emoji, classics ❤️ 👍 👎 😂 ‼️ ❓ or any other, e.g. 🥨."
                  },
                  "message_id": {
                    "type": "string"
                  },
                  "phone": {
                    "type": "string",
                    "description": "E.164, pair with external_id."
                  },
                  "external_id": {
                    "type": "string",
                    "description": "iMessage GUID of the target message."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Queued (or already delivered).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "queued": {
                      "type": "boolean"
                    },
                    "already_delivered": {
                      "type": "boolean"
                    },
                    "reaction": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Message not found in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unsupported emoji, or target message has no iMessage GUID.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Signals"
        ],
        "operationId": "removeReaction",
        "summary": "Take back a tapback",
        "description": "A still-queued tapback is cancelled before delivery (cancelled_before_delivery true). An already-delivered tapback is genuinely removed from the recipient's device: the line presses the same tapback again and Apple toggles it off (removal_queued true). Idempotent; removing a tapback that is not there returns 200 with removed false.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "emoji"
                ],
                "properties": {
                  "emoji": {
                    "type": "string"
                  },
                  "message_id": {
                    "type": "string"
                  },
                  "phone": {
                    "type": "string"
                  },
                  "external_id": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Removal queued / cancelled / nothing to remove.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "removed": {
                      "type": "boolean"
                    },
                    "was_delivered": {
                      "type": "boolean"
                    },
                    "cancelled_before_delivery": {
                      "type": "boolean"
                    },
                    "removal_queued": {
                      "type": "boolean",
                      "description": "True when the tapback had already been delivered and your line has been queued to press it again and toggle it off."
                    },
                    "note": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "description": "Not a single emoji, or the message has no line attached to deliver the removal (`no_delivery_route`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/typing": {
      "post": {
        "tags": [
          "Signals"
        ],
        "operationId": "setTyping",
        "summary": "Show or clear the native typing bubble",
        "description": "Lights the typing indicator on the recipient's device from your sending line. The window defaults to about 10 seconds (override with duration_ms, clamped 3 to 60 seconds); the response returns expires_at and a keepalive_interval_ms to re-POST at while composing. is_typing false clears immediately.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone",
                  "device_id",
                  "is_typing"
                ],
                "properties": {
                  "phone": {
                    "type": "string"
                  },
                  "device_id": {
                    "type": "string"
                  },
                  "is_typing": {
                    "type": "boolean"
                  },
                  "duration_ms": {
                    "type": "integer",
                    "minimum": 3000,
                    "maximum": 60000,
                    "default": 10000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Applied.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "is_typing": {
                      "type": "boolean"
                    },
                    "expires_at": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "keepalive_interval_ms": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "note": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/read": {
      "post": {
        "tags": [
          "Signals"
        ],
        "operationId": "markRead",
        "summary": "Clear CRM unread state (optional native read receipt)",
        "description": "Clears the connected CRM conversation's unread badge for this contact (graceful no-op without a CRM). With native true plus device_id, a native mark-thread-read request is queued for the line.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone"
                ],
                "properties": {
                  "phone": {
                    "type": "string"
                  },
                  "native": {
                    "type": "boolean",
                    "default": false
                  },
                  "device_id": {
                    "type": "string",
                    "description": "Required when native is true."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "cleared_crm_unread": {
                      "type": "boolean"
                    },
                    "detail": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "native_read_queued": {
                      "type": "boolean"
                    },
                    "native_read_note": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/opt-out": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "operationId": "getOptOutState",
        "summary": "Read a contact's opt-out state by phone",
        "description": "Exact E.164 match against this workspace's contacts. known_contact is false when no contact row exists for that phone.",
        "parameters": [
          {
            "name": "phone",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "E.164 phone number."
          }
        ],
        "responses": {
          "200": {
            "description": "Opt-out state.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "phone": {
                      "type": "string"
                    },
                    "opted_out": {
                      "type": "boolean"
                    },
                    "opted_out_at": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "known_contact": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Contacts"
        ],
        "operationId": "setOptOutState",
        "summary": "Opt a contact out, or (with explicit confirmation) back in",
        "description": "Opting out runs the same enforcement path as an inbound STOP reply: the contact is suppressed everywhere and the contact.opted_out webhook fires. Idempotent. Re-subscribing (opted_out false) requires confirm_resubscribe true and never fires a webhook; use it only when the contact explicitly asked to hear from you again. Unknown phones return 404 contact_not_found.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone",
                  "opted_out"
                ],
                "properties": {
                  "phone": {
                    "type": "string",
                    "description": "E.164 phone number."
                  },
                  "opted_out": {
                    "type": "boolean"
                  },
                  "confirm_resubscribe": {
                    "type": "boolean",
                    "description": "Required (true) when opted_out is false."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Opt-out state updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "phone": {
                      "type": "string"
                    },
                    "opted_out": {
                      "type": "boolean"
                    },
                    "already_opted_out": {
                      "type": "boolean"
                    },
                    "resubscribed": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No contact with this phone in the workspace (contact_not_found).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/bot": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "operationId": "getBotStatus",
        "summary": "Read whether the AI assistant will reply to a contact, and why",
        "description": "Returns the per-contact setting alongside an effective block computed from the reply engine's own gate chain, distinguishing switched-on-and-replying from switched-on-but-nothing-will-answer.",
        "parameters": [
          {
            "name": "phone",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "E.164 phone number."
          }
        ],
        "responses": {
          "200": {
            "description": "Bot control state for the contact.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BotStatus"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No contact with this phone in the workspace (contact_not_found).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Contacts"
        ],
        "operationId": "setBotStatus",
        "summary": "Pause, hand off, or autopilot the AI assistant for one contact",
        "description": "status values: inactive (mute for this contact), snooze (pause for snooze_minutes, default 60, capped at 90 days; the right call when a human takes over), active (return to the assistant's own schedule), active_always (reply 24/7 to this contact, overriding configured business hours; requires confirm_always_on true). Unknown phones return 404 contact_not_found.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone",
                  "status"
                ],
                "properties": {
                  "phone": {
                    "type": "string",
                    "description": "E.164 phone number."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "active_always",
                      "inactive",
                      "snooze"
                    ]
                  },
                  "bot_id": {
                    "type": "string",
                    "description": "Bot uuid to assign, \"keep\" (default, leave the assignment alone) or \"none\" (clear it). A bot not owned by this account is rejected."
                  },
                  "snooze_minutes": {
                    "type": "number",
                    "description": "snooze only. Default 60, capped at 90 days."
                  },
                  "confirm_always_on": {
                    "type": "boolean",
                    "description": "Required (true) when status is active_always."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bot control state after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/BotStatus"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "bot_name": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "applied": {
                          "type": "boolean"
                        },
                        "warnings": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No contact with this phone in the workspace (contact_not_found).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/contacts": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "operationId": "listContacts",
        "summary": "Search and list contacts",
        "description": "Filter by exact phone, email, tag, created_after, or a free-text search across name and phone. Requires read permission. Live keys only; test keys are rejected here.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 25,
              "maximum": 100
            }
          },
          {
            "name": "phone",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "email",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tag",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "created_after",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Contacts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "contacts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Contact"
                      }
                    },
                    "page": {
                      "type": "integer"
                    },
                    "per_page": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "has_more": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Contacts"
        ],
        "operationId": "upsertContact",
        "summary": "Create a contact (upsert by phone)",
        "description": "Creates the contact, or updates the existing one with the same phone number. HTTP 201 on create, 200 on update; the response carries created true or false. Requires write permission.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone_number"
                ],
                "properties": {
                  "phone_number": {
                    "type": "string",
                    "description": "E.164."
                  },
                  "first_name": {
                    "type": "string"
                  },
                  "last_name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "company": {
                    "type": "string"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "notes": {
                    "type": "string"
                  },
                  "custom_fields": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated existing contact."
          },
          "201": {
            "description": "Created."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/contacts/{contact_id}": {
      "patch": {
        "tags": [
          "Contacts"
        ],
        "operationId": "updateContact",
        "summary": "Update a contact (including opt-out)",
        "description": "Partial update; custom_fields shallow-merges with the existing map. opted_out true suppresses future platform sends to this contact. Requires write permission.",
        "parameters": [
          {
            "name": "contact_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "first_name": {
                    "type": "string"
                  },
                  "last_name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "company": {
                    "type": "string"
                  },
                  "notes": {
                    "type": "string"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "custom_fields": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "opted_out": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated contact.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "contact": {
                      "$ref": "#/components/schemas/Contact"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/capability": {
      "post": {
        "tags": [
          "Utility"
        ],
        "operationId": "checkCapability",
        "summary": "iMessage vs SMS capability for phone numbers",
        "description": "Batch lookup, up to 100 numbers. Known contacts answer instantly from observed delivery state; unknown numbers can queue a live probe (probe true, default) when the workspace has probes enabled and an online line with probe quota (50/day/line). Per-number status: imessage, sms, unknown, or pending. Requires read permission. Test keys get simulated answers marked sandbox: true and never queue a real probe.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phones"
                ],
                "properties": {
                  "phones": {
                    "type": "array",
                    "maxItems": 100,
                    "items": {
                      "type": "string"
                    }
                  },
                  "probe": {
                    "type": "boolean",
                    "default": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Capability results.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "phone": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "imessage",
                              "sms",
                              "unknown",
                              "pending"
                            ]
                          },
                          "confidence": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "enum": [
                              "confirmed",
                              "inferred",
                              "none",
                              null
                            ]
                          },
                          "last_checked": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          },
                          "probe_queued": {
                            "type": "boolean"
                          }
                        }
                      }
                    },
                    "probes_enabled": {
                      "type": "boolean"
                    },
                    "probes_queued": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/usage": {
      "get": {
        "tags": [
          "Utility"
        ],
        "operationId": "getApiUsage",
        "summary": "Usage by key and endpoint",
        "description": "Per-key call counts, success rate, last-used, and per-endpoint breakdown; the programmatic twin of the dashboard usage panel. Requires read permission. Live keys only.",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 30,
              "minimum": 1,
              "maximum": 90
            }
          },
          {
            "name": "key_id",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Restrict to one key in your scope."
          }
        ],
        "responses": {
          "200": {
            "description": "Usage report.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "period": {
                      "type": "object",
                      "properties": {
                        "days": {
                          "type": "integer"
                        },
                        "since": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "total_calls": {
                      "type": "integer"
                    },
                    "keys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/KeyUsage"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/mcp": {
      "post": {
        "tags": [
          "MCP"
        ],
        "operationId": "mcpEndpoint",
        "summary": "Hosted Model Context Protocol server",
        "description": "Stateless JSON-RPC 2.0 over Streamable HTTP. Point Claude, Cursor, or any MCP client at this endpoint with your Bearer key and it discovers the full tool set (send_message, get_conversation, list_groups, list_devices, check_message_status, opt-out and assistant controls, reactions, typing, read state, voice memos, contacts, capability lookup, and human-confirmed FaceTime calling). brk_test_ sandbox keys work on every tool, simulator only.",
        "parameters": [
          {
            "in": "header",
            "name": "MCP-Protocol-Version",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "2026-07-28",
                "2025-06-18",
                "2025-03-26",
                "2024-11-05"
              ]
            },
            "description": "Required and set to `2026-07-28` for modern requests; legacy clients negotiate through `initialize`."
          },
          {
            "in": "header",
            "name": "Mcp-Method",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Required for modern requests and must exactly match the JSON-RPC `method`."
          },
          {
            "in": "header",
            "name": "Mcp-Name",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Required for modern named calls such as `tools/call`; must match `params.name` (the 2026 sentinel Base64 form is accepted)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jsonrpc",
                  "method"
                ],
                "properties": {
                  "jsonrpc": {
                    "type": "string",
                    "enum": [
                      "2.0"
                    ]
                  },
                  "id": {
                    "type": [
                      "string",
                      "number"
                    ],
                    "description": "Request id, omit to send a notification."
                  },
                  "method": {
                    "type": "string",
                    "enum": [
                      "server/discover",
                      "initialize",
                      "ping",
                      "tools/list",
                      "tools/call",
                      "notifications/initialized"
                    ]
                  },
                  "params": {
                    "type": "object",
                    "description": "Modern requests also carry protocolVersion, clientInfo, and clientCapabilities fields inside `_meta`; conforming SDKs populate these automatically."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response, a `result` or `error` member, id echoed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jsonrpc": {
                      "type": "string"
                    },
                    "id": {
                      "type": [
                        "string",
                        "number",
                        "null"
                      ]
                    },
                    "result": {
                      "type": "object",
                      "description": "For modern tools/call: `{ resultType: \"complete\", content: [{type: \"text\", text}], structuredContent, isError }`. Legacy clients receive the same compatibility text content and may also consume structuredContent."
                    },
                    "error": {
                      "type": "object",
                      "description": "JSON-RPC error: -32601 unknown method, -32602 unknown tool / missing required argument, -32603 internal."
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Notification accepted (empty body)."
          },
          "400": {
            "description": "Parse error (-32700), invalid JSON-RPC request (-32600), unsupported protocol (-32022), or modern header/body mismatch (-32020)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Browser Origin is outside the authenticated-origin allowlist."
          },
          "404": {
            "description": "Unknown modern method (-32601); legacy JSON-RPC method errors remain HTTP 200."
          },
          "405": {
            "description": "Non-POST, the transport is stateless POST-only (no SSE stream)."
          },
          "415": {
            "description": "Content-Type must be application/json."
          }
        }
      }
    },
    "/v1/mcp/public": {
      "post": {
        "tags": [
          "MCP"
        ],
        "operationId": "publicMcpEndpoint",
        "summary": "Public MCP endpoint for agent clients",
        "description": "The same hosted MCP server, for clients that authenticate with a workspace-bound key and cannot send custom headers per tool call. Test keys answer 403 workspace_binding_required here; use POST /v1/mcp with a test key instead.",
        "security": [
          {
            "agentOAuth": [
              "workspace:read",
              "messages:read",
              "contacts:read",
              "automation:read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "header",
            "name": "MCP-Protocol-Version",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "2026-07-28",
                "2025-06-18",
                "2025-03-26",
                "2024-11-05"
              ]
            },
            "description": "Required and set to `2026-07-28` for modern requests; legacy clients negotiate through `initialize`."
          },
          {
            "in": "header",
            "name": "Mcp-Method",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Required for modern requests and must exactly match the JSON-RPC `method`."
          },
          {
            "in": "header",
            "name": "Mcp-Name",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Required for modern named calls such as `tools/call`; must match `params.name` (the 2026 sentinel Base64 form is accepted)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jsonrpc",
                  "method"
                ],
                "properties": {
                  "jsonrpc": {
                    "type": "string",
                    "enum": [
                      "2.0"
                    ]
                  },
                  "id": {
                    "type": [
                      "string",
                      "number"
                    ],
                    "description": "Request id, omit to send a notification."
                  },
                  "method": {
                    "type": "string",
                    "enum": [
                      "server/discover",
                      "initialize",
                      "ping",
                      "tools/list",
                      "tools/call",
                      "notifications/initialized"
                    ]
                  },
                  "params": {
                    "type": "object",
                    "description": "Modern requests also carry protocolVersion, clientInfo, and clientCapabilities fields inside `_meta`; conforming SDKs populate these automatically."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response, a `result` or `error` member, id echoed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jsonrpc": {
                      "type": "string"
                    },
                    "id": {
                      "type": [
                        "string",
                        "number",
                        "null"
                      ]
                    },
                    "result": {
                      "type": "object"
                    },
                    "error": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Notification accepted (empty body)."
          },
          "400": {
            "description": "Parse error, invalid JSON-RPC request, unsupported protocol, or modern header/body mismatch."
          },
          "401": {
            "description": "Missing, invalid, expired, revoked, or wrong-resource OAuth token. The response includes an RFC 9728 `WWW-Authenticate` challenge."
          },
          "403": {
            "description": "Grant, workspace, tool, scope, feature, or browser-Origin policy refused the request."
          },
          "405": {
            "description": "Non-POST, the transport is stateless POST-only (no SSE stream)."
          },
          "415": {
            "description": "Content-Type must be application/json."
          }
        }
      }
    },
    "/v1/calling-lines": {
      "get": {
        "tags": [
          "Calling"
        ],
        "operationId": "listCallingLines",
        "summary": "Private beta: List FaceTime calling lines",
        "description": "Private beta. Calling is switched on per workspace; until it is, this endpoint returns 404. Calling lines available to the workspace with coarse readiness: ready, offline, busy, or not ready. Requires the Calls capability on your account.",
        "x-permission": "calls:write",
        "x-rate-limit": "60/min per key",
        "responses": {
          "200": {
            "description": "Calling lines in the bound workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CallingLine"
                      }
                    },
                    "sandbox": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/calls": {
      "post": {
        "tags": [
          "Calling"
        ],
        "operationId": "prepareCall",
        "summary": "Private beta: Prepare a human-confirmed FaceTime call",
        "description": "Private beta. Calling is switched on per workspace; until it is, this endpoint returns 404. Prepares a call and returns a short-lived confirmation URL. Never dials autonomously: a signed-in human opens the URL and presses to start.",
        "x-permission": "calls:write",
        "x-rate-limit": "10/min per key",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "to"
                ],
                "properties": {
                  "to": {
                    "type": "string",
                    "pattern": "^\\+[1-9][0-9]{7,14}$",
                    "examples": [
                      "+13035550101"
                    ]
                  },
                  "line_id": {
                    "type": "string",
                    "description": "Required unless the API key has a default calling line."
                  },
                  "provider": {
                    "type": "string",
                    "enum": [
                      "facetime"
                    ],
                    "default": "facetime"
                  },
                  "contact_name": {
                    "type": "string",
                    "maxLength": 160
                  },
                  "client_reference": {
                    "type": "string",
                    "maxLength": 160
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay of the original prepared call.",
            "headers": {
              "Idempotent-Replay": {
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            }
          },
          "201": {
            "description": "Call prepared. No dial has occurred.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "The line is busy or the idempotency key conflicts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "tags": [
          "Calling"
        ],
        "operationId": "getCall",
        "summary": "Private beta: Read a call's lifecycle",
        "description": "Private beta. Calling is switched on per workspace; until it is, this endpoint returns 404. Returns the lifecycle of a call prepared by this key, with stable states and failure codes.",
        "x-permission": "calls:write",
        "x-rate-limit": "60/min per key",
        "parameters": [
          {
            "name": "call_id",
            "in": "query",
            "required": false,
            "description": "Call id from POST /api/v1/calls, or an incoming call id from a `call.incoming` event.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "description": "`inbound` lists recent incoming calls when `call_id` is absent.",
            "schema": {
              "type": "string",
              "enum": [
                "inbound"
              ]
            }
          },
          {
            "name": "line_id",
            "in": "query",
            "required": false,
            "description": "Restrict the inbound list to one calling line.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Inbound list page size (default 20, max 50).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One call, or a list of incoming calls.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Call"
                    },
                    {
                      "$ref": "#/components/schemas/InboundCall"
                    },
                    {
                      "$ref": "#/components/schemas/InboundCallList"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Calling"
        ],
        "operationId": "endCall",
        "summary": "Private beta: Cancel or end a call",
        "description": "Private beta. Calling is switched on per workspace; until it is, this endpoint returns 404. Cancels a prepared call, or requests that its linked active call end. Safe to repeat.",
        "x-permission": "calls:write",
        "x-rate-limit": "60/min per key",
        "parameters": [
          {
            "$ref": "#/components/parameters/CallId"
          }
        ],
        "responses": {
          "200": {
            "description": "The call is terminal or its end request was recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Call"
                    },
                    {
                      "$ref": "#/components/schemas/InboundCall"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/webhooks": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "registerWebhook",
        "summary": "Register your inbound webhook endpoint",
        "description": "Self-serve. Point Blue Reacher at your https endpoint; events are delivered there signed. Returns a signing secret once. Verify each delivery with X-BlueReacher-Signature = sha256=HMAC-SHA256(secret, raw_body). One endpoint per workspace; posting again replaces it. Live keys only.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "events": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "registered": {
                      "type": "boolean"
                    },
                    "url": {
                      "type": "string"
                    },
                    "events": {
                      "type": "string"
                    },
                    "secret": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "get": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "getWebhook",
        "summary": "Read your current webhook registration",
        "description": "Returns the endpoint URL and subscribed events for your workspace. The signing secret is never returned.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Current registration.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "registered": {
                      "type": "boolean"
                    },
                    "url": {
                      "type": "string"
                    },
                    "events": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "deleteWebhook",
        "summary": "Remove your webhook endpoint",
        "description": "Stops event delivery for your workspace. Idempotent.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/sandbox/inbound": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "sandboxInbound",
        "summary": "Simulate an inbound reply (sandbox only)",
        "description": "Test-key only. Fires a signed message.received event to the webhook you registered with your test key, so you can build and test stop-on-reply and exit conditions end to end without a real device. Register first with POST /v1/webhooks using your brk_test_ key. In the sandbox, brk_test_ keys also read /v1/contacts, /v1/capability and /v1/usage with realistic data marked sandbox: true.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "from": {
                    "type": "string",
                    "description": "Sender phone (E.164). Defaults to a sandbox number."
                  },
                  "content": {
                    "type": "string",
                    "description": "Reply text. Defaults to a sample reply."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Delivered to your registered sandbox webhook.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "delivered": {
                      "type": "boolean"
                    },
                    "endpoint_status": {
                      "type": "integer"
                    },
                    "event": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    }
  },
  "webhooks": {
    "device.connected": {
      "post": {
        "summary": "Partner customer linked a line",
        "description": "Partner-app lifecycle notification. Signed over the raw body with the Partner app's show-once secret and delivered with `X-Blue Reacher-Signature`, `X-Blue Reacher-Event`, and `User-Agent: Blue Reacher-Platform/1.0`. Delivery is best-effort and never controls activation: 5 s timeout, up to three attempts (0 s / 1 s / 3 s). Return 2xx quickly.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PartnerWebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "device.disconnected": {
      "post": {
        "summary": "Partner customer's linked line disconnected",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PartnerWebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "device.removed": {
      "post": {
        "summary": "Partner customer's linked line was removed",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PartnerWebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "message.received": {
      "post": {
        "summary": "Inbound message hit one of your lines",
        "description": "Register endpoints in the dashboard. Every delivery is an HTTPS POST with headers X-BlueReacher-Event and X-BlueReacher-Signature: sha256=<HMAC-SHA256(secret, raw_body)>. Verify by recomputing the HMAC over the raw body with your endpoint's secret. Delivery: 5 second timeout, up to 3 attempts (0s / 1s / 3s backoff); 3 consecutive failed deliveries auto-disable the endpoint.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx quickly; do heavy work async."
          }
        }
      }
    },
    "message.sent": {
      "post": {
        "summary": "An outbound message left one of your lines",
        "description": "Fires once per message at send confirmation, edge-triggered on the first transition into sent. For API sends this is usually the definitive event: US SMS and many iMessage sends never produce a delivery receipt, so do not wait for message.delivered to conclude a send left the line.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "message.failed": {
      "post": {
        "summary": "An outbound message failed to deliver",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "message.delivered": {
      "post": {
        "summary": "First delivery receipt for an outbound message",
        "description": "Edge-triggered on the first delivery receipt only. data.delivered_at carries the receipt timestamp.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "message.read": {
      "post": {
        "summary": "Recipient read your outbound message (read receipts on)",
        "description": "Edge-triggered on the first read receipt; `data.read_at` carries the chat.db timestamp. Only fires when the recipient has read receipts enabled. (2026-07-28+)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "reaction.received": {
      "post": {
        "summary": "A contact added or removed a tapback on a message",
        "description": "data: message_id, reaction (love/like/dislike/laugh/emphasize/question), emoji, action (added/removed), phone_number, contact_id, device_id. Contact reactions only; your own outbound tapbacks do not fire it.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "contact.created": {
      "post": {
        "summary": "A new contact was created by a message",
        "description": "`data`: `contact_id`, `phone_number`, `contact_name`, `source` (\"inbound_message\"), `direction`, `service_type`, `device_id`, `organization_id`. Covers contacts created by inbound/outbound messages through the bridge pipeline; contacts created by CRM sync do not emit it yet. (2026-07-28+)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "contact.opted_out": {
      "post": {
        "summary": "A contact opted out (STOP reply or API opt-out)",
        "description": "Fires from the single opt-out chokepoint, so every path emits identically. A later START/UNSTOP emits the separate contact.resubscribed event.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "contact.resubscribed": {
      "post": {
        "summary": "A previously opted-out contact became reachable again",
        "description": "Fires only when a contact who was actually opted out sends a recognized START/UNSTOP resubscribe phrase. A stray \"start\" in an ordinary conversation does not emit it. `data`: `contact_id`, `phone_number`, `organization_id`, `resubscribed_at`, and `source`. Consumers that suppress work after `contact.opted_out` should use this event to undo that suppression deliberately.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "device.status_changed": {
      "post": {
        "summary": "A line went online or offline",
        "description": "data: device_id, device_name, previous_status, new_status (online/offline).",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "device.health_changed": {
      "post": {
        "summary": "A connected line cannot deliver",
        "description": "Health beyond up/down: the line is connected but cannot actually send. data: device_id, device_name, state, severity, detected_at, and a per-state detail object. States include send_outage (critical), setup_incomplete (critical), imessage_collapse (critical), sms_not_configured (warning), outdated_version (warning).",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "call.prepared": {
      "post": {
        "summary": "A Calls API session is awaiting human confirmation",
        "description": "Fires after `POST /api/v1/calls` creates a forward-only live session. Existing sessions are never backfilled. Delivery uses the same HMAC, workspace/line scope, retry, log, replay, quota, and auto-disable rules as the messaging webhooks. `data` is the public call representation and never includes the confirmation URL/token, contact name, recordings, transcripts, bridge credentials, or provider diagnostics.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallWebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "call.confirmed": {
      "post": {
        "summary": "A signed-in human confirmed the prepared call",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallWebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "call.started": {
      "post": {
        "summary": "The platform accepted the call for bridge dispatch",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallWebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "call.ringing": {
      "post": {
        "summary": "The bridge started the native FaceTime call",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallWebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "call.answered": {
      "post": {
        "summary": "The recipient answered and live media was detected",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallWebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "call.completed": {
      "post": {
        "summary": "An answered call ended",
        "description": "Terminal and mutually exclusive with `call.failed`, `call.cancelled`, and `call.expired`. A hangup after answer remains answered until the bridge records completion, then emits this event.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallWebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "call.failed": {
      "post": {
        "summary": "The provider or call start failed before answer",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallWebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "call.cancelled": {
      "post": {
        "summary": "The call was stopped before answer",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallWebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "call.expired": {
      "post": {
        "summary": "The prepared call was not confirmed before expiry",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallWebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "call.incoming": {
      "post": {
        "summary": "A contact is ringing one of your calling lines",
        "description": "Fires when a line reports a FaceTime Audio ring from a known contact. `data` is the `InboundCall` representation; its `id` is the call id for `GET /api/v1/calls?call_id=` and for `DELETE` (decline). Answer stays in the Blue Reacher dashboard. `data` never includes the contact name, a recording, or a transcript.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallWebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "call.missed": {
      "post": {
        "summary": "An incoming call ended with nobody taking it",
        "description": "Terminal for an incoming call that was never answered. `missed_reason` is `no_answer`, `voicemail`, `declined` (dashboard or API), or `silenced` (macOS Focus or screen sharing kept it from ringing). Answered incoming calls end with `call.completed` instead; both carry `direction: inbound`.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallWebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "call.transferred": {
      "post": {
        "summary": "A teammate took over a live call",
        "description": "Fires when a teammate presses **Take call** on a transfer offered through `POST /api/v1/calls/transfer` or the Transfer button in Blue Reacher, for a call prepared through the API. The call stays `answered`; `data` adds `transfer_count` and `transferred_to_user_id`. Once per call, the first hand-off.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallWebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    },
    "call.transcribed": {
      "post": {
        "summary": "A recorded call's transcript is ready",
        "description": "Fires when a recorded call's transcript finishes, usually about a minute after `call.completed`, for any recorded call on your lines. `data` carries `transcript`, `transcript_truncated`, `summary`, `recording_url`, `workspace_id`, `phone_number`, `direction` and the same `id` the call's other events carried. An account with more than one workspace must set the webhook to one workspace to receive it.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallWebhookEnvelope"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledge."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your Blue Reacher API key (brk_live_ or brk_test_), sent as a Bearer token."
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Validation error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Resource exists but is outside this key's scope, or a feature/plan gate.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Not found in your scope.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded, retry after `retryAfter` seconds.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string"
                },
                "error_code": {
                  "type": "string"
                },
                "retryAfter": {
                  "type": "number"
                },
                "resetAt": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing/invalid/expired API key, or insufficient permission.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "BotStatus": {
        "type": "object",
        "description": "Per-contact AI assistant control state. status is null when mode is business_hours or after_hours. snoozed_until null while snoozed is true means the pause is indefinite.",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "phone": {
            "type": "string"
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "active",
              "active_always",
              "inactive",
              "snooze",
              null
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "inherit",
              "off",
              "always_on",
              "business_hours",
              "after_hours"
            ]
          },
          "assigned_bot_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "snoozed": {
            "type": "boolean"
          },
          "snoozed_until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "snoozed_indefinitely": {
            "type": "boolean"
          },
          "effective": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "active",
                  "active_outside_hours",
                  "contact_off",
                  "dnd",
                  "human_override",
                  "no_bot",
                  "user_disabled",
                  "org_disabled",
                  "test_mode"
                ]
              },
              "severity": {
                "type": "string",
                "enum": [
                  "active",
                  "warn",
                  "off"
                ]
              },
              "label": {
                "type": "string"
              },
              "detail": {
                "type": "string"
              }
            }
          },
          "effective_note": {
            "type": "string"
          }
        }
      },
      "Call": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "object",
          "provider",
          "status",
          "to",
          "line_id",
          "confirmation_required",
          "confirmation_expires_at",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "const": "call"
          },
          "provider": {
            "type": "string",
            "enum": [
              "facetime"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "prepared",
              "confirmed",
              "connecting",
              "ringing",
              "answered",
              "completed",
              "failed",
              "cancelled",
              "expired"
            ]
          },
          "to": {
            "type": "string",
            "pattern": "^\\+[1-9][0-9]{7,14}$"
          },
          "line_id": {
            "type": "string"
          },
          "client_reference": {
            "type": [
              "string",
              "null"
            ]
          },
          "confirmation_required": {
            "type": "boolean"
          },
          "confirmation_url": {
            "type": "string",
            "format": "uri",
            "description": "Present only on the initial non-sandbox prepare response."
          },
          "confirmation_expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "confirmed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "answered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "duration_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "failure_code": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "recipient_busy",
              "recipient_declined",
              "no_answer",
              "line_unavailable",
              "cancelled",
              "call_not_started",
              "call_failed",
              null
            ]
          },
          "sandbox": {
            "type": "boolean"
          }
        }
      },
      "CallWebhookData": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "object",
          "provider",
          "status",
          "to",
          "line_id",
          "client_reference",
          "confirmation_required",
          "confirmation_expires_at",
          "created_at",
          "confirmed_at",
          "started_at",
          "answered_at",
          "completed_at",
          "duration_seconds",
          "failure_code"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "call"
          },
          "provider": {
            "type": "string",
            "enum": [
              "facetime"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "prepared",
              "confirmed",
              "connecting",
              "ringing",
              "answered",
              "completed",
              "failed",
              "cancelled",
              "expired"
            ]
          },
          "to": {
            "type": "string",
            "pattern": "^\\+[1-9][0-9]{7,14}$"
          },
          "line_id": {
            "type": "string",
            "format": "uuid"
          },
          "client_reference": {
            "type": [
              "string",
              "null"
            ]
          },
          "confirmation_required": {
            "type": "boolean"
          },
          "confirmation_expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "confirmed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "answered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "duration_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "failure_code": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "CallWebhookEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "event",
          "event_id",
          "timestamp",
          "api_version",
          "data"
        ],
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "call.prepared",
              "call.confirmed",
              "call.started",
              "call.ringing",
              "call.answered",
              "call.completed",
              "call.failed",
              "call.cancelled",
              "call.expired",
              "call.incoming",
              "call.missed"
            ]
          },
          "event_id": {
            "type": "string",
            "format": "uuid",
            "description": "Stable for this occurrence across at-least-once delivery retries."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "api_version": {
            "type": "string",
            "const": "2026-03-07"
          },
          "data": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CallWebhookData"
              },
              {
                "$ref": "#/components/schemas/InboundCall"
              }
            ]
          }
        }
      },
      "CallingLine": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "object",
          "label",
          "provider",
          "readiness"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "const": "calling_line"
          },
          "label": {
            "type": "string"
          },
          "phone_number": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^\\+[1-9][0-9]{7,14}$"
          },
          "provider": {
            "type": "string",
            "enum": [
              "facetime"
            ]
          },
          "readiness": {
            "type": "string",
            "enum": [
              "ready",
              "offline",
              "busy",
              "not_ready"
            ]
          }
        }
      },
      "Contact": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "phone_number": {
            "type": "string"
          },
          "first_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "company": {
            "type": [
              "string",
              "null"
            ]
          },
          "tags": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "custom_fields": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "is_imessage": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "opted_out": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "last_contacted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_message_received_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ConversationMessage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ]
          },
          "message": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "text",
              "media",
              "voice_memo"
            ]
          },
          "sent_at": {
            "type": "string",
            "format": "date-time"
          },
          "device_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "delivery_channel": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "imessage",
              "sms",
              null
            ],
            "description": "Actual transport (blue vs green bubble)."
          },
          "delivery_unconfirmed": {
            "type": "boolean",
            "description": "true when status is sent but nothing on the line confirmed the text went out. A delivery or read receipt clears it."
          },
          "reactions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "emoji": {
                  "type": "string"
                },
                "emoji_name": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "from_contact": {
                  "type": "boolean"
                },
                "delivered": {
                  "type": "boolean"
                },
                "delivered_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "ai_generated": {
            "type": "boolean"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "error_code": {
            "type": "string"
          },
          "help": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "InboundCall": {
        "type": "object",
        "additionalProperties": false,
        "description": "A FaceTime Audio call a contact placed TO one of your line calling lines (`provider` is always `facetime`; Telnyx incoming calls are not exposed). `id` is the public call id (it arrives on `call.incoming`). `status` is `ringing`, `answered`, `completed`, or `missed`; `missed_reason` is set only when `status` is `missed`. `recording_url` appears only on GET responses (never in webhook payloads) and only when a recording exists.",
        "required": [
          "id",
          "object",
          "direction",
          "provider",
          "status",
          "from",
          "line_id",
          "contact_id",
          "started_at",
          "answered_at",
          "completed_at",
          "duration_seconds",
          "missed_reason",
          "recording_available"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "const": "call"
          },
          "direction": {
            "type": "string",
            "const": "inbound"
          },
          "provider": {
            "type": "string",
            "enum": [
              "facetime"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "ringing",
              "answered",
              "completed",
              "missed"
            ]
          },
          "from": {
            "type": "string",
            "description": "The contact's stored phone number."
          },
          "line_id": {
            "type": "string",
            "format": "uuid"
          },
          "contact_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "answered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "duration_seconds": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "missed_reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "no_answer",
              "voicemail",
              "declined",
              "silenced",
              null
            ]
          },
          "recording_available": {
            "type": "boolean"
          },
          "recording_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "GET responses only, when a recording exists."
          }
        }
      },
      "InboundCallList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "object",
          "data",
          "has_more"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InboundCall"
            }
          },
          "has_more": {
            "type": "boolean"
          }
        }
      },
      "KeyUsage": {
        "type": "object",
        "properties": {
          "key_id": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "key_prefix": {
            "type": [
              "string",
              "null"
            ]
          },
          "is_active": {
            "type": "boolean"
          },
          "organization_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "device_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "device_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "calls": {
            "type": "integer"
          },
          "success": {
            "type": "integer"
          },
          "errors": {
            "type": "integer"
          },
          "success_rate": {
            "type": [
              "integer",
              "null"
            ]
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "endpoints": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "endpoint": {
                  "type": "string"
                },
                "calls": {
                  "type": "integer"
                },
                "success": {
                  "type": "integer"
                }
              }
            }
          }
        }
      },
      "Line": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "number": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "online",
              "offline"
            ]
          },
          "sent_today": {
            "type": "integer"
          },
          "cold_outreach_today": {
            "type": "integer"
          },
          "daily_cap": {
            "type": "integer"
          },
          "queue_depth": {
            "type": "integer"
          },
          "available_today": {
            "type": "integer"
          },
          "last_seen": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "warmup": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "mode": {
                "type": "string"
              },
              "started_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "duration_days": {
                "type": "integer"
              },
              "active_days": {
                "type": "integer"
              }
            }
          },
          "fleet_warmup": {
            "type": [
              "object",
              "null"
            ]
          }
        },
        "description": "A sending line. Called device in field names."
      },
      "MessageRecord": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "content": {
            "type": [
              "string",
              "null"
            ]
          },
          "direction": {
            "type": "string",
            "enum": [
              "incoming",
              "outgoing"
            ]
          },
          "status": {
            "type": "string"
          },
          "service_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "media_urls": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "is_voice_message": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "voice_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "voice_duration_seconds": {
            "type": [
              "number",
              "null"
            ]
          },
          "device_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "sent_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "read_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "error_message": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PartnerWebhookEnvelope": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "device.connected",
              "device.disconnected",
              "device.removed"
            ]
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "api_version": {
            "type": "string",
            "const": "2026-05-18"
          },
          "data": {
            "type": "object",
            "properties": {
              "partner_user_id": {
                "type": "string"
              },
              "connection_id": {
                "type": "string",
                "format": "uuid"
              },
              "device_id": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uuid"
              },
              "device_name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "partner_user_id"
            ],
            "additionalProperties": true
          }
        },
        "required": [
          "event",
          "timestamp",
          "api_version",
          "data"
        ]
      },
      "WebhookEnvelope": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "message.received",
              "message.sent",
              "message.failed",
              "message.delivered",
              "message.read",
              "reaction.received",
              "contact.created",
              "contact.opted_out",
              "contact.resubscribed",
              "device.status_changed",
              "device.health_changed",
              "call.prepared",
              "call.confirmed",
              "call.started",
              "call.ringing",
              "call.answered",
              "call.completed",
              "call.failed",
              "call.cancelled",
              "call.expired",
              "call.incoming",
              "call.missed",
              "test"
            ]
          },
          "event_id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique per event occurrence and stable across delivery retries of that occurrence, deduplicate on it (delivery is at-least-once). Present on events fired 2026-07-28 or later."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "api_version": {
            "type": "string",
            "examples": [
              "2026-03-07"
            ]
          },
          "data": {
            "type": "object",
            "description": "Field set varies by event, message.* events carry the message shape below; reaction.*, contact.*, and line.* events carry the shapes described on their webhook entries. `call.*` events carry the exact public call representation in `CallWebhookData`.",
            "properties": {
              "message_id": {
                "type": "string"
              },
              "phone_number": {
                "type": "string"
              },
              "contact_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "contact_name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "content": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "direction": {
                "type": "string",
                "enum": [
                  "incoming",
                  "outgoing"
                ]
              },
              "service_type": {
                "type": "string",
                "description": "\"iMessage\" or \"SMS\"."
              },
              "device_name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "device_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "media_urls": {
                "type": [
                  "array",
                  "null"
                ],
                "items": {
                  "type": "string"
                }
              },
              "organization_id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        }
      }
    },
    "parameters": {
      "CallId": {
        "name": "call_id",
        "in": "query",
        "required": true,
        "description": "Call id returned by POST /api/v1/calls.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      }
    }
  }
}
