Skip to content

Click to call ​

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

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 ​

MethodPathSummary
GET/v1/click-to-call/sitesList the tenant's click-to-call sites
POST/v1/click-to-call/sitesMint 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-configRead the call-request form's configuration
POST/v1/public/call-requestsLeave a call request

List the tenant's click-to-call sites ​

http
GET /v1/click-to-call/sites

Every 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.

FieldTypeRequiredDescription
sitesarray of ClickToCallSiteyes
sites[].idstring (uuid)no
sites[].campaign_idstring (uuid)no
sites[].labelstringno
sites[].allow_immediatebooleanno
sites[].allow_scheduledbooleanno
sites[].tenant_daily_capintegerno
sites[].number_daily_capintegerno
sites[].site_hourly_capintegerno
sites[].revoked_atstring (date-time) | nullno
sites[].created_atstring (date-time)no
sites[].updated_atstring (date-time)no
sites[].consent_versionstring | nullnoCurrent consent text version (decimal string, e.g. "2"); null while the site has no text.
sites[].consent_textstring | nullno
sites[].consent_sha256string | nullnoLowercase 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/sites

Creates 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)

FieldTypeRequiredDescription
campaign_idstring (uuid)yes
labelstringno
allow_immediatebooleanno
allow_scheduledbooleanno
tenant_daily_capintegerno
number_daily_capintegerno
site_hourly_capintegerno
consent_textstringnoBecomes 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).

FieldTypeRequiredDescription
siteobject (ClickToCallSite)yesA click-to-call site as the console sees it. Never carries the key.
site.idstring (uuid)no
site.campaign_idstring (uuid)no
site.labelstringno
site.allow_immediatebooleanno
site.allow_scheduledbooleanno
site.tenant_daily_capintegerno
site.number_daily_capintegerno
site.site_hourly_capintegerno
site.revoked_atstring (date-time) | nullno
site.created_atstring (date-time)no
site.updated_atstring (date-time)no
site.consent_versionstring | nullnoCurrent consent text version (decimal string, e.g. "2"); null while the site has no text.
site.consent_textstring | nullno
site.consent_sha256string | nullnoLowercase hex sha256 of the bytes of consent_text, computed by the server.
tokenstringyesPlaintext 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

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

Responses

200 ​

The site with its current consent text.

FieldTypeRequiredDescription
siteobject (ClickToCallSite)yesA click-to-call site as the console sees it. Never carries the key.
site.idstring (uuid)no
site.campaign_idstring (uuid)no
site.labelstringno
site.allow_immediatebooleanno
site.allow_scheduledbooleanno
site.tenant_daily_capintegerno
site.number_daily_capintegerno
site.site_hourly_capintegerno
site.revoked_atstring (date-time) | nullno
site.created_atstring (date-time)no
site.updated_atstring (date-time)no
site.consent_versionstring | nullnoCurrent consent text version (decimal string, e.g. "2"); null while the site has no text.
site.consent_textstring | nullno
site.consent_sha256string | nullnoLowercase 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

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

Request body (JSON, required)

FieldTypeRequiredDescription
campaign_idstring (uuid)no
labelstringno
allow_immediatebooleanno
allow_scheduledbooleanno
tenant_daily_capintegerno
number_daily_capintegerno
site_hourly_capintegerno
consent_textstringnoA 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.

FieldTypeRequiredDescription
siteobject (ClickToCallSite)yesA click-to-call site as the console sees it. Never carries the key.
site.idstring (uuid)no
site.campaign_idstring (uuid)no
site.labelstringno
site.allow_immediatebooleanno
site.allow_scheduledbooleanno
site.tenant_daily_capintegerno
site.number_daily_capintegerno
site.site_hourly_capintegerno
site.revoked_atstring (date-time) | nullno
site.created_atstring (date-time)no
site.updated_atstring (date-time)no
site.consent_versionstring | nullnoCurrent consent text version (decimal string, e.g. "2"); null while the site has no text.
site.consent_textstring | nullno
site.consent_sha256string | nullnoLowercase 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

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

Responses

200 ​

The revoked site.

FieldTypeRequiredDescription
siteobject (ClickToCallSite)yesA click-to-call site as the console sees it. Never carries the key.
site.idstring (uuid)no
site.campaign_idstring (uuid)no
site.labelstringno
site.allow_immediatebooleanno
site.allow_scheduledbooleanno
site.tenant_daily_capintegerno
site.number_daily_capintegerno
site.site_hourly_capintegerno
site.revoked_atstring (date-time) | nullno
site.created_atstring (date-time)no
site.updated_atstring (date-time)no
site.consent_versionstring | nullnoCurrent consent text version (decimal string, e.g. "2"); null while the site has no text.
site.consent_textstring | nullno
site.consent_sha256string | nullnoLowercase 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-config

What 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.

FieldTypeRequiredDescription
configobject (ClickToCallPublicConfig)yes
config.site_idstring (uuid)no
config.labelstringno
config.allow_immediatebooleanno
config.allow_scheduledbooleanno
config.consent_versionstring | nullnoCurrent consent text version (decimal string, e.g. "2"); null while the site has no text.
config.consent_textstring | nullno
config.consent_sha256string | nullnoLowercase 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-requests

INSERT-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)

FieldTypeRequiredDescription
modeimmediate · scheduledyes
scheduled_forstring (date-time)noRequired when mode is scheduled, forbidden when immediate.
phonestringyesNormalized E.164, e.g. +13125550100.
consent_text_versionstringyesThe consent_version the form got from the public config.
consent_text_sha256stringyesThe consent_sha256 the form got from the public config.
consent_checkedbooleanyesThe visitor's declaration. Must be true: false answers 422 consent_not_checked and nothing is written.
captcha_tokenstringnoOptional. Checked by the captcha provider when the cell configures one (none by default); a rejected token answers 403 captcha_failed.

Responses

202 ​

Received.

FieldTypeRequiredDescription
request_idstring (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.

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