{"openapi":"3.1.0","info":{"title":"Cenaly PBX API","version":"1.0.0","description":"REST API of your Cenaly phone system: call log, recordings and transcripts, click-to-call, extensions, forwarding and do-not-disturb, presence and routing. The same key also works with the MCP server at `https://api.meni.ge/pbx-api/mcp` (streamable HTTP) for AI assistants.\n\nCreate a key in the admin panel: **Telephony → API & MCP**. Pass it as `Authorization: Bearer <key>`.\n\nChatGPT, claude.ai and other MCP connectors that cannot send a key connect with OAuth 2.1 instead: paste `https://api.meni.ge/pbx-api/mcp` as the connector URL, sign in and allow access (discovery `/.well-known/oauth-protected-resource/pbx-api/mcp`, dynamic client registration, PKCE S256). The access token works on every endpoint below with the scopes the owner allowed.\n\nLimits per key: 120 requests/min, 20000 per day; click-to-call 10/min and 300/day.\n\nLive call events (`call.started`, `call.ringing`, `call.answered`, `call.transferred`, `call.ended`): poll `GET /v1/events` (or the MCP tool `get_call_events`), or create a webhook (`POST /v1/webhooks`, scope `webhooks:write`) — each event is POSTed as JSON `{id, type, createdAt, locationId, webhookId, attempt, data}` with the header `X-Cenaly-Signature: t=<unix>,v1=<hex>`, where v1 = HMAC-SHA256(secret, `<t>.<raw body>`). Verify it in constant time and reject t older than 5 minutes. Answer 2xx within 10 s; otherwise the event is retried for about an hour (10 s, 30 s, 2, 5, 10, 15, 15 min) with the same `id`. The call processing webhook of the Calls section (Calls → Sources → Webhook) still posts the transcript and analysis after each recorded call.\n\nChanging call rules (`routes:write`) and loading the configuration from tables (`config:import`) are separate rights that no ready-made set includes. Every change made with a key appears in the admin action log and in the history of the section under the key name, and can be rolled back. Allowed countries and anti-fraud limits of outside calls are not changed by API keys."},"servers":[{"url":"https://api.meni.ge/pbx-api"}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"Key from Telephony → API & MCP. Scopes: calls:read, recordings:read, calls:dial, extensions:read, extensions:write, routes:read, routes:write, presence:read, reports:read, config:export, config:import, webhooks:write, daynight:write."},"oauth2":{"type":"oauth2","description":"For MCP connectors (ChatGPT, claude.ai): the owner signs in and allows access; the connection shows up in Telephony → API & MCP and is revoked there.","flows":{"authorizationCode":{"authorizationUrl":"https://api.meni.ge/pbx-api/oauth/authorize","tokenUrl":"https://api.meni.ge/pbx-api/oauth/token","refreshUrl":"https://api.meni.ge/pbx-api/oauth/token","scopes":{"calls:read":"Read the call log","recordings:read":"Listen to recordings and read transcripts","calls:dial":"Click-to-call (spends call minutes)","extensions:read":"See extensions, forwarding and do-not-disturb","extensions:write":"Change forwarding and do-not-disturb","routes:read":"See numbers and routing rules","routes:write":"Change inbound and outbound routing rules","presence:read":"See who is free or on a call","reports:read":"Read call-center metrics and analytics","config:export":"Export the phone system configuration file","config:import":"Import a configuration file (preview, then apply)","webhooks:write":"Manage webhooks that receive live call events","daynight:write":"Close or open the location (*28)"}}}}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]},"CallEvent":{"type":"object","required":["id","type","at","callId","seq"],"properties":{"id":{"type":"string","description":"`<callId>:<seq>` — a retried delivery carries the same id"},"type":{"type":"string","enum":["call.started","call.ringing","call.answered","call.transferred","call.ended"]},"at":{"type":"string","format":"date-time"},"callId":{"type":"string"},"seq":{"type":"integer"},"direction":{"type":"string","enum":["inbound","outbound","internal","transfer"]},"from":{"$ref":"#/components/schemas/Party"},"to":{"$ref":"#/components/schemas/Party"},"lineId":{"type":"string"},"lineLabel":{"type":"string"},"party":{"$ref":"#/components/schemas/Party","description":"ringing: whose phone rings; answered: who answered; transferred: to whom"},"by":{"$ref":"#/components/schemas/Party","description":"transferred: who transferred"},"status":{"type":"string","description":"ended: answered | missed | busy | cancelled | …"},"answeredBy":{"$ref":"#/components/schemas/Party"},"durationSec":{"type":"integer"},"talkSec":{"type":"integer"},"ringSec":{"type":"integer"},"hangupBy":{"type":"string","enum":["caller","callee","pbx"]}}},"Webhook":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"},"description":"Empty = all call events"},"locations":{"type":"array","items":{"type":"string"},"description":"Empty = all locations"},"enabled":{"type":"boolean"},"secretHint":{"type":"string"},"createdAt":{"type":"string"}}},"OpenClosed":{"type":"object","properties":{"location":{"type":"string"},"codeEnabled":{"type":"boolean"},"remoteSupported":{"type":"boolean"},"closed":{"type":"boolean"},"closedBy":{"type":"string","description":"Extension or \"cloud\""},"closedUntil":{"type":"string","format":"date-time","description":"Closed until this moment (set with `until`); reopens by itself"},"request":{"type":"object","properties":{"closed":{"type":"boolean"},"at":{"type":"integer"},"applied":{"type":"boolean"}}}}},"Party":{"type":"object","properties":{"kind":{"type":"string"},"number":{"type":"string"},"name":{"type":"string"},"extension":{"type":"string"}}},"Call":{"type":"object","properties":{"id":{"type":"string"},"startedAt":{"type":"string","format":"date-time"},"answeredAt":{"type":"string","format":"date-time"},"endedAt":{"type":"string","format":"date-time"},"direction":{"type":"string","enum":["inbound","outbound","internal","transfer"]},"status":{"type":"string"},"statusGroup":{"type":"string","enum":["answered","missed","ai","other"]},"from":{"$ref":"#/components/schemas/Party"},"to":{"$ref":"#/components/schemas/Party"},"answeredBy":{"$ref":"#/components/schemas/Party"},"line":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"}}},"ringSec":{"type":"integer"},"talkSec":{"type":"integer"},"durationSec":{"type":"integer"},"hangupBy":{"type":"string"},"steps":{"type":"array","items":{"type":"object"},"description":"Routing trace (single call only)"}}},"ForwardTarget":{"oneOf":[{"type":"object","properties":{"kind":{"const":"extension"},"extension":{"type":"string"}},"required":["kind","extension"]},{"type":"object","properties":{"kind":{"const":"number"},"number":{"type":"string"}},"required":["kind","number"]},{"type":"object","properties":{"kind":{"const":"voicemail"}},"required":["kind"]}]},"Via":{"oneOf":[{"type":"object","properties":{"kind":{"const":"line"},"lineId":{"type":"string"}},"required":["kind","lineId"]},{"type":"object","properties":{"kind":{"const":"trunk"},"trunkId":{"type":"string"}},"required":["kind","trunkId"]}]},"InboundRule":{"type":"object","description":"Only the fields you pass change; null removes an optional field. Distribution to people (grid, queue, \"everyone\") is edited in the admin panel — route to a ring group with action `extension` and the group number.","properties":{"name":{"type":["string","null"]},"enabled":{"type":"boolean"},"match":{"type":"object","description":"{kind:\"any\"} | {kind:\"time\",days:[0-6],from:\"HH:MM\",to:\"HH:MM\"} | {kind:\"callerPrefix\",callerPrefix} | {kind:\"line\",lineId,targets?} | {kind:\"trunk\",trunkId,targets?}"},"when":{"type":["object","null"],"description":"Extra conditions, all must match: days, from, to, callerPrefix, lineId, trunkId, targets"},"action":{"type":"string","enum":["extension","ivr","voicemail","forwardExternal","greeting","secretary","bot","hangup","echo"]},"extension":{"type":"string"},"botId":{"type":"string"},"forwardNumber":{"type":"string"},"menuId":{"type":"string"},"fallback":{"type":["object","null"],"properties":{"action":{"type":"string"},"extension":{"type":"string"},"botId":{"type":"string"},"timeoutSec":{"type":"integer"}}}}},"OutboundRule":{"type":"object","properties":{"name":{"type":["string","null"]},"enabled":{"type":"boolean"},"pattern":{"type":"string","description":"Digits, X (0-9), N (2-9), Z (1-9), leading +, trailing \".\""},"via":{"$ref":"#/components/schemas/Via"},"backupVia":{"oneOf":[{"$ref":"#/components/schemas/Via"},{"type":"null"}]},"scope":{"type":"object","description":"{kind:\"all\"} | {kind:\"extensions\",extensions:[\"101\"]}"},"stripDigits":{"type":"integer","minimum":0,"maximum":10},"prefix":{"type":"string"}}},"ConfigTables":{"type":"object","description":"{ <table>: [ { <column>: <cell text> } ] } — the same tables and columns as the Excel/CSV export (extensions, groups, lines, inbound, outbound). A table may also be passed exactly as `GET /v1/config/export` returns it (`{ columns, rows }`)."},"Forwarding":{"type":["object","null"],"properties":{"always":{"$ref":"#/components/schemas/ForwardTarget"},"busy":{"$ref":"#/components/schemas/ForwardTarget"},"noAnswer":{"$ref":"#/components/schemas/ForwardTarget"},"noAnswerSec":{"type":"integer","minimum":5,"maximum":120},"offline":{"$ref":"#/components/schemas/ForwardTarget"},"dnd":{"$ref":"#/components/schemas/ForwardTarget"}}}}},"paths":{"/v1/me":{"get":{"summary":"The key: scopes, locations, limits","responses":{"200":{"description":"Key","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":[]}]}},"/v1/locations":{"get":{"summary":"Locations of the key and their phone systems","responses":{"200":{"description":"Locations","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":[]}]}},"/v1/calls":{"get":{"summary":"Call log, newest first","description":"The phone system keeps the last 400 calls for 90 days per location (`window`).","parameters":[{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."},{"name":"since","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"until","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"direction","in":"query","schema":{"type":"string","enum":["inbound","outbound","internal","transfer"]}},{"name":"status","in":"query","schema":{"type":"string"},"description":"answered | missed | ai | other, or an exact status"},{"name":"number","in":"query","schema":{"type":"string"},"description":"Digits contained in the caller/callee number"},{"name":"extension","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"`nextCursor` of the previous page"}],"responses":{"200":{"description":"Page","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Call"}},"nextCursor":{"type":"string"}}}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["calls:read"]}],"x-scope":"calls:read"}},"/v1/calls/{id}":{"get":{"summary":"One call with its routing trace","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Call id from the call log."},{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"responses":{"200":{"description":"Call","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"call_not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["calls:read"]}],"x-scope":"calls:read"}},"/v1/calls/{id}/recording":{"get":{"summary":"Temporary link to the recording (10 min)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Call id from the call log."},{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"responses":{"200":{"description":"Link","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"},"expiresIn":{"type":"integer"}}}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"recording_not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["recordings:read"]}],"x-scope":"recordings:read"}},"/v1/calls/{id}/transcript":{"get":{"summary":"Transcript and summary of a recorded call","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Call id from the call log."},{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"responses":{"200":{"description":"Transcript","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"recording_not_found | transcript_not_ready","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"transcript_in_progress","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["recordings:read"]}],"x-scope":"recordings:read"}},"/v1/dial":{"post":{"summary":"Click-to-call: ring the extension, then dial the number","description":"The phone of `extension` rings first; after the employee answers, the phone system dials `number` using the outbound rules of the location. Only dedicated phone systems.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["extension","number"],"properties":{"location":{"type":"string"},"extension":{"type":"string"},"number":{"type":"string"}}}}}},"responses":{"200":{"description":"Queued","content":{"application/json":{"schema":{"type":"object","properties":{"commandId":{"type":"string"},"state":{"type":"string"}}}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"extension or phone system not ready","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"number invalid or denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["calls:dial"]}],"x-scope":"calls:dial"}},"/v1/dial/{commandId}":{"get":{"summary":"Progress of a click-to-call","parameters":[{"name":"commandId","in":"path","required":true,"schema":{"type":"string"}},{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"responses":{"200":{"description":"State","content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"string","enum":["queued","sent","ringing","failed"]},"reason":{"type":"string"}}}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["calls:dial"]}],"x-scope":"calls:dial"}},"/v1/extensions":{"get":{"summary":"Extensions, ring groups and rooms","parameters":[{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"responses":{"200":{"description":"Extensions","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["extensions:read"]}],"x-scope":"extensions:read"}},"/v1/extensions/{extension}/forwarding":{"put":{"summary":"Set personal forwarding of an extension","parameters":[{"name":"extension","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9]{2,8}$"}},{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["forward"],"properties":{"forward":{"$ref":"#/components/schemas/Forwarding"}}}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"invalid rule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"no_extension","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["extensions:write"]}],"x-scope":"extensions:write"}},"/v1/extensions/{extension}/dnd":{"put":{"summary":"Turn do-not-disturb on or off","parameters":[{"name":"extension","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9]{2,8}$"}},{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["on"],"properties":{"on":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"no_extension","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"dnd_unsupported","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["extensions:write"]}],"x-scope":"extensions:write"}},"/v1/presence":{"get":{"summary":"Presence of people right now","parameters":[{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"responses":{"200":{"description":"Presence","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["presence:read"]}],"x-scope":"presence:read"}},"/v1/metrics":{"get":{"summary":"Call-center metrics for a period","description":"Service level (threshold, target and short-abandon limit come from the queue settings of the location), average speed of answer, average and longest wait, abandoned calls, AHT, first-call resolution, transfers, bot share, CSAT; where missed calls are lost (`loss`); breakdown by queue, line, employee and direction; comparison with the previous period and the same period last year. Computed from daily summaries of the call history, the same numbers as the admin panel “Analytics” view.","parameters":[{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."},{"name":"from","in":"query","schema":{"type":"string","format":"date"},"description":"First local day (default: 6 days before `to`)"},{"name":"to","in":"query","schema":{"type":"string","format":"date"},"description":"Last local day, inclusive (default: today)"},{"name":"compare","in":"query","schema":{"type":"string"},"description":"`prev`, `year`, `prev,year` or empty"},{"name":"sections","in":"query","schema":{"type":"string"},"description":"metrics,loss,queues,lines,operators,directions,heatmap,daily"},{"name":"callbacks","in":"query","schema":{"type":"boolean"},"description":"Count missed-call callbacks (periods up to 92 days)"}],"responses":{"200":{"description":"Metrics","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"bad_period | period_too_long","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["reports:read"]}],"x-scope":"reports:read"}},"/v1/routes":{"get":{"summary":"Numbers, inbound and outbound rules","parameters":[{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"responses":{"200":{"description":"Routes","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["routes:read"]}],"x-scope":"routes:read"}},"/v1/routes/history":{"get":{"summary":"Change history of inbound or outbound rules","parameters":[{"name":"section","in":"query","required":true,"schema":{"type":"string","enum":["inbound","outbound"]}},{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"responses":{"200":{"description":"Versions","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["routes:read"]}],"x-scope":"routes:read"}},"/v1/routes/history/restore":{"post":{"summary":"Roll inbound or outbound rules back to a version","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["section","versionId"],"properties":{"location":{"type":"string"},"section":{"type":"string","enum":["inbound","outbound"]},"versionId":{"type":"string"}}}}}},"responses":{"200":{"description":"Restored","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"version_not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"the old version no longer fits the current settings","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["routes:write"]}],"x-scope":"routes:write"}},"/v1/routes/inbound":{"post":{"summary":"Create an inbound call rule","parameters":[{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/InboundRule"},{"type":"object","required":["action"],"properties":{"position":{"type":"integer","minimum":1,"description":"Place in the list, 1 = checked first. New rules go last by default."}}}]}}}},"responses":{"200":{"description":"Saved; `warnings: [\"after_catch_all\"]` if an earlier rule catches every call","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"invalid rule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"people_distribution_admin_only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["routes:write"]}],"x-scope":"routes:write"}},"/v1/routes/inbound/{id}":{"put":{"summary":"Change an inbound rule (only the fields passed)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Rule id from GET /v1/routes."},{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/InboundRule"},{"type":"object","properties":{"position":{"type":"integer","minimum":1,"description":"Place in the list, 1 = checked first. New rules go last by default."}}}]}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"rule_not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"rev_conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["routes:write"]}],"x-scope":"routes:write"},"delete":{"summary":"Delete an inbound rule","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Rule id from GET /v1/routes."},{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"rule_not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["routes:write"]}],"x-scope":"routes:write"}},"/v1/routes/inbound/{id}/move":{"post":{"summary":"Move an inbound rule to another place","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Rule id from GET /v1/routes."},{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["position"],"properties":{"position":{"type":"integer","minimum":1,"description":"Place in the list, 1 = checked first. New rules go last by default."}}}}}},"responses":{"200":{"description":"Moved","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"rule_not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["routes:write"]}],"x-scope":"routes:write"}},"/v1/routes/outbound":{"patch":{"summary":"Outside calls on/off and the default line","parameters":[{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"fallback":{"oneOf":[{"$ref":"#/components/schemas/Via"},{"type":"null"}]}}}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["routes:write"]}],"x-scope":"routes:write"}},"/v1/routes/outbound/rules":{"post":{"summary":"Create an outbound rule","parameters":[{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OutboundRule"},{"type":"object","required":["pattern","via"],"properties":{"position":{"type":"integer","minimum":1,"description":"Place in the list, 1 = checked first. New rules go last by default."}}}]}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"invalid rule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["routes:write"]}],"x-scope":"routes:write"}},"/v1/routes/outbound/rules/{id}":{"put":{"summary":"Change an outbound rule (only the fields passed)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Rule id from GET /v1/routes."},{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/OutboundRule"},{"type":"object","properties":{"position":{"type":"integer","minimum":1,"description":"Place in the list, 1 = checked first. New rules go last by default."}}}]}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"rule_not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["routes:write"]}],"x-scope":"routes:write"},"delete":{"summary":"Delete an outbound rule","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Rule id from GET /v1/routes."},{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"rule_not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["routes:write"]}],"x-scope":"routes:write"}},"/v1/routes/outbound/rules/{id}/move":{"post":{"summary":"Move an outbound rule to another place","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Rule id from GET /v1/routes."},{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["position"],"properties":{"position":{"type":"integer","minimum":1,"description":"Place in the list, 1 = checked first. New rules go last by default."}}}}}},"responses":{"200":{"description":"Moved","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"rule_not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["routes:write"]}],"x-scope":"routes:write"}},"/v1/config/export":{"get":{"summary":"Phone system tables in the file export format","parameters":[{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."},{"name":"tables","in":"query","schema":{"type":"string"},"description":"Comma-separated subset: extensions,groups,lines,inbound,outbound"}],"responses":{"200":{"description":"Tables","content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer"},"tables":{"type":"object"}}}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["config:export"]}],"x-scope":"config:export"}},"/v1/config/import":{"post":{"summary":"Load tables: preview, then apply","description":"Without `apply` it is a preview: what will be added / changed / removed and errors per row; nothing is saved. Apply with `apply: true` and `baseRev` = `rev` of the preview. A cell equal to the export means \"no change\"; `sync: {<table>: true}` removes rows missing from the file. Passwords of new extensions are shown only in the admin panel.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tables"],"properties":{"location":{"type":"string"},"tables":{"$ref":"#/components/schemas/ConfigTables"},"sync":{"type":"object"},"apply":{"type":"boolean"},"baseRev":{"type":"integer"}}}}}},"responses":{"200":{"description":"Report","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"rev_conflict — preview again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["config:import"]}],"x-scope":"config:import"}},"/v1/events":{"get":{"summary":"Live call events of the last 7 days (up to 2000 per location)","description":"Oldest first, up to 500 per answer (`more` = there is a next page). Pass `since` = `next` of the previous answer to get only new events or the next page. Events flow while the location has a webhook or a key with `calls:read`.","parameters":[{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."},{"name":"since","in":"query","required":false,"schema":{"type":"string"},"description":"Event id or ISO time"},{"name":"types","in":"query","required":false,"schema":{"type":"string"},"description":"Comma-separated event types"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":500}}],"responses":{"200":{"description":"Events","content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/CallEvent"}},"more":{"type":"boolean"},"next":{"type":"string"}}}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["calls:read"]}],"x-scope":"calls:read"}},"/v1/webhooks":{"get":{"summary":"Webhooks for live call events","responses":{"200":{"description":"Webhooks","content":{"application/json":{"schema":{"type":"object","properties":{"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}},"delivery":{"type":"string","enum":["ready","not_configured"]}}}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["webhooks:write"]}],"x-scope":"webhooks:write"},"post":{"summary":"Create a webhook (the signing secret is returned once)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"},"name":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"locations":{"type":"array","items":{"type":"string"}},"enabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"webhook":{"$ref":"#/components/schemas/Webhook"},"secret":{"type":"string"}}}}}},"400":{"description":"url_https_required / url_private / event_unknown","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"webhooks_limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["webhooks:write"]}],"x-scope":"webhooks:write"}},"/v1/webhooks/{id}":{"patch":{"summary":"Change a webhook (only the fields you pass)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"webhook_not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["webhooks:write"]}],"x-scope":"webhooks:write"},"delete":{"summary":"Delete a webhook","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"webhook_not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["webhooks:write"]}],"x-scope":"webhooks:write"}},"/v1/webhooks/{id}/rotate-secret":{"post":{"summary":"New signing secret (returned once)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Rotated","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["webhooks:write"]}],"x-scope":"webhooks:write"}},"/v1/webhooks/{id}/test":{"post":{"summary":"Send a test event (webhook.ping)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Queued","content":{"application/json":{"schema":{"type":"object","properties":{"queued":{"type":"boolean"},"reason":{"type":"string"}}}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["webhooks:write"]}],"x-scope":"webhooks:write"}},"/v1/webhooks/{id}/deliveries":{"get":{"summary":"Delivery log of a webhook","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deliveries","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["webhooks:write"]}],"x-scope":"webhooks:write"}},"/v1/open-closed":{"get":{"summary":"Closed right now (*28 or until a date)?","parameters":[{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"responses":{"200":{"description":"State","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenClosed"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["routes:read"]}],"x-scope":"routes:read"},"put":{"summary":"Close until the end of the local day or until a date, or open","description":"Same as *28 on a desk phone: rules with a time window stop matching and calls go to the \"after hours\" rules. With `until` the location stays closed until that moment (at most 30 days) and reopens by itself; an ISO time without an offset is read in the location time zone. `closed: false` opens and cancels `until`. Requires \"Close with *28\" in the routing settings (409 `daynight_code_off`); closing until the end of the day also needs an up-to-date phone system (409 `firmware_update_required`).","parameters":[{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"Location id. Optional when the key sees exactly one location."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["closed"],"properties":{"closed":{"type":"boolean"},"until":{"type":"string","description":"ISO date-time (only with closed=true)"}}}}}},"responses":{"200":{"description":"Requested","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenClosed"}}}},"400":{"description":"until_invalid / until_past / until_too_far","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"daynight_code_off / firmware_update_required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":["daynight:write"]}],"x-scope":"daynight:write"}},"/mcp":{"post":{"summary":"MCP server (JSON-RPC 2.0, streamable HTTP, stateless)","description":"Tools mirror the REST operations and are filtered by the key scopes: list_locations, list_calls, get_call, get_call_recording, get_call_transcript, call_number, get_dial_status, list_extensions, set_forwarding, set_do_not_disturb, get_presence, get_call_metrics, list_routes, list_route_history, save_inbound_rule, delete_inbound_rule, move_inbound_rule, save_outbound_rule, delete_outbound_rule, move_outbound_rule, set_outbound_calls, restore_route_version, export_config, import_config, get_call_events, list_webhooks, save_webhook, delete_webhook, rotate_webhook_secret, test_webhook, list_webhook_deliveries, get_open_closed, set_open_closed.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"JSON-RPC response","content":{"application/json":{"schema":{"type":"object"}}}},"202":{"description":"Notification accepted"},"401":{"description":"API key missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the scope (`scope_required`) or the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded; see Retry-After","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{"bearer":[]},{"oauth2":[]}]}}}}