# yaml-language-server: $schema=https://json-schema.org/draft/2020-12/schema openapi: 3.1.0 info: title: agent-call Cell Agent API version: 1.0.0 description: >- Restricted mTLS API used by the SIP management backend to deliver a versioned Trunk snapshot to one voice Cell. The Cell validates the SHA-256 snapshot, applies it with an atomic file replacement, reloads Asterisk, restores the previous file on reload failure, and returns applied only after the reload succeeds. A disabled snapshot removes the Cell-local Trunk fragment and reloads Asterisk. servers: - url: https://cell.internal:9443 description: Cell management network only tags: - name: health - name: trunk-apply paths: /healthz/live: get: tags: [health] operationId: live responses: '200': description: Cell Agent is alive content: application/json: schema: {$ref: '#/components/schemas/Health'} /v1/sip/trunks/{trunk_id}/apply: parameters: - {$ref: '#/components/parameters/TrunkId'} post: tags: [trunk-apply] operationId: applyTrunk security: [{CellManagementMtls: []}] parameters: - {$ref: '#/components/parameters/RequestId'} requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/Publication'} responses: '200': description: Asterisk has loaded the exact snapshot content: application/json: schema: {$ref: '#/components/schemas/Acknowledgement'} '400': {$ref: '#/components/responses/BadRequest'} '403': {$ref: '#/components/responses/Forbidden'} '409': description: Revision is stale or has a gap content: application/json: schema: {$ref: '#/components/schemas/ErrorResponse'} '502': description: Asterisk rejected the apply or reload content: application/json: schema: {$ref: '#/components/schemas/ErrorResponse'} /v1/sip/trunks/{trunk_id}/state: parameters: - {$ref: '#/components/parameters/TrunkId'} get: tags: [trunk-apply] operationId: getTrunkState security: [{CellManagementMtls: []}] responses: '200': description: Durable Cell apply state content: application/json: schema: {$ref: '#/components/schemas/State'} '404': {$ref: '#/components/responses/NotFound'} components: securitySchemes: CellManagementMtls: type: mutualTLS description: Management backend client certificate signed by the Cell CA. parameters: TrunkId: name: trunk_id in: path required: true schema: {type: string, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$'} RequestId: name: X-Request-ID in: header required: true schema: {type: string, minLength: 1, maxLength: 128} schemas: Health: type: object additionalProperties: false required: [status, mode, cell_id] properties: status: {type: string, const: ok} mode: {type: string, const: real} cell_id: {type: string} CodecProfile: type: object additionalProperties: false required: [allowed, preferred] properties: allowed: type: array minItems: 1 uniqueItems: true items: {type: string, enum: [PCMA, PCMU]} preferred: {type: string, enum: [PCMA, PCMU]} SipConfig: type: object additionalProperties: false required: [host, port, transport, auth_mode, register, credential_ref] properties: host: {type: string, minLength: 1, maxLength: 253} port: {type: integer, minimum: 1, maximum: 65535} transport: {type: string, enum: [udp, tcp, tls]} auth_mode: {type: string, enum: [ip, digest]} register: {type: boolean} credential_ref: type: [string, 'null'] description: Secret-store reference only; plaintext is forbidden. TrunkConfig: type: object additionalProperties: false required: - display_name - enabled - sip - codec_profile - caller_ids - dial_prefix - egress_pool_id - max_concurrency - max_cps properties: display_name: {type: string, minLength: 1, maxLength: 256} enabled: {type: boolean} sip: {$ref: '#/components/schemas/SipConfig'} codec_profile: {$ref: '#/components/schemas/CodecProfile'} caller_ids: type: array minItems: 1 uniqueItems: true items: {type: string, minLength: 1, maxLength: 128} dial_prefix: {type: string, maxLength: 32} egress_pool_id: {type: string} max_concurrency: {type: integer, minimum: 1} max_cps: {type: integer, minimum: 1} Publication: type: object additionalProperties: false required: [mode, cell_id, trunk_id, revision, config, config_sha256] properties: mode: {type: string, const: real} cell_id: {type: string} trunk_id: {type: string} revision: {type: integer, minimum: 1} config: {$ref: '#/components/schemas/TrunkConfig'} config_sha256: type: string pattern: '^[0-9a-f]{64}$' Acknowledgement: type: object additionalProperties: false required: [mode, cell_id, trunk_id, revision, config_sha256, status, idempotent] properties: mode: {type: string, const: real} cell_id: {type: string} trunk_id: {type: string} revision: {type: integer, minimum: 1} config_sha256: {type: string, pattern: '^[0-9a-f]{64}$'} status: {type: string, const: applied} idempotent: {type: boolean} State: type: object additionalProperties: false required: [ mode, cell_id, trunk_id, desired_revision, applied_revision, status, updated_at, ] properties: mode: {type: string, const: real} cell_id: {type: string} trunk_id: {type: string} desired_revision: {type: integer, minimum: 1} applied_revision: {type: integer, minimum: 0} status: {type: string, enum: [applying, applied, failed]} last_error: {type: [string, 'null']} updated_at: {type: string, format: date-time} ErrorResponse: type: object required: [error] properties: error: type: object required: [code, message] properties: code: {type: string} message: {type: string} responses: BadRequest: description: Invalid publication or hash content: application/json: schema: {$ref: '#/components/schemas/ErrorResponse'} Forbidden: description: Certificate or Cell identity is not authorized content: application/json: schema: {$ref: '#/components/schemas/ErrorResponse'} NotFound: description: State does not exist content: application/json: schema: {$ref: '#/components/schemas/ErrorResponse'}