Supervision
Generated from
DialerDigital/corecommit6e9f026f0a72— specopenapi/dialer-v1.yamlv0.36.0. Do not edit by hand; see How this reference is built. Download the OpenAPI spec.
Supervisor audio (listen/whisper/barge/takeover), evidence-first.
Endpoints
| Method | Path | Summary |
|---|---|---|
POST | /v1/supervision/calls/{call_id}/listen | Silent monitor (audible to nobody) |
POST | /v1/supervision/calls/{call_id}/whisper | Whisper (audible to the agent leg only) |
POST | /v1/supervision/calls/{call_id}/barge | Barge (three-way, audible to both sides) |
POST | /v1/supervision/calls/{call_id}/takeover | Takeover (debtor transferred to the supervisor) |
Silent monitor (audible to nobody)
POST /v1/supervision/calls/{call_id}/listenOriginates a supervision leg to the supervisor's REGISTERED endpoint running FreeSWITCH eavesdrop on the call. EVIDENCE FIRST: every attempt — success or failure, including probes at unknown uuids — writes a durable supervision_actions row BEFORE any switch command (failures flip it to failed, refusals to refused, never delete it). {call_id} is the live call's FreeSWITCH uuid (= call_attempts.call_uuid). Cross-tenant uuids are indistinguishable from nonexistent and trigger ZERO switch commands. One active mode per supervisor per call: a new mode hangs up the previous leg.
WHO may supervise (ADR #116): supervisor_ext must resolve to an active agent whose role is supervisor or admin; a plain agent is 403role_forbidden (evidenced as refused, ZERO switch commands), for every principal (dd_, ddu_). The role check decides before the call lookup's outcome is acted on (an unknown call is 403, not 404, for an unauthorized caller: no 404-vs-403 oracle). Governed per tenant by the catalog key supervision_role_enforced (default ON; OFF restores "any active extension"). An agent (ddw_) principal is stopped by the default-deny widget allowlist before this operation; behind it, it may name only its own extension (403 forbidden).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
call_id | path | string (uuid) | yes | The live call's FreeSWITCH uuid (= call_attempts.call_uuid, also the call_id of the call.* floor events). |
Request body (JSON, required)
| Field | Type | Required | Description |
|---|---|---|---|
supervisor_ext | string | yes | Must be an ACTIVE roster extension of THIS tenant with role supervisor or admin (ADR |
{
"supervisor_ext": "2000"
}Responses
200
Supervision leg originated (audit row durable).
| Field | Type | Required | Description |
|---|---|---|---|
supervision | object (SupervisionResult) | yes | |
supervision.action | listen · whisper · barge · takeover | no | |
supervision.call_id | string | no | |
supervision.supervisor_ext | string | no | |
supervision.session_id | string | null | no | Supervisor leg uuid; null for takeover (no eavesdrop leg). |
supervision.audit_id | string (uuid) | no | The durable supervision_actions row. |
supervision.to_number | string | no | Masked last-4 (***0184) — the full E.164 never crosses this surface. |
{
"supervision": {
"action": "listen",
"call_id": "6f0a1b2c-3d4e-5f6a-7b8c-9d0e1f2a3b4c",
"supervisor_ext": "2000",
"session_id": "7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d",
"audit_id": "9d0e1f2a-3b4c-5d6e-7f8a-9b0c1d2e3f4a",
"to_number": "***1001"
}
}400 — BadRequest
Malformed request (shape/type errors, invalid filters, bad timestamps).
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}401 — Unauthorized
Missing, unknown or revoked API key (or, on /v1/admin/*, a bad/unset admin token; on webhooks, a bad/missing signature).
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}402 — PlanInactive
The tenant's plan was READ and is not effectively active, so the action is gated (code plan_inactive). On the SUPERVISION surface this status means the plan was actually read: a plan that could NOT be read passes instead of refusing (ADR #37). That is deliberately the opposite of the inbound gate's fail-CLOSED posture — a supervision leg goes to the tenant's own registrar and buys no carrier minutes, so the cost of a false refusal (a compliance officer blinded during a live call) exceeds the cost of a false admission.
Note which verbs can return it: the three EAVESDROP modes only. takeover is exempt and never returns plan_inactive, under the rule "gate what ADDS exposure, never what CORRECTS it" — listen/whisper/barge each open a new leg, while takeover takes the call away and kills the agent leg. A plan lapsing mid-shift must not strand a live call the supervisor is trying to end. The exemption is narrow, not a billing loophole: a dark tenant still cannot open NEW supervision, it only keeps the lever to end properly what is already running.
{
"error": {
"code": "plan_inactive",
"message": "tenant plan is not active; traffic is gated"
}
}403 — SupervisionRoleForbidden
The caller may not supervise with that supervisor_ext (ADR #116, core#928). code role_forbidden: the extension resolves to an active agent whose role is not supervisor or admin. code forbidden: an agent (ddw_) principal named an extension that is not its own. Either way the attempt IS evidenced — a supervision_actions row with result: refused — and ZERO switch commands ran. Per tenant, supervision_role_enforced = false turns the role check off (the identity binding stays).
role_forbidden
{
"error": {
"code": "role_forbidden",
"message": "supervisor_ext must belong to an agent with role supervisor or admin"
}
}forbidden
{
"error": {
"code": "forbidden",
"message": "supervisor_ext must be the agent bound to this token"
}
}404 — NotFound
Unknown id, malformed (non-UUID) id OR another tenant's id — RLS returns zero rows, so all three are indistinguishable by design (no existence leak, never a 403 for foreign ids).
{
"error": {
"code": "not_found",
"message": "resource not found for this tenant"
}
}429 — SupervisionQuotaFull
The tenant's SUPERVISION quota is full (code supervision_capacity): max_supervision_channels concurrent supervision sessions are already open. A SEPARATE ceiling from max_concurrent_channels (ADR #37) and independent in BOTH directions — a saturated dialing floor never refuses supervision, and an exhausted supervision quota never refuses a dial. The unit is the (call, supervisor) session, so switching listen → barge on the same call costs no second slot. Retryable as soon as a supervised call ends. No Retry-After header is set.
{
"error": {
"code": "supervision_capacity",
"message": "tenant supervision quota is full; retry when a supervised call ends"
}
}502 — SupervisionFailed
The switch rejected the supervision command (call already ended / supervisor device not registered).
{
"error": {
"code": "supervision_failed",
"message": "the switch rejected the supervision command"
}
}503 — SupervisionUnavailable
code audit_unavailable: the evidence row could not be written so NO switch command was sent (evidence-first, fail-closed). codeesl_unavailable: switch control link down. Both retryable.
audit_unavailable
{
"error": {
"code": "audit_unavailable",
"message": "supervision audit unavailable, retry"
}
}esl_unavailable
{
"error": {
"code": "esl_unavailable",
"message": "telephony control unavailable, retry"
}
}Whisper (audible to the agent leg only)
POST /v1/supervision/calls/{call_id}/whisperSame contract and evidence-first semantics as listen; the coaching audio reaches only the agent.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
call_id | path | string (uuid) | yes | The live call's FreeSWITCH uuid (= call_attempts.call_uuid, also the call_id of the call.* floor events). |
Request body (JSON, required)
| Field | Type | Required | Description |
|---|---|---|---|
supervisor_ext | string | yes | Must be an ACTIVE roster extension of THIS tenant with role supervisor or admin (ADR |
Responses
200
Supervision leg originated (audit row durable).
| Field | Type | Required | Description |
|---|---|---|---|
supervision | object (SupervisionResult) | yes | |
supervision.action | listen · whisper · barge · takeover | no | |
supervision.call_id | string | no | |
supervision.supervisor_ext | string | no | |
supervision.session_id | string | null | no | Supervisor leg uuid; null for takeover (no eavesdrop leg). |
supervision.audit_id | string (uuid) | no | The durable supervision_actions row. |
supervision.to_number | string | no | Masked last-4 (***0184) — the full E.164 never crosses this surface. |
400 — BadRequest
Malformed request (shape/type errors, invalid filters, bad timestamps).
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}401 — Unauthorized
Missing, unknown or revoked API key (or, on /v1/admin/*, a bad/unset admin token; on webhooks, a bad/missing signature).
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}402 — PlanInactive
The tenant's plan was READ and is not effectively active, so the action is gated (code plan_inactive). On the SUPERVISION surface this status means the plan was actually read: a plan that could NOT be read passes instead of refusing (ADR #37). That is deliberately the opposite of the inbound gate's fail-CLOSED posture — a supervision leg goes to the tenant's own registrar and buys no carrier minutes, so the cost of a false refusal (a compliance officer blinded during a live call) exceeds the cost of a false admission.
Note which verbs can return it: the three EAVESDROP modes only. takeover is exempt and never returns plan_inactive, under the rule "gate what ADDS exposure, never what CORRECTS it" — listen/whisper/barge each open a new leg, while takeover takes the call away and kills the agent leg. A plan lapsing mid-shift must not strand a live call the supervisor is trying to end. The exemption is narrow, not a billing loophole: a dark tenant still cannot open NEW supervision, it only keeps the lever to end properly what is already running.
{
"error": {
"code": "plan_inactive",
"message": "tenant plan is not active; traffic is gated"
}
}403 — SupervisionRoleForbidden
The caller may not supervise with that supervisor_ext (ADR #116, core#928). code role_forbidden: the extension resolves to an active agent whose role is not supervisor or admin. code forbidden: an agent (ddw_) principal named an extension that is not its own. Either way the attempt IS evidenced — a supervision_actions row with result: refused — and ZERO switch commands ran. Per tenant, supervision_role_enforced = false turns the role check off (the identity binding stays).
role_forbidden
{
"error": {
"code": "role_forbidden",
"message": "supervisor_ext must belong to an agent with role supervisor or admin"
}
}forbidden
{
"error": {
"code": "forbidden",
"message": "supervisor_ext must be the agent bound to this token"
}
}404 — NotFound
Unknown id, malformed (non-UUID) id OR another tenant's id — RLS returns zero rows, so all three are indistinguishable by design (no existence leak, never a 403 for foreign ids).
{
"error": {
"code": "not_found",
"message": "resource not found for this tenant"
}
}429 — SupervisionQuotaFull
The tenant's SUPERVISION quota is full (code supervision_capacity): max_supervision_channels concurrent supervision sessions are already open. A SEPARATE ceiling from max_concurrent_channels (ADR #37) and independent in BOTH directions — a saturated dialing floor never refuses supervision, and an exhausted supervision quota never refuses a dial. The unit is the (call, supervisor) session, so switching listen → barge on the same call costs no second slot. Retryable as soon as a supervised call ends. No Retry-After header is set.
{
"error": {
"code": "supervision_capacity",
"message": "tenant supervision quota is full; retry when a supervised call ends"
}
}502 — SupervisionFailed
The switch rejected the supervision command (call already ended / supervisor device not registered).
{
"error": {
"code": "supervision_failed",
"message": "the switch rejected the supervision command"
}
}503 — SupervisionUnavailable
code audit_unavailable: the evidence row could not be written so NO switch command was sent (evidence-first, fail-closed). codeesl_unavailable: switch control link down. Both retryable.
audit_unavailable
{
"error": {
"code": "audit_unavailable",
"message": "supervision audit unavailable, retry"
}
}esl_unavailable
{
"error": {
"code": "esl_unavailable",
"message": "telephony control unavailable, retry"
}
}Barge (three-way, audible to both sides)
POST /v1/supervision/calls/{call_id}/bargeSame contract and evidence-first semantics as listen; the supervisor is audible to agent AND debtor.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
call_id | path | string (uuid) | yes | The live call's FreeSWITCH uuid (= call_attempts.call_uuid, also the call_id of the call.* floor events). |
Request body (JSON, required)
| Field | Type | Required | Description |
|---|---|---|---|
supervisor_ext | string | yes | Must be an ACTIVE roster extension of THIS tenant with role supervisor or admin (ADR |
Responses
200
Supervision leg originated (audit row durable).
| Field | Type | Required | Description |
|---|---|---|---|
supervision | object (SupervisionResult) | yes | |
supervision.action | listen · whisper · barge · takeover | no | |
supervision.call_id | string | no | |
supervision.supervisor_ext | string | no | |
supervision.session_id | string | null | no | Supervisor leg uuid; null for takeover (no eavesdrop leg). |
supervision.audit_id | string (uuid) | no | The durable supervision_actions row. |
supervision.to_number | string | no | Masked last-4 (***0184) — the full E.164 never crosses this surface. |
400 — BadRequest
Malformed request (shape/type errors, invalid filters, bad timestamps).
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}401 — Unauthorized
Missing, unknown or revoked API key (or, on /v1/admin/*, a bad/unset admin token; on webhooks, a bad/missing signature).
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}402 — PlanInactive
The tenant's plan was READ and is not effectively active, so the action is gated (code plan_inactive). On the SUPERVISION surface this status means the plan was actually read: a plan that could NOT be read passes instead of refusing (ADR #37). That is deliberately the opposite of the inbound gate's fail-CLOSED posture — a supervision leg goes to the tenant's own registrar and buys no carrier minutes, so the cost of a false refusal (a compliance officer blinded during a live call) exceeds the cost of a false admission.
Note which verbs can return it: the three EAVESDROP modes only. takeover is exempt and never returns plan_inactive, under the rule "gate what ADDS exposure, never what CORRECTS it" — listen/whisper/barge each open a new leg, while takeover takes the call away and kills the agent leg. A plan lapsing mid-shift must not strand a live call the supervisor is trying to end. The exemption is narrow, not a billing loophole: a dark tenant still cannot open NEW supervision, it only keeps the lever to end properly what is already running.
{
"error": {
"code": "plan_inactive",
"message": "tenant plan is not active; traffic is gated"
}
}403 — SupervisionRoleForbidden
The caller may not supervise with that supervisor_ext (ADR #116, core#928). code role_forbidden: the extension resolves to an active agent whose role is not supervisor or admin. code forbidden: an agent (ddw_) principal named an extension that is not its own. Either way the attempt IS evidenced — a supervision_actions row with result: refused — and ZERO switch commands ran. Per tenant, supervision_role_enforced = false turns the role check off (the identity binding stays).
role_forbidden
{
"error": {
"code": "role_forbidden",
"message": "supervisor_ext must belong to an agent with role supervisor or admin"
}
}forbidden
{
"error": {
"code": "forbidden",
"message": "supervisor_ext must be the agent bound to this token"
}
}404 — NotFound
Unknown id, malformed (non-UUID) id OR another tenant's id — RLS returns zero rows, so all three are indistinguishable by design (no existence leak, never a 403 for foreign ids).
{
"error": {
"code": "not_found",
"message": "resource not found for this tenant"
}
}429 — SupervisionQuotaFull
The tenant's SUPERVISION quota is full (code supervision_capacity): max_supervision_channels concurrent supervision sessions are already open. A SEPARATE ceiling from max_concurrent_channels (ADR #37) and independent in BOTH directions — a saturated dialing floor never refuses supervision, and an exhausted supervision quota never refuses a dial. The unit is the (call, supervisor) session, so switching listen → barge on the same call costs no second slot. Retryable as soon as a supervised call ends. No Retry-After header is set.
{
"error": {
"code": "supervision_capacity",
"message": "tenant supervision quota is full; retry when a supervised call ends"
}
}502 — SupervisionFailed
The switch rejected the supervision command (call already ended / supervisor device not registered).
{
"error": {
"code": "supervision_failed",
"message": "the switch rejected the supervision command"
}
}503 — SupervisionUnavailable
code audit_unavailable: the evidence row could not be written so NO switch command was sent (evidence-first, fail-closed). codeesl_unavailable: switch control link down. Both retryable.
audit_unavailable
{
"error": {
"code": "audit_unavailable",
"message": "supervision audit unavailable, retry"
}
}esl_unavailable
{
"error": {
"code": "esl_unavailable",
"message": "telephony control unavailable, retry"
}
}Takeover (debtor transferred to the supervisor)
POST /v1/supervision/calls/{call_id}/takeoverTransfers the debtor leg to the supervisor and hangs up the orphaned agent leg. session_id is null — there is no eavesdrop leg. Same evidence-first audit contract as the other modes.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
call_id | path | string (uuid) | yes | The live call's FreeSWITCH uuid (= call_attempts.call_uuid, also the call_id of the call.* floor events). |
Request body (JSON, required)
| Field | Type | Required | Description |
|---|---|---|---|
supervisor_ext | string | yes | Must be an ACTIVE roster extension of THIS tenant with role supervisor or admin (ADR |
Responses
200
Debtor leg transferred (audit row durable).
| Field | Type | Required | Description |
|---|---|---|---|
supervision | object (SupervisionResult) | yes | |
supervision.action | listen · whisper · barge · takeover | no | |
supervision.call_id | string | no | |
supervision.supervisor_ext | string | no | |
supervision.session_id | string | null | no | Supervisor leg uuid; null for takeover (no eavesdrop leg). |
supervision.audit_id | string (uuid) | no | The durable supervision_actions row. |
supervision.to_number | string | no | Masked last-4 (***0184) — the full E.164 never crosses this surface. |
400 — BadRequest
Malformed request (shape/type errors, invalid filters, bad timestamps).
{
"error": {
"code": "bad_request",
"message": "from must be RFC-3339"
}
}401 — Unauthorized
Missing, unknown or revoked API key (or, on /v1/admin/*, a bad/unset admin token; on webhooks, a bad/missing signature).
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403 — SupervisionRoleForbidden
The caller may not supervise with that supervisor_ext (ADR #116, core#928). code role_forbidden: the extension resolves to an active agent whose role is not supervisor or admin. code forbidden: an agent (ddw_) principal named an extension that is not its own. Either way the attempt IS evidenced — a supervision_actions row with result: refused — and ZERO switch commands ran. Per tenant, supervision_role_enforced = false turns the role check off (the identity binding stays).
role_forbidden
{
"error": {
"code": "role_forbidden",
"message": "supervisor_ext must belong to an agent with role supervisor or admin"
}
}forbidden
{
"error": {
"code": "forbidden",
"message": "supervisor_ext must be the agent bound to this token"
}
}404 — NotFound
Unknown id, malformed (non-UUID) id OR another tenant's id — RLS returns zero rows, so all three are indistinguishable by design (no existence leak, never a 403 for foreign ids).
{
"error": {
"code": "not_found",
"message": "resource not found for this tenant"
}
}502 — SupervisionFailed
The switch rejected the supervision command (call already ended / supervisor device not registered).
{
"error": {
"code": "supervision_failed",
"message": "the switch rejected the supervision command"
}
}503 — SupervisionUnavailable
code audit_unavailable: the evidence row could not be written so NO switch command was sent (evidence-first, fail-closed). codeesl_unavailable: switch control link down. Both retryable.
audit_unavailable
{
"error": {
"code": "audit_unavailable",
"message": "supervision audit unavailable, retry"
}
}esl_unavailable
{
"error": {
"code": "esl_unavailable",
"message": "telephony control unavailable, retry"
}
}