Skip to content

Inbound ivr menus ​

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

The tenant's inbound IVR menus: published versions, append-only history, rendered to audio when published. An inbound route with dest_type: ivr runs one of them.

Endpoints ​

MethodPathSummary
GET/v1/inbound-ivr-menusThe tenant's current inbound IVR menus
POST/v1/inbound-ivr-menusPublish an inbound IVR menu, or the next version of one
GET/v1/inbound-ivr-menus/{id}One inbound IVR menu, its current version and its history
POST/v1/inbound-ivr-menus/{id}/retireRetire an inbound IVR menu

The tenant's current inbound IVR menus ​

http
GET /v1/inbound-ivr-menus

Every menu that is not retired, in its current (highest) version, ordered by name. A retired menu is not listed; GET /v1/inbound-ivr-menus/{id} still answers it. Readable by a dd_ key and by any dashboard login; a widget token (ddw_) is 403 forbidden.

An inbound route with dest_type: ivr names one of these menus in its dest_ref; each call to it hears the version current when it connects (core#1319).

Responses

200 ​

The current menus.

FieldTypeRequiredDescription
inbound_ivr_menusarray of InboundIvrMenuyes
inbound_ivr_menus[].idstring (uuid)yesThe menu; stable across its versions.
inbound_ivr_menus[].statuspublished · retiredyes
inbound_ivr_menus[].versionintegeryesThe current version, or the one the menu was retired at.
inbound_ivr_menus[].namestringyes
inbound_ivr_menus[].menuobject (InboundIvrMenuNode)yes
inbound_ivr_menus[].menu.promptstringyesWhat the caller hears on entering the node, in the tenant's words (at most 2000 bytes; blank is refused). Rendered to audio when the menu is published.
inbound_ivr_menus[].menu.defaultanyyesMANDATORY. With no key pressed the prompt plays again and, the second time, this action runs — no menu is a dead end. Never optout: pressing nothing is not asking to leave the list.
inbound_ivr_menus[].menu.entriesobjectyesOne DTMF key (0-9, *, #) → a terminal action or a submenu.
inbound_ivr_menus[].published_atstring (date-time)yesWhen this version was published.
inbound_ivr_menus[].published_bystringyesThe user or agent id of the credential that published this version, or tenant-api-key.
inbound_ivr_menus[].retired_atstring,null (date-time)yes
inbound_ivr_menus[].retired_bystring,nullyes

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 — Forbidden ​

The tenant is suspended. A dashboard session (ddu_) of a tenant whose login policy (ADR 227) is in mode enforce answers 403 with the code login_policy_denied on a request from an address or at an hour the policy does not allow — the same body for both; the session is not revoked and a later request inside the policy goes through. A widget token (ddw_) of a seat is held to the same policy on every request (core#2406, flag widget_login_honors_policy). A synthetic (demo) tenant answers 403 with the code synthetic_tenant on every route that would reach a third party — manual dial, external transfer or conference leg, SMS send (core#2251, ADR #280 addendum). No payment changes it.

json
{
  "error": {
    "code": "forbidden",
    "message": "tenant is suspended"
  }
}

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"
  }
}

Publish an inbound IVR menu, or the next version of one ​

http
POST /v1/inbound-ivr-menus

Publishes menu under name. Without menu_id it is a new menu, version 1, whose id is the menu's from then on; with menu_id it is the next version of that menu, which becomes the current one. Nothing is ever edited in place: the history keeps every version.

Publishing, in order, and nothing is written unless all pass:

  1. the tree is validated (InboundIvrMenuNode): every node has a prompt and a default action, keys are single DTMF keys, at most 5 levels and 64 nodes;
  2. every queue:<id> must be an ACTIVE (not retired) inbound queue of this tenant (/v1/inbound-queues) and every agent:<id> an ACTIVE agent of this tenant, else 422 naming them (another tenant's, a non-existent one and a retired queue read the same);
  3. every prompt is rendered to audio NOW, not when a call arrives. If one does not render the menu is not published: 503 prompt_render_failed, and the same request may be retried.

The prompts are the tenant's words: the platform adds no text of its own. A menu_id that is not a menu of this tenant is 404; a retired one is 409 conflict (a retired menu stays retired — publish a new one).

Tenant management only: a dd_ key or an account_admin login. A member login is 403 role_forbidden; a widget token (ddw_) is 403 forbidden. Who published is taken from the credential.

Request body (JSON, required)

FieldTypeRequiredDescription
namestringyesThe menu's name, for the operator (trimmed; blank is refused).
menuobject (InboundIvrMenuNode)yes
menu.promptstringyesWhat the caller hears on entering the node, in the tenant's words (at most 2000 bytes; blank is refused). Rendered to audio when the menu is published.
menu.defaultanyyesMANDATORY. With no key pressed the prompt plays again and, the second time, this action runs — no menu is a dead end. Never optout: pressing nothing is not asking to leave the list.
menu.entriesobjectyesOne DTMF key (0-9, *, #) → a terminal action or a submenu.
menu_idstring (uuid)noThe menu to publish the next version of. Absent ⇒ a new menu.
json
{
  "name": "Main line",
  "menu": {
    "prompt": "Thanks for calling. For sales press 1. For billing press 2. To stop receiving our calls press 9.",
    "default": "queue:6f1c1b7e-2d7a-4c1e-9d3a-0b5e8f7a1c21",
    "entries": {
      "1": {
        "action": "agent:0d9e2b4a-8c3f-4e6b-a1d2-3c4b5a6f7e80"
      },
      "2": {
        "submenu": {
          "prompt": "For a copy of your invoice press 1.",
          "default": "hangup",
          "entries": {
            "1": {
              "action": "queue:6f1c1b7e-2d7a-4c1e-9d3a-0b5e8f7a1c21"
            }
          }
        }
      },
      "9": {
        "action": "optout"
      }
    }
  }
}

Responses

201 ​

Published.

FieldTypeRequiredDescription
inbound_ivr_menuobject (InboundIvrMenu)yes
inbound_ivr_menu.idstring (uuid)yesThe menu; stable across its versions.
inbound_ivr_menu.statuspublished · retiredyes
inbound_ivr_menu.versionintegeryesThe current version, or the one the menu was retired at.
inbound_ivr_menu.namestringyes
inbound_ivr_menu.menuobject (InboundIvrMenuNode)yes
inbound_ivr_menu.menu.promptstringyesWhat the caller hears on entering the node, in the tenant's words (at most 2000 bytes; blank is refused). Rendered to audio when the menu is published.
inbound_ivr_menu.menu.defaultanyyesMANDATORY. With no key pressed the prompt plays again and, the second time, this action runs — no menu is a dead end. Never optout: pressing nothing is not asking to leave the list.
inbound_ivr_menu.menu.entriesobjectyesOne DTMF key (0-9, *, #) → a terminal action or a submenu.
inbound_ivr_menu.published_atstring (date-time)yesWhen this version was published.
inbound_ivr_menu.published_bystringyesThe user or agent id of the credential that published this version, or tenant-api-key.
inbound_ivr_menu.retired_atstring,null (date-time)yes
inbound_ivr_menu.retired_bystring,nullyes

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 ​

role_forbidden: a dashboard login without the account_admin role; forbidden: a widget token or a suspended tenant.

json
{
  "error": {
    "code": "role_forbidden",
    "message": "a dashboard login needs the account_admin role to manage inbound IVR menus"
  }
}

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"
  }
}

409 — Conflict ​

Invalid state transition, uniqueness conflict, or a seat that is busy/reserved.

json
{
  "error": {
    "code": "conflict",
    "message": "invalid state transition"
  }
}

422 — Unprocessable ​

Shape is fine, semantics are not (broken domain rule).

json
{
  "error": {
    "code": "unprocessable",
    "message": "scheduled_at is closer than the minimum notice for a callback (10 minutes unless the tenant's callback_min_notice_seconds says otherwise)"
  }
}

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"
  }
}

503 ​

A prompt did not render to audio; nothing was published. Retryable.

json
{
  "error": {
    "code": "prompt_render_failed",
    "message": "a prompt could not be rendered to audio; the menu was not published"
  }
}

One inbound IVR menu, its current version and its history ​

http
GET /v1/inbound-ivr-menus/{id}

The menu's current version — or, once retired, the version it was retired at, with status: retired — plus history, every fact from the first version to the retirement. A menu that is not this tenant's (or does not exist) is 404. Same readers as the list.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yesResource UUID. Malformed or cross-tenant ids read as 404.

Responses

200 ​

The menu and its history.

FieldTypeRequiredDescription
inbound_ivr_menuanyyes

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 — Forbidden ​

The tenant is suspended. A dashboard session (ddu_) of a tenant whose login policy (ADR 227) is in mode enforce answers 403 with the code login_policy_denied on a request from an address or at an hour the policy does not allow — the same body for both; the session is not revoked and a later request inside the policy goes through. A widget token (ddw_) of a seat is held to the same policy on every request (core#2406, flag widget_login_honors_policy). A synthetic (demo) tenant answers 403 with the code synthetic_tenant on every route that would reach a third party — manual dial, external transfer or conference leg, SMS send (core#2251, ADR #280 addendum). No payment changes it.

json
{
  "error": {
    "code": "forbidden",
    "message": "tenant is suspended"
  }
}

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"
  }
}

Retire an inbound IVR menu ​

http
POST /v1/inbound-ivr-menus/{id}/retire

Retires the menu with a new fact that names its current version; the history is append-only. A retired menu stays retired: it cannot be published again. An id that is not a current menu of this tenant (unknown, already retired, or another tenant's) is 404. A menu that an ACTIVE inbound route still uses is 409 conflict naming the routes: disable them or point them at another menu first (core#1319). Same roles as publishing. No body.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yesResource UUID. Malformed or cross-tenant ids read as 404.

Responses

200 ​

Retired.

FieldTypeRequiredDescription
inbound_ivr_menuobject (InboundIvrMenu)yes
inbound_ivr_menu.idstring (uuid)yesThe menu; stable across its versions.
inbound_ivr_menu.statuspublished · retiredyes
inbound_ivr_menu.versionintegeryesThe current version, or the one the menu was retired at.
inbound_ivr_menu.namestringyes
inbound_ivr_menu.menuobject (InboundIvrMenuNode)yes
inbound_ivr_menu.menu.promptstringyesWhat the caller hears on entering the node, in the tenant's words (at most 2000 bytes; blank is refused). Rendered to audio when the menu is published.
inbound_ivr_menu.menu.defaultanyyesMANDATORY. With no key pressed the prompt plays again and, the second time, this action runs — no menu is a dead end. Never optout: pressing nothing is not asking to leave the list.
inbound_ivr_menu.menu.entriesobjectyesOne DTMF key (0-9, *, #) → a terminal action or a submenu.
inbound_ivr_menu.published_atstring (date-time)yesWhen this version was published.
inbound_ivr_menu.published_bystringyesThe user or agent id of the credential that published this version, or tenant-api-key.
inbound_ivr_menu.retired_atstring,null (date-time)yes
inbound_ivr_menu.retired_bystring,nullyes

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 ​

role_forbidden: a dashboard login without the account_admin role; forbidden: a widget token or a suspended tenant.

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"
  }
}

409 — Conflict ​

Invalid state transition, uniqueness conflict, or a seat that is busy/reserved.

json
{
  "error": {
    "code": "conflict",
    "message": "invalid state transition"
  }
}

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"
  }
}

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