Appearance
Support access
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.
Platform-support access (ADR 225): the customer's consent (a grant with a reason and a fixed expiry, one live per tenant, revoked at will) and the append-only trail of it. account_admin only, except the banner.
Endpoints
| Method | Path | Summary |
|---|---|---|
POST | /v1/support-access/grants | Let platform support in, with a reason and a fixed expiry |
DELETE | /v1/support-access/grants/{id} | End the consent at once |
GET | /v1/support-access/events | The trail of platform-support access, newest first |
GET | /v1/support-access/active | Whether a support session is open right now (the banner) |
Let platform support in, with a reason and a fixed expiry
http
POST /v1/support-access/grantsThe customer's consent to platform-support access (ADR 225). An account_admin states a reason (a ticket or a motive, 1 to 500 characters, no control characters) and, optionally, a duration_minutes (15 to 480, default 60). The grant expires at created_at + duration_minutes and that expiry NEVER moves: a longer access is a new grant.
One grant is live per tenant at a time: while one is neither revoked nor expired the answer is 409 grant_already_live, naming it. A grant_created event is written in the same transaction.
This route does not open anything by itself: a grant is the consent a support session needs, and the session lane is gated by the support_access_enabled flag, which is OFF in every cell. The route answers whatever the flag says, so a customer can always grant, revoke and read the trail.
account_admin dashboard login only (see 403). Platform staff have no route to write a grant.
Request body (JSON, required)
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | yes | |
duration_minutes | integer | no |
Responses
201
The grant is live.
| Field | Type | Required | Description |
|---|---|---|---|
grant | object (SupportAccessGrant) | yes | |
grant.id | string (uuid) | yes | |
grant.granted_by | string (uuid) | yes | The account_admin who granted it. |
grant.reason | string | yes | |
grant.duration_minutes | integer | yes | |
grant.created_at | string (date-time) | yes | |
grant.expires_at | string (date-time) | yes | Always created_at + duration_minutes; it never moves. |
grant.revoked_at | string (date-time) | null | yes | |
grant.revoked_by | string (uuid) | null | 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
role_forbidden: anything but a dashboard login (ddu_) whose person has the account_admin role — a dd_ API key and a member login included; forbidden: a widget token (ddw_) or a suspended tenant.
json
{
"error": {
"code": "role_forbidden",
"message": "granting platform-support access takes a dashboard login with the account_admin role"
}
}409
grant_already_live: the tenant already has a grant that is neither revoked nor expired; revoke it first.
json
{
"error": {
"code": "grant_already_live",
"message": "a support-access grant is already live for this tenant; revoke it before granting again"
}
}422
unprocessable: the reason is missing, blank, over 500 characters or has control characters, or duration_minutes is not an integer from 15 to 480. Nothing is written.
json
{
"error": {
"code": "unprocessable",
"message": "duration_minutes must be an integer from 15 to 480"
}
}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"
}
}End the consent at once
http
DELETE /v1/support-access/grants/{id}Revokes the grant id. The row is kept as evidence: it gains revoked_at and revoked_by, once, and a grant_revoked event is written in the same transaction. A grant that expired by itself can still be revoked. A revoked grant is 409 grant_already_revoked.
An unknown id, a malformed one and another tenant's grant are the same 404.
account_admin dashboard login only (see 403).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string (uuid) | yes |
Responses
200
The grant, now revoked.
| Field | Type | Required | Description |
|---|---|---|---|
grant | object (SupportAccessGrant) | yes | |
grant.id | string (uuid) | yes | |
grant.granted_by | string (uuid) | yes | The account_admin who granted it. |
grant.reason | string | yes | |
grant.duration_minutes | integer | yes | |
grant.created_at | string (date-time) | yes | |
grant.expires_at | string (date-time) | yes | Always created_at + duration_minutes; it never moves. |
grant.revoked_at | string (date-time) | null | yes | |
grant.revoked_by | string (uuid) | null | yes |
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: anything but a dashboard login (ddu_) whose person has the account_admin role.
json
{
"error": {
"code": "role_forbidden",
"message": "revoking platform-support access takes a dashboard login with the account_admin role"
}
}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
grant_already_revoked: the grant was revoked before.
json
{
"error": {
"code": "grant_already_revoked",
"message": "this grant was already revoked"
}
}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"
}
}The trail of platform-support access, newest first
http
GET /v1/support-access/eventsThe append-only record of the consent and of everything support did under it, keyset-paginated, newest first. event is one of grant_created, grant_revoked, session_opened, session_revoked, request and request_denied. A request row names the route PATTERN and the id in the path, never a body, a query string or a response. Rows are never edited or deleted.
Filters: event, grant_id, since (inclusive) and until (exclusive), both RFC-3339 on occurred_at. A filter that does not parse is 400 naming the accepted values — never a silently empty page. Tenant-scoped: another tenant's rows are never visible, whatever the filter.
account_admin dashboard login only (see 403).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
event | query | grant_created · grant_revoked · session_opened · session_revoked · request · request_denied | no | |
grant_id | query | string (uuid) | no | |
since | query | string (date-time) | no | Only events at or after this instant. |
until | query | string (date-time) | no | Only events strictly before this instant. |
limit | query | integer | no | |
cursor | query | string | no | Opaque keyset cursor — the next_cursor of the previous page. |
Responses
200
One page of the trail, newest first.
| Field | Type | Required | Description |
|---|---|---|---|
events | array of SupportAccessEvent | yes | |
events[].id | string (uuid) | yes | |
events[].grant_id | string (uuid) | yes | |
events[].session_id | string (uuid) | null | yes | The support session; null on grant_created and grant_revoked. |
events[].event | grant_created · grant_revoked · session_opened · session_revoked · request · request_denied | yes | |
events[].actor_kind | customer_user · operator | yes | |
events[].actor_ref | string | yes | The customer person's id, or operator:<id> (<name>) for platform staff. |
events[].method | GET · HEAD | null | yes | Only on request and request_denied. |
events[].route | string | null | yes | The route PATTERN (for example /v1/leads/:id), never the path with values; only on request and request_denied. |
events[].resource_id | string | null | yes | The id in the path, on request rows only. |
events[].ip | string | null | yes | |
events[].occurred_at | string (date-time) | yes | |
next_cursor | string | null | yes | Cursor of the next page; null on the last one. |
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: anything but a dashboard login (ddu_) whose person has the account_admin role.
json
{
"error": {
"code": "role_forbidden",
"message": "the support-access trail takes a dashboard login with the account_admin role"
}
}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"
}
}Whether a support session is open right now (the banner)
http
GET /v1/support-access/activeWhat the dashboard shows every person of the tenant: is someone from platform support inside the account at this moment, until when, and who. active is false when the support_access_enabled flag is off for the tenant, when there is no live grant, or when no session of it is open and unexpired. A session is open from its session_opened event until a session_revoked one, for at most 60 minutes and never past the grant's expiry. The support session itself arrives with a later change: until then this answers active: false for every tenant.
Any dashboard login (ddu_) of the tenant, member included. An API key (dd_) is 403.
Responses
200
The banner's answer.
| Field | Type | Required | Description |
|---|---|---|---|
active | boolean | yes | |
expires_at | string (date-time) | null | yes | When the open session ends; null when not active. |
operator_name | string | null | yes | The platform person's name; null when not active or unnamed. |
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 dd_ API key; forbidden: a widget token or a suspended tenant.
json
{
"error": {
"code": "role_forbidden",
"message": "the support-access banner takes a dashboard login"
}
}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"
}
}