Files
agent-call/docs/contracts/sip-management.openapi.yaml
T

565 lines
18 KiB
YAML

# yaml-language-server: $schema=https://json-schema.org/draft/2020-12/schema
openapi: 3.1.0
info:
title: agent-call Asterisk/SIP Management API
version: 1.0.0
description: >-
Independent Asterisk/SIP management backend. Admin write operations are
separate from the SaaS read-only Trunk directory and from ordinary
scheduling APIs. In mock mode publication records are intents only. In
real mode a publication is successful only after every selected Cell Agent
returns a matching mTLS acknowledgement.
servers:
- url: https://sip-admin.internal
description: Restricted operator management network
- url: https://sip-read.internal
description: SaaS read-only service network
tags:
- name: health
- name: admin-trunks
- name: admin-cells
- name: saas-readonly
paths:
/healthz/live:
get:
tags: [health]
operationId: live
responses:
'200':
description: Service is alive
content:
application/json:
schema:
$ref: '#/components/schemas/Health'
/admin/v1/trunks:
get:
tags: [admin-trunks]
operationId: listAdminTrunks
security: [{SipAdminBearer: []}]
responses:
'200':
description: All Trunks, including unpublished revisions
content:
application/json:
schema:
$ref: '#/components/schemas/AdminTrunkList'
'401': {$ref: '#/components/responses/Unauthorized'}
'403': {$ref: '#/components/responses/Forbidden'}
/admin/v1/trunks/{trunk_id}:
parameters:
- {$ref: '#/components/parameters/TrunkId'}
get:
tags: [admin-trunks]
operationId: getAdminTrunk
security: [{SipAdminBearer: []}]
responses:
'200':
description: Trunk configuration and revisions
content:
application/json:
schema:
$ref: '#/components/schemas/AdminTrunk'
'401': {$ref: '#/components/responses/Unauthorized'}
'404': {$ref: '#/components/responses/NotFound'}
put:
tags: [admin-trunks]
operationId: createTrunkRevision
security: [{SipAdminBearer: []}]
parameters:
- {$ref: '#/components/parameters/IfMatch'}
- {$ref: '#/components/parameters/RequestId'}
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TrunkConfig'
responses:
'200':
description: New draft revision for an existing Trunk
content:
application/json:
schema: {$ref: '#/components/schemas/AdminTrunk'}
'201':
description: New Trunk with its first draft revision
content:
application/json:
schema: {$ref: '#/components/schemas/AdminTrunk'}
'400': {$ref: '#/components/responses/BadRequest'}
'401': {$ref: '#/components/responses/Unauthorized'}
'409': {$ref: '#/components/responses/Conflict'}
/admin/v1/trunks/{trunk_id}/publish:
parameters:
- {$ref: '#/components/parameters/TrunkId'}
post:
tags: [admin-trunks]
operationId: publishTrunk
security: [{SipAdminBearer: []}]
parameters:
- {$ref: '#/components/parameters/IfMatch'}
- {$ref: '#/components/parameters/RequestId'}
responses:
'200':
description: >-
Published revision after all selected Cell acknowledgements
content:
application/json:
schema: {$ref: '#/components/schemas/AdminTrunk'}
'401': {$ref: '#/components/responses/Unauthorized'}
'409': {$ref: '#/components/responses/Conflict'}
/admin/v1/trunks/{trunk_id}/disable:
parameters:
- {$ref: '#/components/parameters/TrunkId'}
post:
tags: [admin-trunks]
operationId: disableTrunk
security: [{SipAdminBearer: []}]
parameters:
- {$ref: '#/components/parameters/IfMatch'}
- {$ref: '#/components/parameters/RequestId'}
responses:
'200':
description: Trunk disabled after selected Cell acknowledgements
content:
application/json:
schema: {$ref: '#/components/schemas/AdminTrunk'}
'401': {$ref: '#/components/responses/Unauthorized'}
'409': {$ref: '#/components/responses/Conflict'}
/admin/v1/trunks/{trunk_id}/rollback:
parameters:
- {$ref: '#/components/parameters/TrunkId'}
post:
tags: [admin-trunks]
operationId: rollbackTrunk
security: [{SipAdminBearer: []}]
parameters:
- {$ref: '#/components/parameters/IfMatch'}
- {$ref: '#/components/parameters/RequestId'}
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
required: [target_revision]
properties:
target_revision: {type: integer, minimum: 1}
responses:
'200':
description: >-
New revision copied from the target after Cell acknowledgements
content:
application/json:
schema: {$ref: '#/components/schemas/AdminTrunk'}
'401': {$ref: '#/components/responses/Unauthorized'}
'409': {$ref: '#/components/responses/Conflict'}
/admin/v1/trunks/{trunk_id}/publications:
parameters:
- {$ref: '#/components/parameters/TrunkId'}
get:
tags: [admin-trunks]
operationId: listTrunkPublications
security: [{SipAdminBearer: []}]
responses:
'200':
description: Per-Cell publication intents
content:
application/json:
schema:
type: object
required: [mode, publications]
properties:
mode: {$ref: '#/components/schemas/Mode'}
publications:
type: array
items: {$ref: '#/components/schemas/Publication'}
'401': {$ref: '#/components/responses/Unauthorized'}
'404': {$ref: '#/components/responses/NotFound'}
/admin/v1/trunks/{trunk_id}/audit:
parameters:
- {$ref: '#/components/parameters/TrunkId'}
get:
tags: [admin-trunks]
operationId: listTrunkAudit
security: [{SipAdminBearer: []}]
responses:
'200':
description: Immutable management audit entries
content:
application/json:
schema:
type: object
required: [mode, audit]
properties:
mode: {$ref: '#/components/schemas/Mode'}
audit:
type: array
items: {$ref: '#/components/schemas/AuditEntry'}
'401': {$ref: '#/components/responses/Unauthorized'}
'404': {$ref: '#/components/responses/NotFound'}
/admin/v1/cells:
get:
tags: [admin-cells]
operationId: listCells
security: [{SipAdminBearer: []}]
responses:
'200':
description: Registered multi-machine voice Cells
content:
application/json:
schema:
type: object
required: [mode, cells]
properties:
mode: {$ref: '#/components/schemas/Mode'}
cells:
type: array
items: {$ref: '#/components/schemas/Cell'}
'401': {$ref: '#/components/responses/Unauthorized'}
/admin/v1/cells/{cell_id}:
parameters:
- {$ref: '#/components/parameters/CellId'}
put:
tags: [admin-cells]
operationId: registerCell
security: [{SipAdminBearer: []}]
parameters:
- {$ref: '#/components/parameters/IfMatch'}
- {$ref: '#/components/parameters/RequestId'}
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/CellConfig'}
responses:
'200':
description: Updated Cell revision
content:
application/json:
schema: {$ref: '#/components/schemas/Cell'}
'201':
description: Registered Cell
content:
application/json:
schema: {$ref: '#/components/schemas/Cell'}
'401': {$ref: '#/components/responses/Unauthorized'}
'409': {$ref: '#/components/responses/Conflict'}
/readonly/v1/sip/trunks:
get:
tags: [saas-readonly]
operationId: listAuthorizedTrunks
security: [{SaasTrunkReadBearer: []}]
responses:
'200':
description: Published Trunks authorized for this SaaS principal
content:
application/json:
schema:
$ref: '#/components/schemas/ReadonlyTrunkList'
'401': {$ref: '#/components/responses/Unauthorized'}
/readonly/v1/sip/trunks/{trunk_id}:
parameters:
- {$ref: '#/components/parameters/TrunkId'}
get:
tags: [saas-readonly]
operationId: getAuthorizedTrunk
security: [{SaasTrunkReadBearer: []}]
responses:
'200':
description: Published, sanitized Trunk metadata
content:
application/json:
schema: {$ref: '#/components/schemas/ReadonlyTrunk'}
'401': {$ref: '#/components/responses/Unauthorized'}
'404': {$ref: '#/components/responses/NotFound'}
components:
securitySchemes:
SipAdminBearer:
type: http
scheme: bearer
bearerFormat: opaque
description: >-
Dedicated operator/backend credential for SIP management writes. It is
not accepted by the SaaS read-only API or ordinary scheduling API.
SaasTrunkReadBearer:
type: http
scheme: bearer
bearerFormat: opaque
description: >-
Dedicated SaaS read-only credential. It cannot publish, modify, disable,
rollback, or access Cell management.
parameters:
TrunkId:
name: trunk_id
in: path
required: true
schema: {type: string, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$'}
CellId:
name: cell_id
in: path
required: true
schema: {type: string, pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$'}
IfMatch:
name: If-Match
in: header
required: true
description: Exact latest revision required for CAS; quotes are accepted.
schema: {type: integer, minimum: 0}
RequestId:
name: X-Request-ID
in: header
required: true
schema: {type: string, minLength: 1, maxLength: 128}
responses:
BadRequest:
description: Invalid configuration or request
content:
application/json:
schema: {$ref: '#/components/schemas/ErrorResponse'}
Unauthorized:
description: Missing or wrong authentication domain
content:
application/json:
schema: {$ref: '#/components/schemas/ErrorResponse'}
Forbidden:
description: Credential lacks the required scope
content:
application/json:
schema: {$ref: '#/components/schemas/ErrorResponse'}
Conflict:
description: CAS conflict or no compatible Cell
content:
application/json:
schema: {$ref: '#/components/schemas/ErrorResponse'}
NotFound:
description: Resource is not visible or does not exist
content:
application/json:
schema: {$ref: '#/components/schemas/ErrorResponse'}
schemas:
Mode:
type: string
enum: [mock, real]
Health:
type: object
additionalProperties: false
required: [status, mode]
properties:
status: {type: string, const: ok}
mode: {$ref: '#/components/schemas/Mode'}
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]
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
writeOnly: true
description: >-
Secret-store reference only; plaintext credentials are 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
pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$'
max_concurrency: {type: integer, minimum: 1}
max_cps: {type: integer, minimum: 1}
CellConfig:
type: object
additionalProperties: false
required: [egress_pool_id, codec_capabilities, status, max_concurrency]
properties:
egress_pool_id:
type: string
pattern: '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$'
codec_capabilities:
type: array
minItems: 1
uniqueItems: true
items: {type: string, enum: [PCMA, PCMU]}
status: {type: string, enum: [healthy, draining, disabled]}
max_concurrency: {type: integer, minimum: 1}
management_url:
type: string
format: uri
pattern: '^https://'
description: >-
mTLS Cell Agent endpoint. Required when mode=real; credentials and
query strings are not allowed.
RevisionInfo:
type: object
additionalProperties: false
required: [revision, state, created_at, created_by]
properties:
revision: {type: integer, minimum: 1}
state: {type: string, enum: [draft, publishing, published, superseded]}
created_at: {type: string, format: date-time}
created_by: {type: string}
AdminTrunk:
type: object
required:
- mode
- trunk_id
- latest_revision
- active_revision
- status
- compatible_cell_ids
- latest
- active
- versions
properties:
mode: {$ref: '#/components/schemas/Mode'}
trunk_id: {type: string}
latest_revision: {type: integer, minimum: 1}
active_revision: {type: integer, minimum: 0}
status: {type: string, enum: [draft, published, disabled]}
updated_at: {type: string, format: date-time}
compatible_cell_ids: {type: array, items: {type: string}}
latest: {$ref: '#/components/schemas/TrunkView'}
active: {$ref: '#/components/schemas/TrunkView'}
versions:
type: array
items:
$ref: '#/components/schemas/RevisionInfo'
TrunkView:
allOf:
- {$ref: '#/components/schemas/TrunkConfig'}
- type: object
properties:
trunk_id: {type: string}
credential_configured: {type: boolean}
asterisk_allow:
type: array
items: {type: string, enum: [alaw, ulaw]}
ReadonlyTrunk:
type: object
required:
- mode
- trunk_id
- revision
- status
- config
properties:
mode: {$ref: '#/components/schemas/Mode'}
trunk_id: {type: string}
revision: {type: integer, minimum: 1}
status: {type: string, const: published}
updated_at: {type: string, format: date-time}
config: {$ref: '#/components/schemas/TrunkView'}
AdminTrunkList:
type: object
required: [mode, trunks]
properties:
mode: {$ref: '#/components/schemas/Mode'}
trunks: {type: array, items: {$ref: '#/components/schemas/AdminTrunk'}}
ReadonlyTrunkList:
type: object
required: [mode, trunks]
properties:
mode: {$ref: '#/components/schemas/Mode'}
trunks:
type: array
items:
$ref: '#/components/schemas/ReadonlyTrunk'
Cell:
type: object
required: [mode, cell_id, revision, config, updated_at, updated_by]
properties:
mode: {$ref: '#/components/schemas/Mode'}
cell_id: {type: string}
revision: {type: integer, minimum: 1}
config: {$ref: '#/components/schemas/CellConfig'}
updated_at: {type: string, format: date-time}
updated_by: {type: string}
Publication:
type: object
required: [trunk_id, revision, cell_id, status, updated_at]
properties:
trunk_id: {type: string}
revision: {type: integer, minimum: 1}
cell_id: {type: string}
status: {type: string, enum: [pending, applied, failed]}
error_code: {type: [string, 'null']}
updated_at: {type: string, format: date-time}
AuditEntry:
type: object
required:
- audit_id
- resource_type
- resource_id
- action
- revision
- actor
- details_json
- created_at
properties:
audit_id: {type: string}
resource_type: {type: string, const: trunk}
resource_id: {type: string}
action:
type: string
enum:
[
upsert,
publish,
publish_failed,
disable,
disable_failed,
rollback,
rollback_failed,
]
revision: {type: integer, minimum: 0}
actor: {type: string}
request_id: {type: [string, 'null']}
details_json: {type: string}
created_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}