565 lines
18 KiB
YAML
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}
|