{"openapi":"3.1.0","info":{"title":"Bolrach Voice API","version":"1.0.0","description":"The cloud PBX (FreeSWITCH + voip.ms) behind one key: numbers, health and click-to-call. Calls are placed on behalf of the extension's assigned owner, so the PBX's own calling permissions gate every dial-out.","contact":{"url":"https://docs.bolrach.com/voice"}},"servers":[{"url":"https://api.bolrach.com"}],"security":[{"bearerKey":[]},{"headerKey":[]}],"paths":{"/v1/voice/dids":{"get":{"operationId":"voiceDids","summary":"The platform's phone numbers","responses":{"200":{"description":"DIDs with label and assignment state.","content":{"application/json":{"schema":{"type":"object","properties":{"dids":{"type":"array","items":{"type":"object","properties":{"number":{"type":"string"},"label":{"type":["string","null"]},"assigned":{"type":"boolean"},"state":{"type":["string","null"]}}}}}}}}},"400":{"description":"Bad input, unknown or unassigned extension.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"integer"}},"required":["code","message","status"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"integer"}},"required":["code","message","status"]}},"required":["error"]}}}},"403":{"description":"Key lacks the `voice:read` scope.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"integer"}},"required":["code","message","status"]}},"required":["error"]}}}},"429":{"description":"Rate limit or daily quota exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"integer"}},"required":["code","message","status"]}},"required":["error"]}}}},"502":{"description":"The PBX did not answer (`upstream_error`).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"integer"}},"required":["code","message","status"]}},"required":["error"]}}}}}}},"/v1/voice/status":{"get":{"operationId":"voiceStatus","summary":"PBX health","responses":{"200":{"description":"Event-socket and provider lanes plus object counts.","content":{"application/json":{"schema":{"type":"object","properties":{"esl":{"type":"object","properties":{"dormant":{"type":"boolean"}}},"voipms":{"type":"object","properties":{"dormant":{"type":"boolean"}}},"counts":{"type":"object","properties":{"extensions":{"type":"integer"},"dids":{"type":"integer"},"queues":{"type":"integer"}}}}}}}},"400":{"description":"Bad input, unknown or unassigned extension.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"integer"}},"required":["code","message","status"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"integer"}},"required":["code","message","status"]}},"required":["error"]}}}},"403":{"description":"Key lacks the `voice:read` scope.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"integer"}},"required":["code","message","status"]}},"required":["error"]}}}},"429":{"description":"Rate limit or daily quota exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"integer"}},"required":["code","message","status"]}},"required":["error"]}}}},"502":{"description":"The PBX did not answer (`upstream_error`).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"integer"}},"required":["code","message","status"]}},"required":["error"]}}}}}}},"/v1/voice/click-to-call":{"post":{"operationId":"voiceClickToCall","summary":"Ring an extension, then dial out","description":"Classic click-to-call: the PBX rings `fromExtension` first, then bridges to `toNumber`. Requires `voice:call` (high-trust — calls cost real money), and the extension's owner must hold the PBX's calling permission.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fromExtension":{"type":"string","description":"The PBX extension that rings first."},"toNumber":{"type":"string","description":"Destination number."},"context":{"type":"string","maxLength":80,"description":"Free-form tag stamped on the call log."}},"required":["fromExtension","toNumber"]},"example":{"fromExtension":"1001","toNumber":"+15872001234","context":"crm-lead-842"}}}},"responses":{"200":{"description":"The call attempt with its log id and dialing state."},"400":{"description":"Bad input, unknown or unassigned extension.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"integer"}},"required":["code","message","status"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"integer"}},"required":["code","message","status"]}},"required":["error"]}}}},"403":{"description":"Key lacks the `voice:call` scope.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"integer"}},"required":["code","message","status"]}},"required":["error"]}}}},"429":{"description":"Rate limit or daily quota exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"integer"}},"required":["code","message","status"]}},"required":["error"]}}}},"502":{"description":"The PBX did not answer (`upstream_error`).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"integer"}},"required":["code","message","status"]}},"required":["error"]}}}}}}}},"components":{"securitySchemes":{"bearerKey":{"type":"http","scheme":"bearer","description":"Authorization: Bearer bt_..."},"headerKey":{"type":"apiKey","in":"header","name":"x-api-key"}}}}