Appearance
Click to call
Generated from
DialerDigital/corecommit0fc18008b3c8— specopenapi/dialer-v1.yamlv0.134.0. Do not edit by hand; see How this reference is built. Download the OpenAPI spec.
Click-to-call for the customer's website (ADR 220): the console mints/revokes a PUBLISHABLE ddc_ site key, and the visitor's browser uses it to read the form's config and to leave a call request. Gated by click_to_call_enabled.
Endpoints
| Method | Path | Summary |
|---|---|---|
GET | /v1/click-to-call/sites | List the tenant's click-to-call sites |
POST | /v1/click-to-call/sites | Mint a click-to-call site key |
GET | /v1/click-to-call/sites/{id} | Read one click-to-call site |
PATCH | /v1/click-to-call/sites/{id} | Configure a click-to-call site |
DELETE | /v1/click-to-call/sites/{id} | Revoke a click-to-call site key |
GET | /v1/public/call-request-config | Read the call-request form's configuration |
POST | /v1/public/call-requests | Leave a call request |
List the tenant's click-to-call sites
http
GET /v1/click-to-call/sitesEvery site of the tenant, newest first, revoked ones included (revoked_at set). Each carries its CURRENT consent text, version and sha256. Never carries the key (only its sha256 is stored). Reads are not gated by click_to_call_enabled.
Responses
200
The tenant's sites.
| Field | Type | Required | Description |
|---|---|---|---|
sites | array of ClickToCallSite | yes | |
sites[].id | string (uuid) | no | |
sites[].campaign_id | string (uuid) | no | |
sites[].label | string | no | |
sites[].allow_immediate | boolean | no | |
sites[].allow_scheduled | boolean | no | |
sites[].tenant_daily_cap | integer | no | |
sites[].number_daily_cap | integer | no | |
sites[].site_hourly_cap | integer | no | |
sites[].revoked_at | string (date-time) | null | no | |
sites[].created_at | string (date-time) | no | |
sites[].updated_at | string (date-time) | no | |
sites[].consent_version | string | null | no | Current consent text version (decimal string, e.g. "2"); null while the site has no text. |
sites[].consent_text | string | null | no | |
sites[].consent_sha256 | string | null | no | Lowercase hex sha256 of the bytes of consent_text, computed by the server. |
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"
}
}Mint a click-to-call site key
http
POST /v1/click-to-call/sitesCreates a site bound to one campaign and returns its publishable ddc_ key. The plaintext is shown exactly once; only its sha256 is stored. Requires click_to_call_enabled for the tenant (403 otherwise). A consent_text given here becomes version 1; without it the site has no consent text and refuses every request (409 consent_version_mismatch) until one is configured with PATCH /v1/click-to-call/sites/{id}.
Request body (JSON, required)
| Field | Type | Required | Description |
|---|---|---|---|
campaign_id | string (uuid) | yes | |
label | string | no | |
allow_immediate | boolean | no | |
allow_scheduled | boolean | no | |
tenant_daily_cap | integer | no | |
number_daily_cap | integer | no | |
site_hourly_cap | integer | no | |
consent_text | string | no | Becomes consent version 1 of the site. |
json
{
"campaign_id": "7f1c0b0e-5d3a-4c55-9a3e-0b6f3b1f2a10",
"label": "Landing de ventas"
}Responses
201
The site and its key (shown once).
| Field | Type | Required | Description |
|---|---|---|---|
site | object (ClickToCallSite) | yes | A click-to-call site as the console sees it. Never carries the key. |
site.id | string (uuid) | no | |
site.campaign_id | string (uuid) | no | |
site.label | string | no | |
site.allow_immediate | boolean | no | |
site.allow_scheduled | boolean | no | |
site.tenant_daily_cap | integer | no | |
site.number_daily_cap | integer | no | |
site.site_hourly_cap | integer | no | |
site.revoked_at | string (date-time) | null | no | |
site.created_at | string (date-time) | no | |
site.updated_at | string (date-time) | no | |
site.consent_version | string | null | no | Current consent text version (decimal string, e.g. "2"); null while the site has no text. |
site.consent_text | string | null | no | |
site.consent_sha256 | string | null | no | Lowercase hex sha256 of the bytes of consent_text, computed by the server. |
token | string | yes | Plaintext ddc_ site key, shown exactly once. |
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 — 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"
}
}Read one click-to-call site
http
GET /v1/click-to-call/sites/{id}Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string (uuid) | yes | Resource UUID. Malformed or cross-tenant ids read as 404. |
Responses
200
The site with its current consent text.
| Field | Type | Required | Description |
|---|---|---|---|
site | object (ClickToCallSite) | yes | A click-to-call site as the console sees it. Never carries the key. |
site.id | string (uuid) | no | |
site.campaign_id | string (uuid) | no | |
site.label | string | no | |
site.allow_immediate | boolean | no | |
site.allow_scheduled | boolean | no | |
site.tenant_daily_cap | integer | no | |
site.number_daily_cap | integer | no | |
site.site_hourly_cap | integer | no | |
site.revoked_at | string (date-time) | null | no | |
site.created_at | string (date-time) | no | |
site.updated_at | string (date-time) | no | |
site.consent_version | string | null | no | Current consent text version (decimal string, e.g. "2"); null while the site has no text. |
site.consent_text | string | null | no | |
site.consent_sha256 | string | null | no | Lowercase hex sha256 of the bytes of consent_text, computed by the server. |
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"
}
}Configure a click-to-call site
http
PATCH /v1/click-to-call/sites/{id}Partial update of label, campaign, allowed modes, caps and consent text. A consent_text that differs from the current one inserts version N+1 (the previous versions are append-only evidence and are never modified or deleted); the same text again changes nothing. The identity, the key and revoked_at cannot be changed. A revoked site is read-only (409). Not gated by click_to_call_enabled.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string (uuid) | yes | Resource UUID. Malformed or cross-tenant ids read as 404. |
Request body (JSON, required)
| Field | Type | Required | Description |
|---|---|---|---|
campaign_id | string (uuid) | no | |
label | string | no | |
allow_immediate | boolean | no | |
allow_scheduled | boolean | no | |
tenant_daily_cap | integer | no | |
number_daily_cap | integer | no | |
site_hourly_cap | integer | no | |
consent_text | string | no | A text different from the current one creates version N+1. |
json
{
"number_daily_cap": 3,
"consent_text": "Acepto que me llamen al número indicado."
}Responses
200
The updated site.
| Field | Type | Required | Description |
|---|---|---|---|
site | object (ClickToCallSite) | yes | A click-to-call site as the console sees it. Never carries the key. |
site.id | string (uuid) | no | |
site.campaign_id | string (uuid) | no | |
site.label | string | no | |
site.allow_immediate | boolean | no | |
site.allow_scheduled | boolean | no | |
site.tenant_daily_cap | integer | no | |
site.number_daily_cap | integer | no | |
site.site_hourly_cap | integer | no | |
site.revoked_at | string (date-time) | null | no | |
site.created_at | string (date-time) | no | |
site.updated_at | string (date-time) | no | |
site.consent_version | string | null | no | Current consent text version (decimal string, e.g. "2"); null while the site has no text. |
site.consent_text | string | null | no | |
site.consent_sha256 | string | null | no | Lowercase hex sha256 of the bytes of consent_text, computed by the server. |
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 — 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"
}
}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"
}
}Revoke a click-to-call site key
http
DELETE /v1/click-to-call/sites/{id}Revokes the key (idempotent, a second call returns the same revoked site). Available even with click_to_call_enabled off — a key can always be killed. Requests already received are evidence and are never deleted.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string (uuid) | yes | Resource UUID. Malformed or cross-tenant ids read as 404. |
Responses
200
The revoked site.
| Field | Type | Required | Description |
|---|---|---|---|
site | object (ClickToCallSite) | yes | A click-to-call site as the console sees it. Never carries the key. |
site.id | string (uuid) | no | |
site.campaign_id | string (uuid) | no | |
site.label | string | no | |
site.allow_immediate | boolean | no | |
site.allow_scheduled | boolean | no | |
site.tenant_daily_cap | integer | no | |
site.number_daily_cap | integer | no | |
site.site_hourly_cap | integer | no | |
site.revoked_at | string (date-time) | null | no | |
site.created_at | string (date-time) | no | |
site.updated_at | string (date-time) | no | |
site.consent_version | string | null | no | Current consent text version (decimal string, e.g. "2"); null while the site has no text. |
site.consent_text | string | null | no | |
site.consent_sha256 | string | null | no | Lowercase hex sha256 of the bytes of consent_text, computed by the server. |
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"
}
}Read the call-request form's configuration
http
GET /v1/public/call-request-configWhat the visitor's form needs to draw itself. Nothing about the tenant's internals (no campaign, no caps). It carries the CURRENT consent text with its consent_version and consent_sha256 (sha256 of the text's bytes, computed by the server): the form must show exactly that text and echo the version and hash back in POST /v1/public/call-requests.
Responses
200
The site's public configuration.
| Field | Type | Required | Description |
|---|---|---|---|
config | object (ClickToCallPublicConfig) | yes | |
config.site_id | string (uuid) | no | |
config.label | string | no | |
config.allow_immediate | boolean | no | |
config.allow_scheduled | boolean | no | |
config.consent_version | string | null | no | Current consent text version (decimal string, e.g. "2"); null while the site has no text. |
config.consent_text | string | null | no | |
config.consent_sha256 | string | null | no | Lowercase hex sha256 of the bytes of consent_text, computed by the server. |
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
The lane is off (cell ceiling closed or the tenant's click_to_call_enabled off). Byte-identical to any unknown route.
Leave a call request
http
POST /v1/public/call-requestsINSERT-ONLY. Records the visitor's declaration (never a consent) and answers 202 {request_id} — the answer says nothing about whether the number belongs to a customer of the tenant.
The consent_text_version and consent_text_sha256 must be the site's CURRENT ones (the form got them from GET /v1/public/call-request-config); otherwise the answer is 409 consent_version_mismatch and nothing is written (fail-closed). A site with no consent text configured always answers 409. The accepted version is stored with the request.
Volume is bounded by DURABLE caps counted in the insert's transaction (the site's tenant_daily_cap, number_daily_cap, site_hourly_cap, rolling windows) and by a per-site rate bucket. Every one answers the same 429, writes nothing and carries no Retry-After. If the caps cannot be read the answer is 503 and nothing is written. An optional captcha_token is checked by the configured provider (none by default).
Request body (JSON, required)
| Field | Type | Required | Description |
|---|---|---|---|
mode | immediate · scheduled | yes | |
scheduled_for | string (date-time) | no | Required when mode is scheduled, forbidden when immediate. |
phone | string | yes | Normalized E.164, e.g. +13125550100. |
consent_text_version | string | yes | The consent_version the form got from the public config. |
consent_text_sha256 | string | yes | The consent_sha256 the form got from the public config. |
consent_checked | boolean | yes | The visitor's declaration. Must be true: false answers 422 consent_not_checked and nothing is written. |
captcha_token | string | no | Optional. Checked by the captcha provider when the cell configures one (none by default); a rejected token answers 403 captcha_failed. |
Responses
202
Received.
| Field | Type | Required | Description |
|---|---|---|---|
request_id | string (uuid) | yes |
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 — 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
The lane is off (cell ceiling closed or the tenant's click_to_call_enabled off). Byte-identical to any unknown route; nothing is written.
409
consent_version_mismatch — the version or hash does not match the site's current consent text. Nothing is written; reload the form configuration.
422
consent_not_checked — the consent checkbox was sent unchecked (consent_checked: false); a request that says the visitor did not declare is not a call request. Also unprocessable for a mode the site does not allow. Nothing is written.
429
A volume cap or the per-site bucket was reached. Same body for every cap; no Retry-After; nothing is written.
503
The caps (or the captcha provider) could not be read, so the request is not admitted. Nothing is written.