Skip to content

Supervision ​

Generated from DialerDigital/core commit 0fc18008b3c8 — spec openapi/dialer-v1.yaml v0.134.0. Do not edit by hand; see How this reference is built. Download the OpenAPI spec.

Español

Supervisor audio (listen/whisper/barge/takeover), evidence-first.

Endpoints ​

MethodPathSummary
POST/v1/supervision/calls/{call_id}/listenSilent monitor (audible to nobody)
POST/v1/supervision/calls/{call_id}/whisperWhisper (audible to the agent leg only)
POST/v1/supervision/calls/{call_id}/bargeBarge (three-way, audible to both sides)
POST/v1/supervision/calls/{call_id}/takeoverTakeover (debtor transferred to the supervisor)

Silent monitor (audible to nobody) ​

http
POST /v1/supervision/calls/{call_id}/listen

Originates 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).

WHO may act AS that extension (ADR #135, core#1037 phase 3): the agent behind supervisor_ext is the agent the body names and is bound by the same principal rule as every agent_id verb, decided BEFORE the role check and evidenced the same way. A ddu_ login linked to a seat may name only its own seat (403 forbidden, no flag); a ddu_ login without a seat may name a third party only as account_admin — a member is 403role_forbidden once the tenant's ddu_author_role_enforced is on (born off). A dd_ key names anyone. The same gate applies to whisper, barge, takeover and POST /v1/ai/conversations/{id}/takeover.

Parameters

NameInTypeRequiredDescription
call_idpathstring (uuid)yesThe live call's FreeSWITCH uuid (= call_attempts.call_uuid, also the call_id of the call.* floor events).

Request body (JSON, required)

FieldTypeRequiredDescription
supervisor_extstringyesMust be an ACTIVE roster extension of THIS tenant with role supervisor or admin (ADR #116; 403 role_forbidden otherwise), and one the principal may act as (ADR #135; a seated ddu_ only its own seat, 403 forbidden); their checked-in device receives the supervision leg (answer within 20 s).
json
{
  "supervisor_ext": "2000"
}

Responses

200 ​

Supervision leg originated (audit row durable).

FieldTypeRequiredDescription
supervisionobject (SupervisionResult)yes
supervision.actionlisten · whisper · barge · takeoverno
supervision.call_idstringno
supervision.supervisor_extstringno
supervision.session_idstring | nullnoSupervisor leg uuid; null for takeover (no eavesdrop leg).
supervision.audit_idstring (uuid)noThe durable supervision_actions row.
supervision.to_numberstringnoMasked last-4 (***0184) — the full E.164 never crosses this surface.
json
{
  "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).

json
{
  "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). A dashboard session (ddu_) that a newer login of the same person ended answers the code session_superseded instead (tenant flag single_session_enforced), so the screen can say why it was signed out; a logout, an expired or an unknown token stay unauthorized.

json
{
  "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.

json
{
  "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; ADR #135, core#1037 phase 3). code role_forbidden: the extension resolves to an active agent whose role is not supervisor or admin — or a ddu_ login without a linked seat named somebody else's extension without the account_admin role (once ddu_author_role_enforced is on). codeforbidden: a principal bound to ONE agent — a ddw_ token, or a ddu_ login linked to a seat — 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 extension's role check off (the identity binding and the principal rule stay).

role_forbidden

json
{
  "error": {
    "code": "role_forbidden",
    "message": "supervisor_ext must belong to an agent with role supervisor or admin"
  }
}

forbidden

json
{
  "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).

json
{
  "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.

json
{
  "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).

json
{
  "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

json
{
  "error": {
    "code": "audit_unavailable",
    "message": "supervision audit unavailable, retry"
  }
}

esl_unavailable

json
{
  "error": {
    "code": "esl_unavailable",
    "message": "telephony control unavailable, retry"
  }
}

Whisper (audible to the agent leg only) ​

http
POST /v1/supervision/calls/{call_id}/whisper

Same contract and evidence-first semantics as listen; the coaching audio reaches only the agent.

Parameters

NameInTypeRequiredDescription
call_idpathstring (uuid)yesThe live call's FreeSWITCH uuid (= call_attempts.call_uuid, also the call_id of the call.* floor events).

Request body (JSON, required)

FieldTypeRequiredDescription
supervisor_extstringyesMust be an ACTIVE roster extension of THIS tenant with role supervisor or admin (ADR #116; 403 role_forbidden otherwise), and one the principal may act as (ADR #135; a seated ddu_ only its own seat, 403 forbidden); their checked-in device receives the supervision leg (answer within 20 s).

Responses

200 ​

Supervision leg originated (audit row durable).

FieldTypeRequiredDescription
supervisionobject (SupervisionResult)yes
supervision.actionlisten · whisper · barge · takeoverno
supervision.call_idstringno
supervision.supervisor_extstringno
supervision.session_idstring | nullnoSupervisor leg uuid; null for takeover (no eavesdrop leg).
supervision.audit_idstring (uuid)noThe durable supervision_actions row.
supervision.to_numberstringnoMasked last-4 (***0184) — the full E.164 never crosses this surface.

400 — BadRequest ​

Malformed request (shape/type errors, invalid filters, bad timestamps).

json
{
  "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). A dashboard session (ddu_) that a newer login of the same person ended answers the code session_superseded instead (tenant flag single_session_enforced), so the screen can say why it was signed out; a logout, an expired or an unknown token stay unauthorized.

json
{
  "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.

json
{
  "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; ADR #135, core#1037 phase 3). code role_forbidden: the extension resolves to an active agent whose role is not supervisor or admin — or a ddu_ login without a linked seat named somebody else's extension without the account_admin role (once ddu_author_role_enforced is on). codeforbidden: a principal bound to ONE agent — a ddw_ token, or a ddu_ login linked to a seat — 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 extension's role check off (the identity binding and the principal rule stay).

role_forbidden

json
{
  "error": {
    "code": "role_forbidden",
    "message": "supervisor_ext must belong to an agent with role supervisor or admin"
  }
}

forbidden

json
{
  "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).

json
{
  "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.

json
{
  "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).

json
{
  "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

json
{
  "error": {
    "code": "audit_unavailable",
    "message": "supervision audit unavailable, retry"
  }
}

esl_unavailable

json
{
  "error": {
    "code": "esl_unavailable",
    "message": "telephony control unavailable, retry"
  }
}

Barge (three-way, audible to both sides) ​

http
POST /v1/supervision/calls/{call_id}/barge

Same contract and evidence-first semantics as listen; the supervisor is audible to agent AND debtor.

Parameters

NameInTypeRequiredDescription
call_idpathstring (uuid)yesThe live call's FreeSWITCH uuid (= call_attempts.call_uuid, also the call_id of the call.* floor events).

Request body (JSON, required)

FieldTypeRequiredDescription
supervisor_extstringyesMust be an ACTIVE roster extension of THIS tenant with role supervisor or admin (ADR #116; 403 role_forbidden otherwise), and one the principal may act as (ADR #135; a seated ddu_ only its own seat, 403 forbidden); their checked-in device receives the supervision leg (answer within 20 s).

Responses

200 ​

Supervision leg originated (audit row durable).

FieldTypeRequiredDescription
supervisionobject (SupervisionResult)yes
supervision.actionlisten · whisper · barge · takeoverno
supervision.call_idstringno
supervision.supervisor_extstringno
supervision.session_idstring | nullnoSupervisor leg uuid; null for takeover (no eavesdrop leg).
supervision.audit_idstring (uuid)noThe durable supervision_actions row.
supervision.to_numberstringnoMasked last-4 (***0184) — the full E.164 never crosses this surface.

400 — BadRequest ​

Malformed request (shape/type errors, invalid filters, bad timestamps).

json
{
  "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). A dashboard session (ddu_) that a newer login of the same person ended answers the code session_superseded instead (tenant flag single_session_enforced), so the screen can say why it was signed out; a logout, an expired or an unknown token stay unauthorized.

json
{
  "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.

json
{
  "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; ADR #135, core#1037 phase 3). code role_forbidden: the extension resolves to an active agent whose role is not supervisor or admin — or a ddu_ login without a linked seat named somebody else's extension without the account_admin role (once ddu_author_role_enforced is on). codeforbidden: a principal bound to ONE agent — a ddw_ token, or a ddu_ login linked to a seat — 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 extension's role check off (the identity binding and the principal rule stay).

role_forbidden

json
{
  "error": {
    "code": "role_forbidden",
    "message": "supervisor_ext must belong to an agent with role supervisor or admin"
  }
}

forbidden

json
{
  "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).

json
{
  "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.

json
{
  "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).

json
{
  "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

json
{
  "error": {
    "code": "audit_unavailable",
    "message": "supervision audit unavailable, retry"
  }
}

esl_unavailable

json
{
  "error": {
    "code": "esl_unavailable",
    "message": "telephony control unavailable, retry"
  }
}

Takeover (debtor transferred to the supervisor) ​

http
POST /v1/supervision/calls/{call_id}/takeover

Transfers 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

NameInTypeRequiredDescription
call_idpathstring (uuid)yesThe live call's FreeSWITCH uuid (= call_attempts.call_uuid, also the call_id of the call.* floor events).

Request body (JSON, required)

FieldTypeRequiredDescription
supervisor_extstringyesMust be an ACTIVE roster extension of THIS tenant with role supervisor or admin (ADR #116; 403 role_forbidden otherwise), and one the principal may act as (ADR #135; a seated ddu_ only its own seat, 403 forbidden); their checked-in device receives the supervision leg (answer within 20 s).

Responses

200 ​

Debtor leg transferred (audit row durable).

FieldTypeRequiredDescription
supervisionobject (SupervisionResult)yes
supervision.actionlisten · whisper · barge · takeoverno
supervision.call_idstringno
supervision.supervisor_extstringno
supervision.session_idstring | nullnoSupervisor leg uuid; null for takeover (no eavesdrop leg).
supervision.audit_idstring (uuid)noThe durable supervision_actions row.
supervision.to_numberstringnoMasked last-4 (***0184) — the full E.164 never crosses this surface.

400 — BadRequest ​

Malformed request (shape/type errors, invalid filters, bad timestamps).

json
{
  "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). A dashboard session (ddu_) that a newer login of the same person ended answers the code session_superseded instead (tenant flag single_session_enforced), so the screen can say why it was signed out; a logout, an expired or an unknown token stay unauthorized.

json
{
  "error": {
    "code": "unauthorized",
    "message": "invalid or revoked API key"
  }
}

403 — SupervisionRoleForbidden ​

The caller may not supervise with that supervisor_ext (ADR #116, core#928; ADR #135, core#1037 phase 3). code role_forbidden: the extension resolves to an active agent whose role is not supervisor or admin — or a ddu_ login without a linked seat named somebody else's extension without the account_admin role (once ddu_author_role_enforced is on). codeforbidden: a principal bound to ONE agent — a ddw_ token, or a ddu_ login linked to a seat — 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 extension's role check off (the identity binding and the principal rule stay).

role_forbidden

json
{
  "error": {
    "code": "role_forbidden",
    "message": "supervisor_ext must belong to an agent with role supervisor or admin"
  }
}

forbidden

json
{
  "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).

json
{
  "error": {
    "code": "not_found",
    "message": "resource not found for this tenant"
  }
}

429 — ApiKeyRateLimited ​

This API key is over its request quota, or the tenant's keys together are over the tenant ceiling (core#2503) (code rate_limited). The request was not processed. Wait Retry-After seconds and retry; a refusal is not counted against the budget, so that wait is always enough. Bounded per API key and, above the keys, per tenant (api_tenant_requests_per_minute): a sibling key is unaffected until the tenant's keys together reach that ceiling. The quota applies to every operation reachable with a dd_ key, so the operations that name a more specific 429 (call control, dispositions, SMS, …) can ALSO answer this one with its X-RateLimit-* headers.

json
{
  "error": {
    "code": "rate_limited",
    "message": "API key request quota exceeded; retry after the interval in Retry-After"
  }
}

502 — SupervisionFailed ​

The switch rejected the supervision command (call already ended / supervisor device not registered).

json
{
  "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

json
{
  "error": {
    "code": "audit_unavailable",
    "message": "supervision audit unavailable, retry"
  }
}

esl_unavailable

json
{
  "error": {
    "code": "esl_unavailable",
    "message": "telephony control unavailable, retry"
  }
}

Nothing in these docs is legal advice — always confirm compliance posture with your own counsel.