{
  "source_sha256": "48c69286dd60d57d0204cdc1f2b56e86d904b063e7f2ea59ee9934383e4a2c49",
  "endpoints": [
    {
      "group": "Numbers",
      "method": "GET",
      "name": "Search platform and supplier stock with account prices",
      "path": "/number-orders/search",
      "path_template": "/number-orders/search",
      "scope": "numbers:read",
      "desc": "Requires an approved partner account. Search UK geographic prefixes using source=all for platform and supported supplier stock. Supplier availability is deployment-specific. Prices use the target account currency and rates; integer amounts are in minor currency units. No number is reserved by a search.",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        },
        {
          "key": "prefix",
          "value": "0191",
          "description": "",
          "disabled": false
        },
        {
          "key": "source",
          "value": "stock",
          "description": "",
          "disabled": true
        },
        {
          "key": "limit",
          "value": "1",
          "description": "",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Numbers",
      "method": "POST",
      "name": "Refresh a selected number quote",
      "path": "/number-orders/quote",
      "path_template": "/number-orders/quote",
      "scope": "numbers:read, numbers:write",
      "desc": "Recalculates the signed candidate using current account pricing and returns a new expiry. Review the refreshed price before ordering.",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 1,
        "quote": "replace-me"
      }
    },
    {
      "group": "Numbers",
      "method": "POST",
      "name": "Order the number in a reviewed quote",
      "path": "/number-orders",
      "path_template": "/number-orders",
      "scope": "numbers:write",
      "desc": "Use one UUID for the entire order, including retries after a timeout or token replacement. The durable order identity is scoped to your root and target account. Reusing it with a different quote fails. Completed orders return allocation and billing references. Pending or review means the result is not yet confirmed: read its status and retry only the original request with the same key. Never submit a second key to work around an uncertain purchase. A number is initially unassigned; attach it to a provisioned service after completion.",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 1,
        "quote": "replace-me"
      }
    },
    {
      "group": "Numbers",
      "method": "GET",
      "name": "Read an order result",
      "path": "/number-orders/replace-me",
      "path_template": "/number-orders/{order_id}",
      "scope": "numbers:read",
      "desc": "Read-only status for this root and target account. This request does not buy, charge, or retry supplier actions.",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        }
      ],
      "if_match": false
    },
    {
      "group": "Services",
      "method": "GET",
      "name": "Discover available Simple, Flexible and IVR branch routing controls",
      "path": "/services/editor/catalog",
      "path_template": "/services/editor/catalog",
      "scope": "services:read",
      "desc": "Returns current account entitlements and enabled portal modules, with labelled typed fields. Required before creating a service.",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        }
      ],
      "if_match": false
    },
    {
      "group": "Services",
      "method": "GET",
      "name": "Find a destination or saved routing resource",
      "path": "/services/editor/resources/audio",
      "path_template": "/services/editor/resources/{kind}",
      "scope": "services:read",
      "desc": "Names and IDs only, scoped to the exact owning account. AI selections additionally require current agent permissions and package entitlement. Pages contain at most 50 records; meta.page and meta.has_more support pagination.",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        },
        {
          "key": "search",
          "value": "replace-me",
          "description": "",
          "disabled": true
        },
        {
          "key": "selected",
          "value": "replace-me",
          "description": "",
          "disabled": true
        },
        {
          "key": "page",
          "value": "1",
          "description": "",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Services",
      "method": "POST",
      "name": "Create a Simple or Flexible Routing service",
      "path": "/services/editor",
      "path_template": "/services/editor",
      "scope": "services:write",
      "desc": "Creates an unassigned service and its reviewed option batch atomically, including service history. Requires the estate service_writes gate. Does not move or allocate any number.",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 1,
        "name": "replace-me",
        "service_type": 1,
        "changes": []
      }
    },
    {
      "group": "Services",
      "method": "GET",
      "name": "Read labelled service settings for incremental editing",
      "path": "/services/1/editor",
      "path_template": "/services/{service_id}/editor",
      "scope": "services:read",
      "desc": "Passwords are never returned. Unknown, private or ambiguous legacy options are preserved and marked non-editable. Branching flows cannot use this editor.",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        }
      ],
      "if_match": false
    },
    {
      "group": "Services",
      "method": "PATCH",
      "name": "Add, amend, remove or reorder service settings atomically",
      "path": "/services/1/editor",
      "path_template": "/services/{service_id}/editor",
      "scope": "services:write",
      "desc": "Requires base_config_hash from the reviewed document (or If-Match), explicit account_id and estate service_writes. All changes roll back on stale revision (412), invalid reference or validation error. At least one main destination must remain. Unsubmitted fields, including saved passwords, are preserved. New destinations can use negative client_id values referenced by destination_order.",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 1
      }
    },
    {
      "group": "Accounts",
      "method": "GET",
      "name": "Read account details and removal eligibility",
      "path": "/accounts/1/management",
      "path_template": "/accounts/{account_id}/management",
      "scope": "accounts:read",
      "desc": "Returns contact details, remaining asset counts and a revision for review. Account 1 cannot be removed. Only an account-1 platform administrator can remove an empty child account.",
      "query": [],
      "if_match": false
    },
    {
      "group": "Accounts",
      "method": "GET",
      "name": "List your accounts",
      "path": "/accounts",
      "path_template": "/accounts",
      "scope": "accounts:read",
      "desc": "Returns descendant accounts (not your own account). Use /me for your current account. Scope accounts:read.",
      "query": [
        {
          "key": "page",
          "value": "1",
          "description": "",
          "disabled": true
        },
        {
          "key": "per_page",
          "value": "25",
          "description": "",
          "disabled": true
        },
        {
          "key": "search",
          "value": "replace-me",
          "description": "Match company name or contact name. Pending verification accounts are included.",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Accounts",
      "method": "POST",
      "name": "Create an account",
      "path": "/accounts",
      "path_template": "/accounts",
      "scope": "accounts:write",
      "desc": "Creates a child under an authorised reseller parent. Companion requests use the portal country/timezone/payment/retention/welcome controls and canonical published rate-plan or parent-rate-copy transaction. An account and its hierarchy, rates, plan provenance and prepay setup commit together; a failed plan leaves no partial account. Welcome emails use the current estate portal branding and 24-hour activation. Skipping the email explicitly verifies the account. Approved partner integrations use the same account setup and rate-plan contract.",
      "query": [],
      "if_match": false,
      "body": {}
    },
    {
      "group": "Accounts",
      "method": "DELETE",
      "name": "Remove an empty child account",
      "path": "/accounts/1",
      "path_template": "/accounts/{account_id}",
      "scope": "accounts:write",
      "desc": "Soft-deletes only an empty child account, preserving financial history. Requires the account_writes gate, account-1 administrator and a fresh management revision. Active numbers, services, users, children, devices, packages or credit block removal. Does not send messages.",
      "query": [],
      "if_match": false,
      "body": {
        "revision": "replace-me"
      }
    },
    {
      "group": "Accounts",
      "method": "PATCH",
      "name": "Edit an account",
      "path": "/accounts/1",
      "path_template": "/accounts/{account_id}",
      "scope": "accounts:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n\nUpdates an account's details. The target must be a sub-account (descendant)\nin your hierarchy; your own account cannot be edited here (404). Partial\nupdate — only supplied fields change. Scope `accounts:write`.\n",
      "query": [],
      "if_match": false,
      "body": {
        "name": "replace-me"
      }
    },
    {
      "group": "Numbers",
      "method": "GET",
      "name": "Search available number inventory",
      "path": "/numbers/available",
      "path_template": "/numbers/available",
      "scope": "numbers:read",
      "desc": "Inventory discovery, not a binding quote or reservation. Supplier-less and\nWhatsApp-held numbers are excluded. Use the portal checkout for a current\naccount-specific setup/rental quote and purchase. API allocation is not\navailable in v1. Scope `numbers:read`.\n",
      "query": [
        {
          "key": "page",
          "value": "1",
          "description": "",
          "disabled": true
        },
        {
          "key": "per_page",
          "value": "25",
          "description": "",
          "disabled": true
        },
        {
          "key": "country",
          "value": "replace-me",
          "description": "ISO 3166-1 alpha-2 country code (e.g. GB, AU)",
          "disabled": true
        },
        {
          "key": "prefix",
          "value": "replace-me",
          "description": "Match numbers starting with this prefix (digits, E.164 without +)",
          "disabled": true
        },
        {
          "key": "grade",
          "value": "platinum",
          "description": "Number grade filter",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Numbers",
      "method": "GET",
      "name": "List allocated numbers",
      "path": "/numbers",
      "path_template": "/numbers",
      "scope": "numbers:read",
      "desc": "Numbers allocated to your accounts. Scope `numbers:read`.",
      "query": [
        {
          "key": "page",
          "value": "1",
          "description": "",
          "disabled": true
        },
        {
          "key": "per_page",
          "value": "25",
          "description": "",
          "disabled": true
        },
        {
          "key": "account_id",
          "value": "1",
          "description": "Filter to one account",
          "disabled": true
        },
        {
          "key": "search",
          "value": "replace-me",
          "description": "Literal substring of the allocated number or description; a leading + is accepted.",
          "disabled": true
        },
        {
          "key": "unassigned",
          "value": "False",
          "description": "When true, include only numbers already allocated to the selected account with no active service attachment. This is not purchasable stock.",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Numbers",
      "method": "GET",
      "name": "Get an allocated number",
      "path": "/numbers/1",
      "path_template": "/numbers/{number_id}",
      "scope": "numbers:read",
      "desc": "Reads the current in-tree allocation, service link, cancellation and\nactual rental snapshot. Missing or ambiguous rental data is not guessed.\nA number with multiple current allocations fails with 409\nnumber_allocation_conflict. Unallocated/out-of-tree numbers return 404.\n",
      "query": [],
      "if_match": false
    },
    {
      "group": "Audio",
      "method": "GET",
      "name": "List audio files",
      "path": "/audio",
      "path_template": "/audio",
      "scope": "services:read",
      "desc": "Lists the pre-recorded and text-to-speech audio files your account can\nreference by `audio_id` when building services (whisper, greetings,\nannouncements, voicemail greetings, queue music, and so on). Audio is\ncreated and managed in the portal; this endpoint lets an API client\ndiscover the ids it may use. Scope `services:read`.\nRequires `capabilities.features.media_reads=true`; otherwise authenticated\nrequests return `403 feature_unavailable`, including this legacy alias.\n",
      "query": [
        {
          "key": "page",
          "value": "1",
          "description": "",
          "disabled": true
        },
        {
          "key": "per_page",
          "value": "25",
          "description": "",
          "disabled": true
        },
        {
          "key": "account_id",
          "value": "1",
          "description": "Filter to one account in your tree.",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Resources",
      "method": "GET",
      "name": "List attachable AI agents",
      "path": "/agents",
      "path_template": "/agents",
      "scope": "services:read",
      "desc": "Lists the AI agents you can attach as a destination — those on the account\nthat ALSO have an active call package (the same condition required to\nattach one). Use an agent's `id` as `ai_agent_id` in a route/target.\nAgents are created/managed in the portal. Scope `services:read`.\n",
      "query": [
        {
          "key": "page",
          "value": "1",
          "description": "",
          "disabled": true
        },
        {
          "key": "per_page",
          "value": "25",
          "description": "",
          "disabled": true
        },
        {
          "key": "account_id",
          "value": "1",
          "description": "Filter to one account in your tree.",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Resources",
      "method": "GET",
      "name": "List routing lists (ACL / ABR / DEX / IP)",
      "path": "/lists",
      "path_template": "/lists",
      "scope": "services:read",
      "desc": "Lists the routing lists you can reference by `list_id` when building\nservices: ACL (number allow/block), ABR (area/prefix), DEX (date) and IP\nlists. Lists are created/managed in the portal. Scope `services:read`.\n",
      "query": [
        {
          "key": "page",
          "value": "1",
          "description": "",
          "disabled": true
        },
        {
          "key": "per_page",
          "value": "25",
          "description": "",
          "disabled": true
        },
        {
          "key": "account_id",
          "value": "1",
          "description": "Filter to one account in your tree.",
          "disabled": true
        },
        {
          "key": "type",
          "value": "acl",
          "description": "Filter to one list type.",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Resources",
      "method": "POST",
      "name": "Create a routing list",
      "path": "/lists",
      "path_template": "/lists",
      "scope": "services:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n\nCreates an ACL/ABR/DEX/IP list for an account, optionally with entries.\n`mode` sets the acl list behaviour as CallPilot applies it (see ListCreate). Entry shape depends on\n`type`: acl `{number}`, abr `{area}`, ip `{ip}`,\ndex `{name,start_date,end_date}`. Scope `services:write`.\n",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 1,
        "type": "acl",
        "name": "replace-me"
      }
    },
    {
      "group": "Resources",
      "method": "GET",
      "name": "List address-book contacts",
      "path": "/address-book",
      "path_template": "/address-book",
      "scope": "services:read",
      "desc": "Lists the address-book contacts you can reference by `address_book_id` as\na routing destination. Contacts are created/managed in the portal.\nScope `services:read`.\n",
      "query": [
        {
          "key": "page",
          "value": "1",
          "description": "",
          "disabled": true
        },
        {
          "key": "per_page",
          "value": "25",
          "description": "",
          "disabled": true
        },
        {
          "key": "account_id",
          "value": "1",
          "description": "Filter to one account in your tree.",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Resources",
      "method": "POST",
      "name": "Create an address-book contact",
      "path": "/address-book",
      "path_template": "/address-book",
      "scope": "services:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n\nCreates a contact for an account, optionally with destinations. Each\ndestination's type is derived from its `target` (a SIP URI containing `@`\nis treated as SIP, otherwise a phone number). Scope `services:write`.\n",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 1,
        "name": "replace-me"
      }
    },
    {
      "group": "Numbers",
      "method": "PUT",
      "name": "Change an allocated number's service",
      "path": "/numbers/1/service",
      "path_template": "/numbers/{number_id}/service",
      "scope": "services:write, numbers:read",
      "desc": "Moves an allocated number between services in the same verified owning account.\nRequires the estate service_writes switch and read_only=false. No billing or allocation changes.\nRead both service config_hash values before reviewing this change. Both service revisions are\nchecked and written to the portal history in one transaction. Stale routing or revisions return\n412 with no partial change. Use expected_service_id=null for an unassigned number.\n",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 1,
        "service_id": 1,
        "expected_service_id": 1,
        "config_hash": "replace-me"
      }
    },
    {
      "group": "Numbers",
      "method": "POST",
      "name": "Number allocation (unavailable in v1)",
      "path": "/numbers/1/allocate",
      "path_template": "/numbers/{number_id}/allocate",
      "scope": "numbers:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n\nCompatibility route only. A valid in-scope request returns 503\nnumber_allocation_unavailable; no allocation, rental or charge is written.\nUse the portal checkout, which owns setup prices, recurring quotes,\nbespoke rates and prepay receipts. Enabling number_writes does not enable\nthis operation. Scope `numbers:write`.\n",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 1
      }
    },
    {
      "group": "Numbers",
      "method": "POST",
      "name": "Immediate number release (unavailable in v1)",
      "path": "/numbers/1/release",
      "path_template": "/numbers/{number_id}/release",
      "scope": "numbers:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n\nCompatibility route only. A valid current in-scope allocation returns\n503 number_release_unavailable without changing routing, allocation or\nbilling. Use /numbers/{number_id}/cancel for scheduled cancellation,\nor the portal for immediate teardown. Enabling number_writes does not\nenable this operation. Scope `numbers:write`.\n",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 1
      }
    },
    {
      "group": "Services",
      "method": "GET",
      "name": "List inbound services",
      "path": "/services",
      "path_template": "/services",
      "scope": "services:read",
      "desc": "Scope `services:read`.",
      "query": [
        {
          "key": "page",
          "value": "1",
          "description": "",
          "disabled": true
        },
        {
          "key": "per_page",
          "value": "25",
          "description": "",
          "disabled": true
        },
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": true
        },
        {
          "key": "search",
          "value": "replace-me",
          "description": "Service name or attached E.164 number substring.",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Services",
      "method": "POST",
      "name": "Create an inbound service",
      "path": "/services",
      "path_template": "/services",
      "scope": "services:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n\nCreates an inbound service for an account, optionally attaching\nan allocated number and routing it in one call. The common flow\n\"this number should ring this SIP destination\" is a single request:\n\n```json\n{\n  \"account_id\": 123,\n  \"name\": \"Support line\",\n  \"number_id\": 456,\n  \"route\": { \"type\": \"sip\", \"sip_uri\": \"sip:agent42@sip.client-platform.com\" }\n}\n```\nScope `services:write`.\n",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 2,
        "name": "Support Line",
        "route": {
          "type": "sip",
          "sip_uri": "sip:reception@example.com"
        }
      }
    },
    {
      "group": "Services",
      "method": "GET",
      "name": "Get a service",
      "path": "/services/1",
      "path_template": "/services/{service_id}",
      "scope": "services:read",
      "desc": "",
      "query": [],
      "if_match": false
    },
    {
      "group": "Services",
      "method": "PATCH",
      "name": "Update a service",
      "path": "/services/1",
      "path_template": "/services/{service_id}",
      "scope": "services:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n\nApply only fields in the service's supported_updates. Name is independently\neditable; simple route updates require an unambiguous linear SIP service.\nA complete flow graph may replace an existing API-shaped graph. Portal\nnode-list graphs and rich configuration sections remain read-only.\nSupply the reviewed config_hash via If-Match or base_config_hash.\nMissing precondition returns 428; stale configuration returns 412.\nUnsupported configuration replacement returns 422 configuration_update_unsupported.\nExisting flexible/linear controls returned in editable_options may be patched\nvia option_edits. IDs must belong to this service; type, order, branches,\nsecret parameters and omitted fields are preserved. Cannot be mixed with route/flow.\nRing strategy applies to every compatible main target. Hours use service timezone;\nuse non-overlapping same-day windows (empty means closed all week).\nScope services:write.\n",
      "query": [],
      "if_match": true,
      "body": {
        "account_id": 2,
        "name": "Support Line"
      }
    },
    {
      "group": "Services",
      "method": "DELETE",
      "name": "Delete a service",
      "path": "/services/1",
      "path_template": "/services/{service_id}",
      "scope": "services:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n\nDetaches numbers and records a recoverable service version. Requires the reviewed config_hash through If-Match or base_config_hash. Scope services:write.\n",
      "query": [],
      "if_match": true,
      "body": {
        "account_id": 1
      }
    },
    {
      "group": "Services",
      "method": "POST",
      "name": "Attach a number to a service",
      "path": "/services/1/numbers",
      "path_template": "/services/{service_id}/numbers",
      "scope": "services:write",
      "desc": "Attach an already allocated, currently unassigned number to an owned service using the reviewed If-Match/base_config_hash. Number and service must belong to the exact same account. Current companion scopes, role, editor permission and estate service-writes gate apply. Numbers already assigned elsewhere are refused; this does not purchase stock or move an assigned number between services.",
      "query": [],
      "if_match": true,
      "body": {
        "account_id": 1,
        "number_id": 1
      }
    },
    {
      "group": "Services",
      "method": "GET",
      "name": "List what you can provision",
      "path": "/services/capabilities",
      "path_template": "/services/capabilities",
      "scope": "services:read",
      "desc": "Returns the service types your account is entitled to create (and\nwhether each can be built through this API), plus feature flags resolved\nfrom your account's permissions. Use this to decide which options to\noffer before calling `POST /v1/services`. Requires the `services:read`\nscope.\n",
      "query": [],
      "if_match": false
    },
    {
      "group": "Conferences",
      "method": "POST",
      "name": "Control a live conference — mute/deaf, kick, or dial a party in",
      "path": "/conferences/DDI-12345678-1234-4234-8234-123456789abc-support/actions",
      "path_template": "/conferences/{conference_id}/actions",
      "scope": "conferences:write",
      "desc": "Control an existing live conference using `list`, `mute`, `unmute`, `deaf`,\n`undeaf`, `kick`, `hup`, `kill` or `dial`. No room move, transfer, call\ndiscovery or conference event subscription/delivery API is supported.\n\nRequires an explicit `conferences:write` token grant, the estate conference\nfeature and runtime readiness. This integration grant is independent of\npartner provisioning approval. Global API read-only mode, the current\ntoken owner's restrictions and account hierarchy still apply. All actions,\nincluding `list`, use POST and require a UUID `Idempotency-Key` header.\nCheck `GET /capabilities` for this estate and caller before offering actions.\n\nSupply a safe room token in `conference_id` and a full live channel UUID in\n`fs_uuid`, obtained from an independently verified estate integration.\nThe server resolves the anchor's owning account/node, checks the current\npermitted account hierarchy and reads the named room's members. It verifies\nthe owned anchor belongs to that room before returning room data or sending\nany mutating action, and preflights targeted membership before dispatch.\nKnowing a room name or an unrelated live leg does not authorise control.\nMissing or inaccessible anchors/rooms return a generic `404`.\n\nTo control a dialled SIP or AI leg, retain the original caller's owned live\nUUID in `fs_uuid` and put the target leg's UUID in `member`. A dialled leg\nwithout its own account-owned call-state record cannot serve as the anchor\nand returns `404`.\n\nTargeted actions require a positive member ID or member channel UUID;\n`kill` sends only the server-verified channel UUID to the executor. Dial targets accept conservative PSTN digits with\nan optional leading plus, or a safe SIP URI; command fragments, whitespace\nand URI parameters are rejected. Dial can originate a billable call.\nThe executor uses the verified anchor's billing/routing context. For `dial`,\nprovide `ddi` unless the room has a numeric DDI prefix; `cli` defaults to `ddi`.\n\nA successful `dial` returns `sip_leg_uuid`, the channel UUID the server\nassigns to the new leg before originating it, so you know the leg's ID\nstraight away. Pass it as `member` to mute, kick, hang up or kill that leg\nlater. The response is returned once the call is started, before it is\nanswered: until the leg joins the room, or if it is never answered,\nactions targeting it return `409 call_ended`.\n\nA disabled estate feature returns `403 feature_unavailable`; incomplete\ncall-state, executor credential, allowed hosts or required routing context returns\n`503 conference_unavailable`. Capability readiness is not a live health\nprobe or acceptance evidence. A sandbox needs isolated test call-state\nand an isolated executor; production call-state is not a sandbox fallback.\n\nReuse the same idempotency key for the identical request. Recorded results\ncan be replayed, but an external action can have executed before a timeout\nor lost response. Reconcile live call state before retrying a billable or\ndestructive action after an ambiguous timeout or 5xx.\n",
      "query": [],
      "if_match": false,
      "body": {
        "action": "mute",
        "fs_uuid": "12345678-1234-4234-8234-123456789abc",
        "member": "7"
      }
    },
    {
      "group": "Users",
      "method": "GET",
      "name": "List portal users",
      "path": "/users",
      "path_template": "/users",
      "scope": "users:read",
      "desc": "",
      "query": [
        {
          "key": "page",
          "value": "1",
          "description": "",
          "disabled": true
        },
        {
          "key": "per_page",
          "value": "25",
          "description": "",
          "disabled": true
        },
        {
          "key": "account_id",
          "value": "1",
          "description": "Exact account in the verified token hierarchy; omitted means the whole hierarchy.",
          "disabled": true
        },
        {
          "key": "search",
          "value": "replace-me",
          "description": "Literal substring of email, first name or last name.",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Users",
      "method": "POST",
      "name": "Create a portal user",
      "path": "/users",
      "path_template": "/users",
      "scope": "users:write",
      "desc": "Creates a portal identity within the current hierarchy. Manual password setup marks the email verified and sends no invitation. Companion administrators may instead request send_invite=true: the existing branded portal invitation is sent and the identity remains pending verification. Current role ordering and delegated permissions apply; a failed baseline trigger rolls the creation back. Email transport failure is reported explicitly without duplicating the created user.",
      "query": [],
      "if_match": false,
      "body": {}
    },
    {
      "group": "Users",
      "method": "GET",
      "name": "Get a portal user",
      "path": "/users/1",
      "path_template": "/users/{user_id}",
      "scope": "users:read",
      "desc": "",
      "query": [],
      "if_match": false
    },
    {
      "group": "Users",
      "method": "PATCH",
      "name": "Update a portal user",
      "path": "/users/1",
      "path_template": "/users/{user_id}",
      "scope": "users:write",
      "desc": "Reviewed partial identity update. Companion sessions require account_id plus the current revision and may edit lower-ranked users in their own account or equal/lower-ranked users in descendants. Self identity is not edited here. Email, phone, password, role, disabling sign-in or permissions changes revoke existing login credentials and API tokens. Phone changes clear SMS MFA. Omitted permissions are preserved; supplied IDs must be grantable by the actor.",
      "query": [],
      "if_match": false,
      "body": {
        "firstname": "replace-me"
      }
    },
    {
      "group": "Users",
      "method": "DELETE",
      "name": "Delete a portal user",
      "path": "/users/1",
      "path_template": "/users/{user_id}",
      "scope": "users:write",
      "desc": "Remove a current in-tree user using the reviewed account and revision. Companion portal role ordering is enforced; same-account peers and the signed-in identity are protected. Integration tokens cannot delete administrator roles. Existing login sessions and API tokens are revoked.",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 1,
        "revision": "replace-me"
      }
    },
    {
      "group": "Users",
      "method": "POST",
      "name": "Resend a pending portal user invitation",
      "path": "/users/1/invite",
      "path_template": "/users/{user_id}/invite",
      "scope": "users:write",
      "desc": "Companion administrators may resend an invitation for an editable, unverified user. Requires the current reviewed account and revision. The portal rechecks ownership, role and revision before using its existing activation mailer. Delivery failure is returned explicitly; use a new operation key only after a confirmed failed result. Retry ambiguous failures with the same key. Verified users and protected identities cannot be invited through this endpoint.",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 1,
        "revision": "replace-me"
      }
    },
    {
      "group": "Numbers",
      "method": "POST",
      "name": "Schedule number cancellation",
      "path": "/numbers/1/cancel",
      "path_template": "/numbers/{number_id}/cancel",
      "scope": "numbers:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n\nRecords the portal's 30-day notice intent on the exact current allocation and marks applicable WhatsApp registrations removal_requested in the same transaction. Existing quarantine/reuse holds are not cleared or shortened. The estate cancellation job owns later teardown and reuse eligibility; this request does not detach routing. A repeated pending request returns 200. Conflicting active allocations return 409 number_allocation_conflict. Requires number_writes enabled and numbers:write. Immediate cancellation is unavailable (422 not_available).\n",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 1
      }
    },
    {
      "group": "Resources",
      "method": "PATCH",
      "name": "Rename a routing list",
      "path": "/lists/acl/1",
      "path_template": "/lists/{type}/{id}",
      "scope": "services:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n",
      "query": [],
      "if_match": false,
      "body": {
        "name": "replace-me"
      }
    },
    {
      "group": "Resources",
      "method": "DELETE",
      "name": "Delete a routing list",
      "path": "/lists/acl/1",
      "path_template": "/lists/{type}/{id}",
      "scope": "services:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n\nSoft-deletes the resource and its entries.\n",
      "query": [],
      "if_match": false
    },
    {
      "group": "Resources",
      "method": "GET",
      "name": "List entries in a routing list",
      "path": "/lists/acl/1/entries",
      "path_template": "/lists/{type}/{id}/entries",
      "scope": "services:read",
      "desc": "Returns every active entry ordered by id. This endpoint is not paginated.",
      "query": [],
      "if_match": false
    },
    {
      "group": "Resources",
      "method": "POST",
      "name": "Add an entry to a routing list",
      "path": "/lists/acl/1/entries",
      "path_template": "/lists/{type}/{id}/entries",
      "scope": "services:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n\nFields depend on the path type: acl requires number; abr requires area; ip requires ip; dex requires start_date and end_date, with optional name.\n\nAn acl number already active on the list returns `409 list_entry_exists`\nwith `details.entry_id`; a previously removed one is reactivated (201).\nCallPilot matches acl entries exactly or as a caller-number prefix.\n\nCallPlatform app sessions may only add a number of 7–20 digits (optional\nleading plus) to an acl list whose `mode` is `deny`, need the portal\nConfigs permission, and require `capabilities.features.caller_blocking`.\nThis changes live routing wherever the list is used.\n",
      "query": [],
      "if_match": false,
      "body": {
        "number": "replace-me"
      }
    },
    {
      "group": "Resources",
      "method": "PATCH",
      "name": "Update an entry in a routing list",
      "path": "/lists/acl/1/entries/1",
      "path_template": "/lists/{type}/{id}/entries/{entryId}",
      "scope": "services:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n\nPartial update; provide at least one writable field appropriate for this entry type.\n",
      "query": [],
      "if_match": false,
      "body": {
        "number": "replace-me"
      }
    },
    {
      "group": "Resources",
      "method": "DELETE",
      "name": "Delete an entry from a routing list",
      "path": "/lists/acl/1/entries/1",
      "path_template": "/lists/{type}/{id}/entries/{entryId}",
      "scope": "services:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n",
      "query": [],
      "if_match": false
    },
    {
      "group": "Resources",
      "method": "PATCH",
      "name": "Rename a address-book contact",
      "path": "/address-book/1",
      "path_template": "/address-book/{id}",
      "scope": "services:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n",
      "query": [],
      "if_match": false,
      "body": {
        "name": "replace-me"
      }
    },
    {
      "group": "Resources",
      "method": "DELETE",
      "name": "Delete a address-book contact",
      "path": "/address-book/1",
      "path_template": "/address-book/{id}",
      "scope": "services:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n\nSoft-deletes the resource and its entries.\n",
      "query": [],
      "if_match": false
    },
    {
      "group": "Resources",
      "method": "GET",
      "name": "List entries in a address-book contact",
      "path": "/address-book/1/entries",
      "path_template": "/address-book/{id}/entries",
      "scope": "services:read",
      "desc": "Returns every active entry ordered by id. This endpoint is not paginated.",
      "query": [],
      "if_match": false
    },
    {
      "group": "Resources",
      "method": "POST",
      "name": "Add an entry to a address-book contact",
      "path": "/address-book/1/entries",
      "path_template": "/address-book/{id}/entries",
      "scope": "services:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n\ntarget is required. SIP/number target type is derived by the server; schedule defaults to all days open.\n",
      "query": [],
      "if_match": false,
      "body": {
        "target": "replace-me"
      }
    },
    {
      "group": "Resources",
      "method": "PATCH",
      "name": "Update an entry in a address-book contact",
      "path": "/address-book/1/entries/1",
      "path_template": "/address-book/{id}/entries/{entryId}",
      "scope": "services:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n\nPartial update; provide at least one writable field appropriate for this entry type.\n",
      "query": [],
      "if_match": false,
      "body": {
        "target": "replace-me"
      }
    },
    {
      "group": "Resources",
      "method": "DELETE",
      "name": "Delete an entry from a address-book contact",
      "path": "/address-book/1/entries/1",
      "path_template": "/address-book/{id}/entries/{entryId}",
      "scope": "services:write",
      "desc": "Default-on read-only release guard: when `GET /capabilities` reports\n`data.read_only=true`, this operation returns `403 api_read_only` even\nfor a write-scoped token. When false, token scopes and individual estate\nfeature gates still apply.\n",
      "query": [],
      "if_match": false
    },
    {
      "group": "Reports",
      "method": "GET",
      "name": "Current SIP connections grouped by authorised account",
      "path": "/reports/active-calls/overview",
      "path_template": "/reports/active-calls/overview",
      "scope": "reports:read",
      "desc": "Additive estate-gated overview of SBC dialog connections, not unique phone calls. Requires active_calls_overview capability. Current account is the default; include_children=true or selecting a child root additionally requires a bound administrator, effective accounts:read and reports:read scopes and current Live Stats permission. Ordinary companion users can read only their own account. Hierarchy and permissions are revalidated on every request. No unrelated account names or SIP details are returned. Zero-activity accounts are included. Accounts sort by total connections descending, case-insensitive account name ascending, then account ID. q is a literal case-insensitive account-name substring; totals ignore search and pagination. Each offset page is a fresh snapshot, so activity may reorder accounts between pages. Limits are 1,000 accounts (selected root first, then ID), 10,000 newest account-owned dialog connections and a two-second database statement deadline. is_complete describes aggregation completeness, independently of page has_more; truncated totals are lower bounds and omitted activity must not be presented as zero. When both bounds apply, truncation_reason is account_limit. scoped_accounts and matching_accounts describe only sampled accounts. Errors are never empty snapshots. Database state can lag calls. Estates without this feature retain /reports/active-calls. Unknown query parameters are rejected.",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        },
        {
          "key": "include_children",
          "value": "False",
          "description": "",
          "disabled": true
        },
        {
          "key": "page",
          "value": "1",
          "description": "One-based live snapshot page; (page - 1) times per_page must be less than 1,000.",
          "disabled": true
        },
        {
          "key": "per_page",
          "value": "25",
          "description": "",
          "disabled": true
        },
        {
          "key": "q",
          "value": "replace-me",
          "description": "",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Reports",
      "method": "GET",
      "name": "Current SIP connections for one account",
      "path": "/reports/active-calls",
      "path_template": "/reports/active-calls",
      "scope": "reports:read",
      "desc": "Read-only SBC dialog snapshot, limited to 100 newest connections. Companion users also need the portal Live Stats permission. Delayed database state may lag the live call. Does not control calls or promise a unique session count across call legs.",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        }
      ],
      "if_match": false
    },
    {
      "group": "Reports",
      "method": "GET",
      "name": "List calls",
      "path": "/reports/calls",
      "path_template": "/reports/calls",
      "scope": "reports:read",
      "desc": "Uses the estate reporting service. Account scope defaults to the token hierarchy, or an exact verified account_id. Dates are inclusive calendar days in `timezone` (an IANA zone, default UTC), defaulting to the last seven days including today in that zone and bounded to 31 days. Call times in results stay UTC. Caller/destination are literal substring filters. Unknown query parameters are rejected. No pricing, raw CDR JSON or custom SQL is exposed. Live offset pagination has a 10,000-row offset ceiling; narrow dates for deeper results. Inbound records represent sessions; outbound records represent call legs.",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "Exact account in the verified token hierarchy; omitted means the whole hierarchy.",
          "disabled": true
        },
        {
          "key": "direction",
          "value": "inbound",
          "description": "",
          "disabled": true
        },
        {
          "key": "start_date",
          "value": "2026-09-01",
          "description": "First calendar day in timezone, inclusive. Default: six days before end_date.",
          "disabled": true
        },
        {
          "key": "end_date",
          "value": "2026-09-01",
          "description": "Last calendar day in timezone, inclusive. Default: today in timezone. At most 31 days.",
          "disabled": true
        },
        {
          "key": "timezone",
          "value": "Australia/Sydney",
          "description": "IANA timezone (backward-compatible aliases accepted) whose calendar days start_date and end_date name, for example Europe/London. Default: UTC. Unknown zones and bare offsets return 422.",
          "disabled": true
        },
        {
          "key": "caller",
          "value": "replace-me",
          "description": "Literal substring; percent, underscore and backslash are not wildcards.",
          "disabled": true
        },
        {
          "key": "destination",
          "value": "replace-me",
          "description": "Literal substring of the inbound number or outbound destination.",
          "disabled": true
        },
        {
          "key": "min_duration",
          "value": "0",
          "description": "",
          "disabled": true
        },
        {
          "key": "max_duration",
          "value": "1",
          "description": "Maximum seconds, at least min_duration; omitted means no upper duration filter.",
          "disabled": true
        },
        {
          "key": "page",
          "value": "1",
          "description": "",
          "disabled": true
        },
        {
          "key": "per_page",
          "value": "25",
          "description": "",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Reports",
      "method": "GET",
      "name": "Summarise calls",
      "path": "/reports/summary",
      "path_template": "/reports/summary",
      "scope": "reports:read",
      "desc": "Uses the estate reporting service. Account scope defaults to the token hierarchy, or an exact verified account_id. Dates are inclusive calendar days in `timezone` (an IANA zone, default UTC), defaulting to the last seven days including today in that zone and bounded to 31 days. Call times in results stay UTC. Caller/destination are literal substring filters. Unknown query parameters are rejected. No pricing, raw CDR JSON or custom SQL is exposed. Status meanings match the existing portal call logs; Connected is the log status, not strict human-answer analytics.",
      "query": [
        {
          "key": "group_by",
          "value": "service",
          "description": "Optional grouping by stable service id, destination number, or status. Returns groups alongside unchanged totals and statuses. At most 1000 group/status pairs; narrow dates for larger results.",
          "disabled": true
        },
        {
          "key": "account_id",
          "value": "1",
          "description": "Exact account in the verified token hierarchy; omitted means the whole hierarchy.",
          "disabled": true
        },
        {
          "key": "direction",
          "value": "inbound",
          "description": "",
          "disabled": true
        },
        {
          "key": "start_date",
          "value": "2026-09-01",
          "description": "First calendar day in timezone, inclusive. Default: six days before end_date.",
          "disabled": true
        },
        {
          "key": "end_date",
          "value": "2026-09-01",
          "description": "Last calendar day in timezone, inclusive. Default: today in timezone. At most 31 days.",
          "disabled": true
        },
        {
          "key": "timezone",
          "value": "Australia/Sydney",
          "description": "IANA timezone (backward-compatible aliases accepted) whose calendar days start_date and end_date name, for example Europe/London. Default: UTC. Unknown zones and bare offsets return 422.",
          "disabled": true
        },
        {
          "key": "caller",
          "value": "replace-me",
          "description": "Literal substring; percent, underscore and backslash are not wildcards.",
          "disabled": true
        },
        {
          "key": "destination",
          "value": "replace-me",
          "description": "Literal substring of the inbound number or outbound destination.",
          "disabled": true
        },
        {
          "key": "min_duration",
          "value": "0",
          "description": "",
          "disabled": true
        },
        {
          "key": "max_duration",
          "value": "1",
          "description": "Maximum seconds, at least min_duration; omitted means no upper duration filter.",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Media",
      "method": "GET",
      "name": "List audio available to the account",
      "path": "/media/audio",
      "path_template": "/media/audio",
      "scope": "media:read",
      "desc": "Requires capabilities.features.media_reads=true; otherwise authenticated requests return 403 feature_unavailable. Audio catalogue under media:read. The compatibility /audio route provides the same gated catalogue under services:read. processed indicates the portal processor completed, not that the retained S3 object still exists; download verifies object readiness. Reviewed companion recording/import uses the separately gated audio library upload routes and the portal processor. Unknown filters are rejected and offset must not exceed 10,000.",
      "query": [
        {
          "key": "page",
          "value": "1",
          "description": "",
          "disabled": true
        },
        {
          "key": "per_page",
          "value": "25",
          "description": "",
          "disabled": true
        },
        {
          "key": "account_id",
          "value": "1",
          "description": "Exact account in the verified token hierarchy; omitted means the whole hierarchy.",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Media",
      "method": "GET",
      "name": "Get a temporary audio download URL",
      "path": "/media/audio/1/download",
      "path_template": "/media/audio/{audio_id}/download",
      "scope": "media:read",
      "desc": "Requires capabilities.features.media_reads=true; otherwise authenticated requests return 403 feature_unavailable. Authorises an active in-tree audio file and checks its processed WAV exists using S3 metadata only. Unprocessed/archived objects return 409 media_not_ready; missing/empty objects return 404 media_not_found; storage/IAM failures return 503 media_unavailable. Returns a signed GET URL valid for at most five minutes. Use disposition=inline for playback. Load it directly without forwarding the API bearer token to S3.",
      "query": [
        {
          "key": "disposition",
          "value": "attachment",
          "description": "Use inline for audio-player playback; attachment downloads the file.",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Media",
      "method": "GET",
      "name": "List recording, voicemail and conference metadata",
      "path": "/media/recordings",
      "path_template": "/media/recordings",
      "scope": "media:read",
      "desc": "Requires capabilities.features.media_reads=true; otherwise authenticated requests return 403 feature_unavailable. Metadata from the estate transcription/media catalogue, scoped to the token tree. Not an inventory of every S3 object. Inclusive metadata creation dates are calendar days in `timezone` (default UTC) and default to the last seven days including today, maximum 31 days; media processing may finish after the report's call-start date. Filter by exact call_id from reports and supply an explicit date window for historical playback. Zero or multiple media rows can match a call. Page must be at most 1000 and offset at most 10,000. Unknown query keys are rejected. Invalid stored dates return 502 media_bad_response.",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "Exact account in the verified token hierarchy; omitted means the whole hierarchy.",
          "disabled": true
        },
        {
          "key": "page",
          "value": "1",
          "description": "Page number; (page - 1) * per_page must also not exceed 10,000.",
          "disabled": true
        },
        {
          "key": "per_page",
          "value": "25",
          "description": "",
          "disabled": true
        },
        {
          "key": "start_date",
          "value": "2026-09-01",
          "description": "First calendar day in timezone, inclusive. Default: six days before end_date.",
          "disabled": true
        },
        {
          "key": "end_date",
          "value": "2026-09-01",
          "description": "Last calendar day in timezone, inclusive. Default: today in timezone. At most 31 days.",
          "disabled": true
        },
        {
          "key": "timezone",
          "value": "Australia/Sydney",
          "description": "IANA timezone (backward-compatible aliases accepted) whose calendar days start_date and end_date name, for example Europe/London. Default: UTC. Unknown zones and bare offsets return 422.",
          "disabled": true
        },
        {
          "key": "call_id",
          "value": "replace-me",
          "description": "",
          "disabled": true
        },
        {
          "key": "media_type",
          "value": "recording",
          "description": "",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Media",
      "method": "GET",
      "name": "Get recording metadata and transcript",
      "path": "/media/recordings/1",
      "path_template": "/media/recordings/{recording_id}",
      "scope": "media:read",
      "desc": "Requires capabilities.features.media_reads=true; otherwise authenticated requests return 403 feature_unavailable. Returns metadata plus transcript and summary when available. Stored media URLs and unscoped storage paths are not returned. Invalid stored dates return 502 media_bad_response.",
      "query": [],
      "if_match": false
    },
    {
      "group": "Media",
      "method": "GET",
      "name": "Get a temporary recording download URL",
      "path": "/media/recordings/1/download",
      "path_template": "/media/recordings/{recording_id}/download",
      "scope": "media:read",
      "desc": "Requires capabilities.features.media_reads=true; otherwise authenticated requests return 403 feature_unavailable. Rechecks ownership, validates the configured estate bucket and checks the S3 object metadata before signing. Missing/empty media returns 404 media_not_found; archived media returns 409 media_not_ready; storage/IAM failures return 503 media_unavailable. The URL is valid for at most five minutes. Use disposition=inline for playback, request a new URL after expiry, and never forward the API bearer token to S3. Objects can still disappear after this check.",
      "query": [
        {
          "key": "disposition",
          "value": "attachment",
          "description": "Use inline for audio-player playback; attachment downloads the file.",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Discovery",
      "method": "GET",
      "name": "Get the authenticated API identity",
      "path": "/me",
      "path_template": "/me",
      "scope": "Authenticated token",
      "desc": "Returns the verified token owner and account plus granted scopes. An estate-scoped service token may have user:null. Secrets and token hashes are not returned.",
      "query": [],
      "if_match": false
    },
    {
      "group": "Discovery",
      "method": "GET",
      "name": "Discover the estate and enabled API features",
      "path": "/capabilities",
      "path_template": "/capabilities",
      "scope": "Authenticated token",
      "desc": "Reports effective estate/caller gates and configuration readiness, not a live backend health probe. Conference control additionally requires an explicit conferences:write grant, a permitted current owner, configured call-state connection, executor credential and an executor host allowlist. Partner provisioning approval is independent of conference access; a true conference flag does not prove executor reachability, membership or live acceptance. data.read_only describes the caller's effective release access; false does not independently grant provisioning or any other write workflow. The global estate read-only guard is enabled by default and, when active, rejects every unsafe authenticated method with 403 api_read_only and advertises write features false. media_reads is independently disabled by default and gates both /media/* and /audio, including metadata. Download flags additionally require their configured bucket; no flag proves private media storage or object readiness. A true feature flag still requires the appropriate token scope. Check /services/capabilities for estate service-type entitlements. Unsupported invitation and upload workflows are explicitly false.",
      "query": [],
      "if_match": false
    },
    {
      "group": "Services",
      "method": "POST",
      "name": "Create an unassigned IVR keypad service",
      "path": "/services/ivr",
      "path_template": "/services/ivr",
      "scope": "services:write",
      "desc": "",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 1,
        "name": "Reception keypad",
        "menu": {
          "greeting_audio_id": "1",
          "max_repeats": 3,
          "routes": [
            {
              "digit": "1",
              "name": "Repeat",
              "action": "repeat"
            }
          ]
        }
      }
    },
    {
      "group": "Services",
      "method": "GET",
      "name": "Read an IVR keypad menu without exposing private routing parameters",
      "path": "/services/1/ivr",
      "path_template": "/services/{service_id}/ivr",
      "scope": "services:read",
      "desc": "",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        }
      ],
      "if_match": false
    },
    {
      "group": "Services",
      "method": "PATCH",
      "name": "Review and incrementally update an IVR tree",
      "path": "/services/1/ivr",
      "path_template": "/services/{service_id}/ivr",
      "scope": "services:write",
      "desc": "",
      "query": [],
      "if_match": true,
      "body": {
        "account_id": 1,
        "name": "replace-me",
        "menu": {
          "greeting_audio_id": "replace-me",
          "max_repeats": 1,
          "routes": [
            {
              "digit": "replace-me",
              "name": "replace-me",
              "action": "replace-me"
            }
          ]
        }
      }
    },
    {
      "group": "Services",
      "method": "GET",
      "name": "Explore a service flow by following branches",
      "path": "/services/1/flow-overview",
      "path_template": "/services/{service_id}/flow-overview",
      "scope": "services:read",
      "desc": "Read-only navigation for API graphs and portal node lists. Parameters, credentials, headers and URLs are never included. The result is not a publishable graph.",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        }
      ],
      "if_match": false
    },
    {
      "group": "AI agents",
      "method": "GET",
      "name": "List account-owned AI agents including drafts",
      "path": "/agents/management",
      "path_template": "/agents/management",
      "scope": "agents:read",
      "desc": "",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        },
        {
          "key": "page",
          "value": "1",
          "description": "",
          "disabled": true
        },
        {
          "key": "search",
          "value": "replace-me",
          "description": "",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "AI agents",
      "method": "GET",
      "name": "Read safe AI settings, curated models and flow navigation",
      "path": "/agents/replace-me/management",
      "path_template": "/agents/{agent_id}/management",
      "scope": "agents:read",
      "desc": "",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        }
      ],
      "if_match": false
    },
    {
      "group": "AI agents",
      "method": "PATCH",
      "name": "Review and update existing AI voice and language-model settings",
      "path": "/agents/replace-me/management",
      "path_template": "/agents/{agent_id}/management",
      "scope": "agents:write",
      "desc": "Requires current portal agents permission, a live account hierarchy, agent_writes estate feature and a companion principal. Only submitted controls change; prompts, tools, privacy, graph and automation remain untouched. Server-only provider credentials. A stale revision returns 412; an uncertain publish keeps its idempotent operation pending.",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 2,
        "revision": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
        "settings": {
          "turn_timeout": 10
        }
      }
    },
    {
      "group": "AI agents",
      "method": "GET",
      "name": "Read editable AI identity, greeting, voice and language fields",
      "path": "/agents/replace-me/configuration",
      "path_template": "/agents/{agent_id}/configuration",
      "scope": "agents:read",
      "desc": "Requires a companion principal with current portal agents permission and an agent owned by the selected account in its permitted hierarchy. Returns field values, labels, bounds and current voice/language choices; provider credentials and raw provider configuration are never returned. A supported published CallPlatform flow exposes fields even when the principal cannot write; editable is the write permission indicator. Unsupported saved flows return supported=false, editable=false, a reason and empty fields. Draft agents cannot use this endpoint. Handover node references open the separate flow editor. Language-model and voice-tuning controls remain on the management endpoint.",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "Exact owning account, verified against the principal's current permitted hierarchy.",
          "disabled": false
        }
      ],
      "if_match": false
    },
    {
      "group": "AI agents",
      "method": "PATCH",
      "name": "Publish changed AI identity, greeting, voice and language fields",
      "path": "/agents/replace-me/configuration",
      "path_template": "/agents/{agent_id}/configuration",
      "scope": "agents:write",
      "desc": "Requires the agent_writes estate feature, a writable companion principal and current portal agent-management permission. Send only changed field keys from the reviewed configuration document, its revision and exact account_id. Omitted fields retain their saved values; text is literal, including @, +, percent signs and template variables. The portal compiler preserves unedited graph data and unrelated provider/automation settings. Voice choices and speech-model language compatibility are checked again at publication. Custom greeting variations that cannot be identified safely require editing in the portal. A successful response is the refreshed document with a new revision. Publication spans the provider, automation registry and portal; it is not an atomic transaction across those systems. A 503 may follow an applied provider update, and keeps the durable operation pending. Reuse the same Idempotency-Key and identical body to check the reviewed operation; do not retry it under a fresh key. Inspect the current agent and portal restore history before deciding on recovery. No automatic provider retry or compensation is performed.",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 2,
        "revision": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
        "fields": {
          "greeting": "Hello, you have reached Example Support. How can I help?",
          "business_name": "Example Support"
        }
      }
    },
    {
      "group": "AI agents",
      "method": "GET",
      "name": "Read AI steps, editable fields and stable path identities",
      "path": "/agents/replace-me/flow",
      "path_template": "/agents/{agent_id}/flow",
      "scope": "agents:read",
      "desc": "Requires the same account ownership and current portal agents permission as configuration. Supported published CallPlatform flows expose stable node IDs and named ports with target IDs. Fields and insertion templates define the controls the client may offer. Advanced nodes and unrecognised ports are retained with editable=false; do not infer permission from their kind. Unsupported graphs remain read-only with a reason and safe navigation metadata. The response is a projection, not a replacement graph or provider payload. Draft agents are unsupported.",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "Exact owning account, verified against the principal's current permitted hierarchy.",
          "disabled": false
        }
      ],
      "if_match": false
    },
    {
      "group": "AI agents",
      "method": "PATCH",
      "name": "Publish incremental AI step edits, path changes and insertions",
      "path": "/agents/replace-me/flow",
      "path_template": "/agents/{agent_id}/flow",
      "scope": "agents:write",
      "desc": "Requires the agent_writes estate feature and current companion agent-management permission. Send the reviewed flow revision, exact account_id and 1–25 operations in execution order. update_node changes only submitted fields; reconnect changes one known port; insert_node creates a client-named step after an editable port. Node deletion, arbitrary node kinds and raw graph replacement are not supported. Unknown node data, connection metadata and protected handover rules remain stored. Decision routes are a full list keyed by stable route IDs: removing a route removes its outgoing edge. Handover rules are incremental by their original index; omission never deletes a rule. Inserting a step routes the selected incoming path to it and connects each of its outgoing ports to the prior target, when present. Reconnect later in the same request to choose other targets. Every new decision or transfer fallback port must be connected before saving. The server compiles and validates the resulting flow before publishing. Publication is not atomic across the provider, automation registry and portal. An uncertain 503 can follow a partial publish and leaves the operation pending; never submit a fresh key to repeat it. Reuse the original key and body, inspect current state and use portal restore history for recovery. A 200 confirms the reviewed changes and returns the refreshed flow document.",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 2,
        "revision": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
        "operations": [
          {
            "op": "update_node",
            "node_id": "instruction_1",
            "fields": {
              "instruction": "Ask the caller for their preferred appointment day."
            }
          },
          {
            "op": "reconnect",
            "node_id": "decision_1",
            "port": "yes",
            "target_id": "instruction_1"
          }
        ]
      }
    },
    {
      "group": "AI agents",
      "method": "GET",
      "name": "List filtered AI conversations and aggregates of the returned page",
      "path": "/agents/replace-me/insights",
      "path_template": "/agents/{agent_id}/insights",
      "scope": "agents:read",
      "desc": "Reads stored provider analysis without generating evaluations or summaries. Omit both dates for the last seven UTC calendar days, including today; otherwise supply both inclusive dates, at most 90 days. Filters are applied at the provider and checked again on returned rows. Repeat the same dates and filters when using the opaque cursor; clear the cursor after any filter change. Each response contains at most 25 conversations. Summary and trends describe only its returned page; complete_range is true only for an initial, untruncated page with no more results. Provider records can change while paginating; this is not a fixed report snapshot.",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        },
        {
          "key": "cursor",
          "value": "replace-me",
          "description": "",
          "disabled": true
        },
        {
          "key": "date_from",
          "value": "2026-09-01",
          "description": "Inclusive UTC start date; requires date_to, at most 90 inclusive days.",
          "disabled": true
        },
        {
          "key": "date_to",
          "value": "2026-09-07",
          "description": "Inclusive UTC end date; requires date_from.",
          "disabled": true
        },
        {
          "key": "status",
          "value": "initiated",
          "description": "Provider processing state; independent of evaluation outcome.",
          "disabled": true
        },
        {
          "key": "outcome",
          "value": "success",
          "description": "Stored success evaluation; error means evaluation failed to run, not a failed call.",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "AI agents",
      "method": "GET",
      "name": "Read a conversation summary and spoken transcript",
      "path": "/agents/replace-me/conversations/replace-me",
      "path_template": "/agents/{agent_id}/conversations/{conversation_id}",
      "scope": "agents:read",
      "desc": "",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        }
      ],
      "if_match": false
    },
    {
      "group": "Media",
      "method": "GET",
      "name": "List own and direct-parent shared audio",
      "path": "/media/audio/library",
      "path_template": "/media/audio/library",
      "scope": "media:read",
      "desc": "",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        },
        {
          "key": "page",
          "value": "1",
          "description": "",
          "disabled": true
        },
        {
          "key": "search",
          "value": "replace-me",
          "description": "",
          "disabled": true
        },
        {
          "key": "category_id",
          "value": "0",
          "description": "",
          "disabled": true
        }
      ],
      "if_match": false
    },
    {
      "group": "Media",
      "method": "GET",
      "name": "List the portal audio category catalogue",
      "path": "/media/audio/categories",
      "path_template": "/media/audio/categories",
      "scope": "media:read",
      "desc": "",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        }
      ],
      "if_match": false
    },
    {
      "group": "Media",
      "method": "POST",
      "name": "Upload a recorded or imported WAV or MP3 clip",
      "path": "/media/audio/uploads",
      "path_template": "/media/audio/uploads",
      "scope": "media:write",
      "desc": "WAV is validated as 16-bit PCM mono/stereo at supported 8–48 kHz rates. MP3 is probed as a single audio stream. Both are at most 5 MiB and 120 seconds. No external URL or user filename is accepted. Processing uses the same portal normalization/upload worker in a private directory, producing 16 kHz mono WAV in the estate media bucket. Poll the library for ready or failed status. Requires media_writes and configs.php permission.",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 1,
        "name": "replace-me",
        "audio_base64": "replace-me"
      }
    },
    {
      "group": "Media",
      "method": "PATCH",
      "name": "Update the name and category of owned audio",
      "path": "/media/audio/1",
      "path_template": "/media/audio/{audio_id}",
      "scope": "media:write",
      "desc": "",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 1,
        "name": "replace-me",
        "revision": "replace-me"
      }
    },
    {
      "group": "Media",
      "method": "DELETE",
      "name": "Remove owned audio when no active service or agent uses it",
      "path": "/media/audio/1",
      "path_template": "/media/audio/{audio_id}",
      "scope": "media:write",
      "desc": "",
      "query": [],
      "if_match": false,
      "body": {
        "account_id": 1,
        "revision": "replace-me"
      }
    },
    {
      "group": "Media",
      "method": "GET",
      "name": "Play authorized ready audio with a short-lived signed URL",
      "path": "/media/audio/1/preview",
      "path_template": "/media/audio/{audio_id}/preview",
      "scope": "media:read",
      "desc": "",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        }
      ],
      "if_match": false
    },
    {
      "group": "Accounts",
      "method": "GET",
      "name": "Get current account setup choices and published plans",
      "path": "/accounts/options",
      "path_template": "/accounts/options",
      "scope": "accounts:read",
      "desc": "Companion sessions only. Uses current portal country/timezone choices and the same published, customer-safe rate-plan catalogue. No supplier provenance or pricing credentials are returned.",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        }
      ],
      "if_match": false
    },
    {
      "group": "Users",
      "method": "GET",
      "name": "Get current assignable user roles and page permissions",
      "path": "/users/options",
      "path_template": "/users/options",
      "scope": "users:read",
      "desc": "",
      "query": [
        {
          "key": "account_id",
          "value": "1",
          "description": "",
          "disabled": false
        }
      ],
      "if_match": false
    },
    {
      "group": "Services",
      "method": "GET",
      "name": "List searchable IANA time zones with their current UTC offsets",
      "path": "/services/timezones",
      "path_template": "/services/timezones",
      "scope": "services:read",
      "desc": "",
      "query": [],
      "if_match": false
    }
  ]
}
