{"openapi":"3.0.0","paths":{"/v1/workspaces/{workspace_id}/webhooks/event-types":{"get":{"description":"Each type with a plain description, its group and an example `data` object (what \"Send test\" delivers).","operationId":"WebhooksController_notify_workspace_listEventTypes","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListWebhookEventTypesResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"`UNAUTHORIZED`: Missing or invalid bearer token. A recipient token is refused with `RECIPIENT_TOKEN_INVALID` (it works only on `/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `notify.webhook.read` (reads) or `notify.webhook.manage` (changes, Send test, replay), or Notify is not enabled for the workspace (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List the events an endpoint can subscribe to.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.eventTypes","method":"list","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/webhooks/endpoints":{"get":{"description":"At most 10, so this is always one page. Signing secrets are never returned here, only their last 4 characters.","operationId":"WebhooksController_notify_workspace_listEndpoints","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListWebhookEndpointsResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"The scope id in the path is malformed.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"`UNAUTHORIZED`: Missing or invalid bearer token. A recipient token is refused with `RECIPIENT_TOKEN_INVALID` (it works only on `/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `notify.webhook.read` (reads) or `notify.webhook.manage` (changes, Send test, replay), or Notify is not enabled for the workspace (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List webhook endpoints, oldest first.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.endpoints","method":"list","bind":["workspace_id"],"paginated":true}},"post":{"description":"Deliveries are signed with the Standard Webhooks headers `webhook-id`, `webhook-timestamp` and `webhook-signature` (`v1,<base64 HMAC-SHA256 of \"<id>.<timestamp>.<body>\">`); the returned `whsec_…` secret works unchanged with any Standard Webhooks library. Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h, 6 h and 24 h.","operationId":"WebhooksController_notify_workspace_createEndpoint","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"Idempotency-Key","in":"header","description":"Opaque client-generated string (1-256 chars, `[A-Za-z0-9_-:]`) that makes a retry of this request safe for 24 hours. The same key with a different body returns 409 `IDEMPOTENCY_KEY_REUSE`.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookEndpointDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointWithSecretDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"Validation failure: URL not https with a public host, or an unknown event type (`WEBHOOK_EVENT_TYPE_UNKNOWN`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR","WEBHOOK_EVENT_TYPE_UNKNOWN"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"`UNAUTHORIZED`: Missing or invalid bearer token. A recipient token is refused with `RECIPIENT_TOKEN_INVALID` (it works only on `/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `notify.webhook.read` (reads) or `notify.webhook.manage` (changes, Send test, replay), or Notify is not enabled for the workspace (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Already 10 endpoints (`WEBHOOK_ENDPOINT_LIMIT`), or Idempotency-Key reused with a different body (`IDEMPOTENCY_KEY_REUSE`). Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","WEBHOOK_ENDPOINT_LIMIT","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"The URL points at a private, loopback or internal address.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Add a webhook endpoint. Returns its signing secret once.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.endpoints","method":"create","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/webhooks/endpoints/{endpoint_id}":{"get":{"description":"The endpoint's URL, subscription, on/off state, failure streak and the last 4 characters of its signing secret. The full secret is only returned when the endpoint is created or its secret is rotated.","operationId":"WebhooksController_notify_workspace_getEndpoint","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"endpoint_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the endpoint.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`endpoint_id` is not a UUID.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"`UNAUTHORIZED`: Missing or invalid bearer token. A recipient token is refused with `RECIPIENT_TOKEN_INVALID` (it works only on `/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `notify.webhook.read` (reads) or `notify.webhook.manage` (changes, Send test, replay), or Notify is not enabled for the workspace (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"No such endpoint here (`WEBHOOK_ENDPOINT_NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","WEBHOOK_ENDPOINT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get one webhook endpoint (without its secret).","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.endpoints","method":"retrieve","bind":["workspace_id"],"paginated":false}},"patch":{"description":"Only the fields sent change. `enabled: true` (or a new URL) also clears an automatic disable. Switching off cancels queued retries. The secret is unchanged; rotate it with `POST …/rotate-secret`.","operationId":"WebhooksController_notify_workspace_updateEndpoint","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"endpoint_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the endpoint.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"Idempotency-Key","in":"header","description":"Opaque client-generated string (1-256 chars, `[A-Za-z0-9_-:]`) that makes a retry of this request safe for 24 hours. The same key with a different body returns 409 `IDEMPOTENCY_KEY_REUSE`.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookEndpointDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"Validation failure, or `endpoint_id` is not a UUID.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"`UNAUTHORIZED`: Missing or invalid bearer token. A recipient token is refused with `RECIPIENT_TOKEN_INVALID` (it works only on `/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `notify.webhook.read` (reads) or `notify.webhook.manage` (changes, Send test, replay), or Notify is not enabled for the workspace (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"No such endpoint here (`WEBHOOK_ENDPOINT_NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","WEBHOOK_ENDPOINT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Idempotency-Key reused with a different body (`IDEMPOTENCY_KEY_REUSE`). Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"The new URL points at a private, loopback or internal address.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Change a webhook endpoint: URL, description, events, on/off.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.endpoints","method":"update","bind":["workspace_id"],"paginated":false}},"delete":{"description":"Queued retries are dropped. Its past attempts stay in the event log until the events expire.","operationId":"WebhooksController_notify_workspace_deleteEndpoint","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"endpoint_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the endpoint.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"204":{"description":"Deleted.","headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`endpoint_id` is not a UUID.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"`UNAUTHORIZED`: Missing or invalid bearer token. A recipient token is refused with `RECIPIENT_TOKEN_INVALID` (it works only on `/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `notify.webhook.read` (reads) or `notify.webhook.manage` (changes, Send test, replay), or Notify is not enabled for the workspace (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"No such endpoint here (`WEBHOOK_ENDPOINT_NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","WEBHOOK_ENDPOINT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Delete a webhook endpoint.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.endpoints","method":"delete","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/webhooks/endpoints/{endpoint_id}/rotate-secret":{"post":{"description":"For `grace_hours` (default 24) the previous secret keeps signing too: `webhook-signature` then lists one `v1,` signature per secret, so receivers can switch over without dropping a delivery.","operationId":"WebhooksController_notify_workspace_rotateSecret","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"endpoint_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the endpoint.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"Idempotency-Key","in":"header","description":"Opaque client-generated string (1-256 chars, `[A-Za-z0-9_-:]`) that makes a retry of this request safe for 24 hours. The same key with a different body returns 409 `IDEMPOTENCY_KEY_REUSE`.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotateWebhookSecretDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointWithSecretDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`grace_hours` out of range, or `endpoint_id` is not a UUID.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"`UNAUTHORIZED`: Missing or invalid bearer token. A recipient token is refused with `RECIPIENT_TOKEN_INVALID` (it works only on `/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `notify.webhook.read` (reads) or `notify.webhook.manage` (changes, Send test, replay), or Notify is not enabled for the workspace (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"No such endpoint here (`WEBHOOK_ENDPOINT_NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","WEBHOOK_ENDPOINT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Idempotency-Key reused with a different body (`IDEMPOTENCY_KEY_REUSE`). Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Rotate the signing secret. The new one is returned once.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.endpoints","method":"rotateSecret","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/webhooks/endpoints/{endpoint_id}/test":{"post":{"description":"Delivers the catalogue example of `event_type`, signed like any delivery, with `\"test\": true` in the body. Works while the endpoint is switched off or not subscribed to that type, is logged in the event log, and is never retried or counted toward an automatic disable. Waits up to 15 s for your receiver.","operationId":"WebhooksController_notify_workspace_sendTest","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"endpoint_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the endpoint.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"Idempotency-Key","in":"header","description":"Opaque client-generated string (1-256 chars, `[A-Za-z0-9_-:]`) that makes a retry of this request safe for 24 hours. The same key with a different body returns 409 `IDEMPOTENCY_KEY_REUSE`.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendWebhookTestDto"}}}},"responses":{"200":{"description":"The attempt, successful or not.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryAttemptDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"Unknown `event_type`, or `endpoint_id` is not a UUID.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"`UNAUTHORIZED`: Missing or invalid bearer token. A recipient token is refused with `RECIPIENT_TOKEN_INVALID` (it works only on `/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `notify.webhook.read` (reads) or `notify.webhook.manage` (changes, Send test, replay), or Notify is not enabled for the workspace (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"No such endpoint here (`WEBHOOK_ENDPOINT_NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","WEBHOOK_ENDPOINT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"Idempotency-Key reused with a different body (`IDEMPOTENCY_KEY_REUSE`). Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Send a test event to this endpoint now and return the result.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.endpoints","method":"test","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/webhooks/events":{"get":{"description":"Every event logged while at least one endpoint existed, with the latest state per endpoint and when the next retry is due.","operationId":"WebhooksController_notify_workspace_listEvents","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"type","required":false,"in":"query","description":"Only these types. Repeat the parameter or comma-separate.","schema":{"type":"array","items":{"type":"string","enum":["notification.accepted","delivery.sent","delivery.delivered","delivery.failed","delivery.bounced","delivery.skipped","inbox_item.read","inbox_item.archived","inbox_item.interacted","device.pruned","device.moved","credential.invalid","email.auto_paused","recipient.deleted"]}},"example":["notification.accepted"]},{"name":"endpoint_id","required":false,"in":"query","description":"Only events sent (or queued) to this endpoint.","schema":{"format":"uuid","type":"string"},"example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"status","required":false,"in":"query","description":"Only events in this delivery status.","schema":{"$ref":"#/components/schemas/WebhookEventStatus"},"example":"pending"},{"name":"created_after","required":false,"in":"query","description":"Only events created at or after this instant.","schema":{"format":"date-time","type":"string"},"example":"2026-11-01T00:00:00Z"},{"name":"created_before","required":false,"in":"query","description":"Only events created before this instant.","schema":{"format":"date-time","type":"string"},"example":"2026-12-01T00:00:00Z"},{"name":"limit","required":false,"in":"query","description":"Page size.","schema":{"minimum":1,"maximum":100,"default":20,"type":"integer"},"example":20},{"name":"cursor","required":false,"in":"query","description":"The previous page's `pagination.next_cursor`.","schema":{"type":"string"},"example":"eyJjcmVhdGVkX2F0IjoiMjAyNi0xMS0wMVQwMDowMDowMFoifQ"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListWebhookEventsResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"Invalid filter value or cursor.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"`UNAUTHORIZED`: Missing or invalid bearer token. A recipient token is refused with `RECIPIENT_TOKEN_INVALID` (it works only on `/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `notify.webhook.read` (reads) or `notify.webhook.manage` (changes, Send test, replay), or Notify is not enabled for the workspace (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List webhook events, newest first (kept for 30 days).","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.events","method":"list","bind":["workspace_id"],"paginated":true}}},"/v1/workspaces/{workspace_id}/webhooks/events/{event_id}":{"get":{"description":"The event as it was sent (`type`, `data`) and every delivery attempt to every endpoint, with its trigger, HTTP status, error and the start of the response body. Use it to debug a receiver, then `replay` to send it again.","operationId":"WebhooksController_notify_workspace_getEvent","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"event_id","required":true,"in":"path","description":"The event id, `evt_<uuid>`.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEventDetailDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`event_id` is not `evt_<uuid>`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"`UNAUTHORIZED`: Missing or invalid bearer token. A recipient token is refused with `RECIPIENT_TOKEN_INVALID` (it works only on `/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `notify.webhook.read` (reads) or `notify.webhook.manage` (changes, Send test, replay), or Notify is not enabled for the workspace (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"No such event here (`WEBHOOK_EVENT_NOT_FOUND`); events are kept for 30 days.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","WEBHOOK_EVENT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get one webhook event with every delivery attempt.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.events","method":"retrieve","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/webhooks/events/{event_id}/replay":{"post":{"description":"Same body and `webhook-id`, freshly signed, to every switched-on endpoint that receives this event type now, or only to `endpoint_id`. Waits for the receivers (up to 15 s each) and returns the attempts. A success cancels a retry still queued for that endpoint.","operationId":"WebhooksController_notify_workspace_replay","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"event_id","required":true,"in":"path","description":"The event id, `evt_<uuid>`.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"}},{"name":"endpoint_id","required":false,"in":"query","description":"Send to this endpoint only.","schema":{"format":"uuid","type":"string"},"example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"Idempotency-Key","in":"header","description":"Opaque client-generated string (1-256 chars, `[A-Za-z0-9_-:]`) that makes a retry of this request safe for 24 hours. The same key with a different body returns 409 `IDEMPOTENCY_KEY_REUSE`.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplayWebhookEventResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`event_id` is not `evt_<uuid>`, or `endpoint_id` is not a UUID.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"`UNAUTHORIZED`: Missing or invalid bearer token. A recipient token is refused with `RECIPIENT_TOKEN_INVALID` (it works only on `/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"Caller lacks `notify.webhook.read` (reads) or `notify.webhook.manage` (changes, Send test, replay), or Notify is not enabled for the workspace (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"No such event here (`WEBHOOK_EVENT_NOT_FOUND`); events are kept for 30 days. Or No such endpoint here (`WEBHOOK_ENDPOINT_NOT_FOUND`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","WEBHOOK_EVENT_NOT_FOUND","WEBHOOK_ENDPOINT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"`endpoint_id` is switched off or does not receive this event type (`WEBHOOK_ENDPOINT_NOT_SUBSCRIBED`), or Idempotency-Key reused with a different body (`IDEMPOTENCY_KEY_REUSE`). Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","WEBHOOK_ENDPOINT_NOT_SUBSCRIBED","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Send a logged event again, to its endpoints or to one endpoint.","tags":["webhooks"],"x-pc-sdk":{"resource":"webhooks.events","method":"replay","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/recipients/bulk":{"post":{"description":"Upserts each row like `PUT /recipients/{external_id}`, with its devices. Rows are independent: a bad row is reported in `data` with `status: error` and its code, and never fails the rest. Rows are written in order of external id, in transactions of at most 200. At most 20 calls per minute per workspace (20,000 recipients) and 4 MB per request. Each row may carry its complete `preferences` set (as `PUT …/preferences`); a bad cell fails only its row. Send an `Idempotency-Key` so a retried import replays its first result.","operationId":"RecipientsController_bulk","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"Idempotency-Key","in":"header","description":"A key (1..256 chars, [A-Za-z0-9_\\-:]) makes retries safe: a replay returns the original result for 24 h.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUpsertRecipientsDto"}}}},"responses":{"200":{"description":"One result per row.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUpsertRecipientsResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: `recipients` is missing, empty or longer than 1,000.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"`IDEMPOTENCY_KEY_REUSE`: the `Idempotency-Key` was already used with a different body. Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"`RATE_LIMITED`: more than 20 bulk calls in a minute; see `Retry-After`. Per-IP limit: more than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Create or update up to 1,000 recipients","tags":["recipients"],"x-pc-sdk":{"resource":"recipients","method":"bulkIdentify","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/recipients":{"get":{"description":"Newest first. Filter by `email` (case-insensitive), `auth_user_id`, or `updated_since` for an incremental sync. Pass `next_cursor` as `cursor` until `has_more` is false. `include=preferences` adds each recipient's preference cells (an export of consent).","operationId":"RecipientsController_list","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"email","required":false,"in":"query","description":"Only the recipient(s) with this email address (case-insensitive).","schema":{"format":"email","type":"string"}},{"name":"auth_user_id","required":false,"in":"query","description":"Only the recipient linked to this Auth end user.","schema":{"format":"uuid","type":"string"},"example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"updated_since","required":false,"in":"query","description":"Only recipients changed at or after this time (incremental sync).","schema":{"format":"date-time","type":"string"},"example":"2026-11-01T00:00:00Z"},{"name":"include","required":false,"in":"query","description":"`preferences` adds each recipient's preference cells (for an export).","schema":{"$ref":"#/components/schemas/RecipientInclude"},"example":"preferences"},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor for pagination: the `next_cursor` of the previous page.","schema":{"example":"eyJpZCI6IjAxOTMifQ","type":"string"},"example":"eyJjcmVhdGVkX2F0IjoiMjAyNi0xMS0wMVQwMDowMDowMFoifQ"},{"name":"limit","required":false,"in":"query","description":"Page size.","schema":{"minimum":1,"maximum":100,"default":25,"type":"integer"},"example":25}],"responses":{"200":{"description":"A page of recipients.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientListResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR` on a bad filter, or `BAD_REQUEST`: invalid cursor.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List recipients","tags":["recipients"],"x-pc-sdk":{"resource":"recipients","method":"list","bind":["workspace_id"],"paginated":true}}},"/v1/workspaces/{workspace_id}/recipients/{external_id}":{"put":{"description":"Creates the recipient with this external id, or updates it. Fields you leave out keep their stored value; `null` clears one; `properties` is replaced whole. Calling it again with the same body leaves the same recipient, so a retry is safe without an `Idempotency-Key` (the route does not take one).","operationId":"RecipientsController_upsert","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"external_id","required":true,"in":"path","description":"Your user id for the recipient (URL-encoded): 1-255 of `A-Z a-z 0-9 . _ : @ | -`.","schema":{"type":"string","pattern":"^[A-Za-z0-9._:@|-]{1,255}$","example":"user_123"},"example":"user_123"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertRecipientDto"}}}},"responses":{"200":{"description":"Updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"201":{"description":"Created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is invalid (`errors` names it).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"`AUTH_USER_ALREADY_LINKED`: the Auth end user is linked to another recipient of the workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","AUTH_USER_ALREADY_LINKED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Create or update a recipient","tags":["recipients"],"x-pc-sdk":{"resource":"recipients","method":"identify","bind":["workspace_id"],"paginated":false}},"get":{"description":"The profile; devices are under `…/devices`.","operationId":"RecipientsController_retrieve","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"external_id","required":true,"in":"path","description":"Your user id for the recipient (URL-encoded): 1-255 of `A-Z a-z 0-9 . _ : @ | -`.","schema":{"type":"string","pattern":"^[A-Za-z0-9._:@|-]{1,255}$","example":"user_123"},"example":"user_123"}],"responses":{"200":{"description":"The recipient.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`RECIPIENT_NOT_FOUND`: no recipient with this external id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","RECIPIENT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get a recipient","tags":["recipients"],"x-pc-sdk":{"resource":"recipients","method":"retrieve","bind":["workspace_id"],"paginated":false}},"patch":{"description":"Changes the fields you send on an existing recipient (404 when there is none; use `PUT` to create). `null` clears a field.","operationId":"RecipientsController_update","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"external_id","required":true,"in":"path","description":"Your user id for the recipient (URL-encoded): 1-255 of `A-Z a-z 0-9 . _ : @ | -`.","schema":{"type":"string","pattern":"^[A-Za-z0-9._:@|-]{1,255}$","example":"user_123"},"example":"user_123"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRecipientDto"}}}},"responses":{"200":{"description":"Updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is invalid (`errors` names it).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`RECIPIENT_NOT_FOUND`: no recipient with this external id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","RECIPIENT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"`AUTH_USER_ALREADY_LINKED`: the Auth end user is linked to another recipient of the workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","AUTH_USER_ALREADY_LINKED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update a recipient","tags":["recipients"],"x-pc-sdk":{"resource":"recipients","method":"update","bind":["workspace_id"],"paginated":false}},"delete":{"description":"Deletes the recipient and everything Notify holds for it: devices now, and preferences, inbox items and pending deliveries as those land. Delivery history keeps ids and states but loses addresses and rendered text. The data is gone when this returns; the external id can be used again. Email already handed to ProductCraft Mail stays in Mail's message log under Mail's retention.","operationId":"RecipientsController_erase","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"external_id","required":true,"in":"path","description":"Your user id for the recipient (URL-encoded): 1-255 of `A-Z a-z 0-9 . _ : @ | -`.","schema":{"type":"string","pattern":"^[A-Za-z0-9._:@|-]{1,255}$","example":"user_123"},"example":"user_123"}],"responses":{"202":{"description":"Erased.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientErasureDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`RECIPIENT_NOT_FOUND`: no recipient with this external id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","RECIPIENT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Erase a recipient (GDPR)","tags":["recipients"],"x-pc-sdk":{"resource":"recipients","method":"delete","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/recipients/{external_id}/devices":{"post":{"description":"Registers an APNs or FCM token, or a browser web push subscription, for the recipient. Registering a token the recipient already has refreshes it (200). A token registered to another recipient of the workspace moves to this one (a shared device signed in as someone else), at most 3 times per token per UTC day. A recipient keeps at most 20 active devices: registering more marks the least recently seen as `pruned` (`evicted`). The token is stored hashed and encrypted; responses show its last 6 characters only.","operationId":"DevicesController_create","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"Idempotency-Key","in":"header","description":"A key (1..256 chars, [A-Za-z0-9_\\-:]) makes retries safe: a replay returns the original response for 24 h.","required":false,"schema":{"type":"string"}},{"name":"external_id","required":true,"in":"path","description":"Your user id for the recipient (URL-encoded): 1-255 of `A-Z a-z 0-9 . _ : @ | -`.","schema":{"type":"string","pattern":"^[A-Za-z0-9._:@|-]{1,255}$","example":"user_123"},"example":"user_123"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDeviceDto"}}}},"responses":{"200":{"description":"Already registered to this recipient: refreshed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"201":{"description":"Registered (new, or moved from another recipient).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is invalid (`errors` names it).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`RECIPIENT_NOT_FOUND`: no recipient with this external id; create it with `PUT` first.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","RECIPIENT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"`IDEMPOTENCY_KEY_REUSE`: the `Idempotency-Key` was already used with a different body. Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"`DEVICE_TOKEN_INVALID`: the token or subscription is malformed for its channel (or `environment` is missing for APNs); `WEBPUSH_ENDPOINT_NOT_ALLOWED`: the endpoint is not https on a known push service, or resolves to a private address.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY","DEVICE_TOKEN_INVALID","WEBPUSH_ENDPOINT_NOT_ALLOWED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"`DEVICE_MOVE_LIMIT`: the token already moved between recipients 3 times today (UTC); `Retry-After` gives the seconds to midnight UTC. Per-IP limit: more than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED","DEVICE_MOVE_LIMIT"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Register a device","tags":["recipients"],"x-pc-sdk":{"resource":"recipients.devices","method":"create","bind":["workspace_id"],"paginated":false}},"get":{"description":"Most recently seen first: the active ones (at most 20) and any invalid or pruned ones not yet reaped (kept 7 days).","operationId":"DevicesController_list","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"external_id","required":true,"in":"path","description":"Your user id for the recipient (URL-encoded): 1-255 of `A-Z a-z 0-9 . _ : @ | -`.","schema":{"type":"string","pattern":"^[A-Za-z0-9._:@|-]{1,255}$","example":"user_123"},"example":"user_123"}],"responses":{"200":{"description":"The devices.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceListResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`RECIPIENT_NOT_FOUND`: no recipient with this external id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","RECIPIENT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List a recipient's devices","tags":["recipients"],"x-pc-sdk":{"resource":"recipients.devices","method":"list","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/recipients/{external_id}/devices/{device_id}":{"delete":{"description":"Deletes the device (call it when the user signs out on that device). Pushes stop at once.","operationId":"DevicesController_remove","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"device_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the device.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"external_id","required":true,"in":"path","description":"Your user id for the recipient (URL-encoded): 1-255 of `A-Z a-z 0-9 . _ : @ | -`.","schema":{"type":"string","pattern":"^[A-Za-z0-9._:@|-]{1,255}$","example":"user_123"},"example":"user_123"}],"responses":{"204":{"description":"Removed.","headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`RECIPIENT_NOT_FOUND`, or `DEVICE_NOT_FOUND`: the recipient has no device with this id.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","RECIPIENT_NOT_FOUND","DEVICE_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Remove a device","tags":["recipients"],"x-pc-sdk":{"resource":"recipients.devices","method":"delete","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/recipients/{external_id}/preferences":{"get":{"description":"The cells the recipient set, unsubscribe-all, time zone and quiet hours. Anything not set follows the category defaults.","operationId":"PreferencesController_retrieve","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"external_id","required":true,"in":"path","description":"Your user id for the recipient (URL-encoded): 1-255 of `A-Z a-z 0-9 . _ : @ | -`.","schema":{"type":"string","pattern":"^[A-Za-z0-9._:@|-]{1,255}$","example":"user_123"},"example":"user_123"}],"responses":{"200":{"description":"The preferences.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreferenceSetDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`RECIPIENT_NOT_FOUND`: no recipient with this external id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","RECIPIENT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get a recipient's preferences","tags":["recipients"],"x-pc-sdk":{"resource":"recipients.preferences","method":"retrieve","bind":["workspace_id"],"paginated":false}},"put":{"description":"Sets the complete set of cells: cells you leave out are removed. Most specific wins when sending: type × channel, type × `*`, category × channel, category × `*`, all × channel, then the category default. Calling it again with the same body changes nothing.","operationId":"PreferencesController_replace","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"external_id","required":true,"in":"path","description":"Your user id for the recipient (URL-encoded): 1-255 of `A-Z a-z 0-9 . _ : @ | -`.","schema":{"type":"string","pattern":"^[A-Za-z0-9._:@|-]{1,255}$","example":"user_123"},"example":"user_123"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplacePreferencesDto"}}}},"responses":{"200":{"description":"The preferences after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreferenceSetDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a cell is malformed or repeated.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`RECIPIENT_NOT_FOUND`: no recipient with this external id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","RECIPIENT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"`CATEGORY_NOT_FOUND` / `NOTIFICATION_TYPE_NOT_FOUND`: a cell names an unknown key; `PREFERENCE_LOCKED`: the channel is mandatory for that category and cannot be turned off.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY","CATEGORY_NOT_FOUND","NOTIFICATION_TYPE_NOT_FOUND","PREFERENCE_LOCKED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Replace a recipient's preferences","tags":["recipients"],"x-pc-sdk":{"resource":"recipients.preferences","method":"replace","bind":["workspace_id"],"paginated":false}},"patch":{"description":"Sets or removes (`enabled: null`) the cells you send and keeps the others; also sets unsubscribe-all, time zone and quiet hours.","operationId":"PreferencesController_update","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"external_id","required":true,"in":"path","description":"Your user id for the recipient (URL-encoded): 1-255 of `A-Z a-z 0-9 . _ : @ | -`.","schema":{"type":"string","pattern":"^[A-Za-z0-9._:@|-]{1,255}$","example":"user_123"},"example":"user_123"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePreferencesDto"}}}},"responses":{"200":{"description":"The preferences after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreferenceSetDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a cell is malformed or repeated.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`RECIPIENT_NOT_FOUND`: no recipient with this external id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","RECIPIENT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"`CATEGORY_NOT_FOUND` / `NOTIFICATION_TYPE_NOT_FOUND`: a cell names an unknown key; `PREFERENCE_LOCKED`: the channel is mandatory for that category and cannot be turned off.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY","CATEGORY_NOT_FOUND","NOTIFICATION_TYPE_NOT_FOUND","PREFERENCE_LOCKED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update a recipient's preferences","tags":["recipients"],"x-pc-sdk":{"resource":"recipients.preferences","method":"update","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/categories":{"post":{"description":"At most 50 categories per workspace during the preview. Channels left out of `defaults` are `on`.","operationId":"CategoriesController_create","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"Idempotency-Key","in":"header","description":"A key (1..256 chars, [A-Za-z0-9_\\-:]) makes retries safe: a replay returns the original response for 24 h.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCategoryDto"}}}},"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is invalid (`errors` names it).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"`CATEGORY_KEY_TAKEN`: the key is in use; `IDEMPOTENCY_KEY_REUSE`: the `Idempotency-Key` was used with a different body. Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","CATEGORY_KEY_TAKEN","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"`LIMIT_EXCEEDED`: the workspace already has 50 categories.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY","LIMIT_EXCEEDED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Create a category","tags":["categories"],"x-pc-sdk":{"resource":"categories","method":"create","bind":["workspace_id"],"paginated":false}},"get":{"description":"By `ordinal`, then oldest first.","operationId":"CategoriesController_list","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor: the `next_cursor` of the previous page.","schema":{"type":"string"},"example":"eyJjcmVhdGVkX2F0IjoiMjAyNi0xMS0wMVQwMDowMDowMFoifQ"},{"name":"limit","required":false,"in":"query","description":"Page size.","schema":{"minimum":1,"maximum":100,"default":25,"type":"integer"},"example":25}],"responses":{"200":{"description":"A page of categories.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryListResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR` on a bad parameter, or `BAD_REQUEST`: invalid cursor.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List categories","tags":["categories"],"x-pc-sdk":{"resource":"categories","method":"list","bind":["workspace_id"],"paginated":true}}},"/v1/workspaces/{workspace_id}/categories/{category_id}":{"get":{"operationId":"CategoriesController_retrieve","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"category_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the category.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"200":{"description":"The category.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`CATEGORY_NOT_FOUND`: no category with this id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","CATEGORY_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get a category","tags":["categories"],"x-pc-sdk":{"resource":"categories","method":"retrieve","bind":["workspace_id"],"paginated":false}},"patch":{"description":"Changes the fields you send. `defaults` merges per channel. The key cannot change.","operationId":"CategoriesController_update","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"category_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the category.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCategoryDto"}}}},"responses":{"200":{"description":"Updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is invalid (`errors` names it).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`CATEGORY_NOT_FOUND`: no category with this id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","CATEGORY_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update a category","tags":["categories"],"x-pc-sdk":{"resource":"categories","method":"update","bind":["workspace_id"],"paginated":false}},"delete":{"description":"Only when no notification type uses it. Recipients' preferences for the category are deleted with it.","operationId":"CategoriesController_remove","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"category_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the category.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"204":{"description":"Deleted.","headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`CATEGORY_NOT_FOUND`: no category with this id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","CATEGORY_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"`CATEGORY_IN_USE`: a notification type still uses the category.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","CATEGORY_IN_USE"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Delete a category","tags":["categories"],"x-pc-sdk":{"resource":"categories","method":"delete","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/notification-types":{"post":{"description":"At most 200 types per workspace during the preview. Add templates with `PUT …/templates/{channel}/{locale}`, or create the type and its templates in one call with `PUT /notification-types/by-key/{key}`.","operationId":"TypesController_create","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"Idempotency-Key","in":"header","description":"A key (1..256 chars, [A-Za-z0-9_\\-:]) makes retries safe: a replay returns the original response for 24 h.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateNotificationTypeDto"}}}},"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationTypeDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is invalid (`errors` names it).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"`NOTIFICATION_TYPE_KEY_TAKEN`: the key is in use; `IDEMPOTENCY_KEY_REUSE`: the `Idempotency-Key` was used with a different body. Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","NOTIFICATION_TYPE_KEY_TAKEN","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"`CATEGORY_NOT_FOUND`: no category with `category_key`; `VALIDATION_ERROR`: the routing is invalid; `LIMIT_EXCEEDED`: the workspace already has 200 types.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY","CATEGORY_NOT_FOUND","VALIDATION_ERROR","LIMIT_EXCEEDED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Create a notification type","tags":["notification-types"],"x-pc-sdk":{"resource":"notificationTypes","method":"create","bind":["workspace_id"],"paginated":false}},"get":{"description":"Newest first; archived types only with `include_archived=true`.","operationId":"TypesController_list","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"category","required":false,"in":"query","description":"Only types of this category (key).","schema":{"type":"string"}},{"name":"include_archived","required":false,"in":"query","description":"Include archived types.","schema":{"default":false,"type":"string"},"example":false},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor: the `next_cursor` of the previous page.","schema":{"type":"string"},"example":"eyJjcmVhdGVkX2F0IjoiMjAyNi0xMS0wMVQwMDowMDowMFoifQ"},{"name":"limit","required":false,"in":"query","description":"Page size.","schema":{"minimum":1,"maximum":100,"default":25,"type":"integer"},"example":25}],"responses":{"200":{"description":"A page of types.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationTypeListResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR` on a bad parameter, or `BAD_REQUEST`: invalid cursor.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List notification types","tags":["notification-types"],"x-pc-sdk":{"resource":"notificationTypes","method":"list","bind":["workspace_id"],"paginated":true}}},"/v1/workspaces/{workspace_id}/notification-types/by-key/{key}":{"put":{"description":"Types as code: sets the type and its complete template set in one transaction (the shape `GET /export` returns per type). Fields left out take their defaults; templates not listed are deleted; unchanged templates keep their version. Applying the same body again changes nothing, so the call is safe to retry without an `Idempotency-Key`. Use it to promote types from a staging workspace. An archived type stays archived.","operationId":"TypesController_replaceByKey","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"key","required":true,"in":"path","description":"The type key.","schema":{"type":"string","pattern":"^[a-z][a-z0-9_.-]{0,99}$","example":"invoice.paid"},"example":"invoice.paid"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplaceNotificationTypeDto"}}}},"responses":{"200":{"description":"Replaced (or unchanged).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationTypeDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"201":{"description":"Created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationTypeDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field or template entry is invalid.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"`CATEGORY_NOT_FOUND`, `VALIDATION_ERROR` (routing), `TEMPLATE_INVALID`, `UNSAFE_URL` or `LIMIT_EXCEEDED` (200 types, 32 KB per template).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY","CATEGORY_NOT_FOUND","VALIDATION_ERROR","TEMPLATE_INVALID","UNSAFE_URL","LIMIT_EXCEEDED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Create or replace a notification type by key","tags":["notification-types"],"x-pc-sdk":{"resource":"notificationTypes","method":"replaceByKey","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/notification-types/{type_id}":{"get":{"operationId":"TypesController_retrieve","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"type_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the type.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"200":{"description":"The type.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationTypeDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`NOTIFICATION_TYPE_NOT_FOUND`: no notification type with this id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","NOTIFICATION_TYPE_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get a notification type","tags":["notification-types"],"x-pc-sdk":{"resource":"notificationTypes","method":"retrieve","bind":["workspace_id"],"paginated":false}},"patch":{"description":"Changes the fields you send; `routing` is replaced whole. The key cannot change. Changing `critical` is audited with the old and new value.","operationId":"TypesController_update","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"type_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the type.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateNotificationTypeDto"}}}},"responses":{"200":{"description":"Updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationTypeDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is invalid.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`NOTIFICATION_TYPE_NOT_FOUND`: no notification type with this id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","NOTIFICATION_TYPE_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"`CATEGORY_NOT_FOUND`: no category with `category_key`; `VALIDATION_ERROR`: the routing is invalid.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY","CATEGORY_NOT_FOUND","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update a notification type","tags":["notification-types"],"x-pc-sdk":{"resource":"notificationTypes","method":"update","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/notification-types/{type_id}/archive":{"post":{"description":"Archived types cannot be sent (`NOTIFICATION_TYPE_ARCHIVED`); history stays. Archiving twice is a no-op.","operationId":"TypesController_archive","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"type_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the type.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"200":{"description":"Archived.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationTypeDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`NOTIFICATION_TYPE_NOT_FOUND`: no notification type with this id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","NOTIFICATION_TYPE_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Archive a notification type","tags":["notification-types"],"x-pc-sdk":{"resource":"notificationTypes","method":"archive","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/notification-types/{type_id}/preview":{"post":{"description":"Renders every routed channel with sample `data`, or as a real `recipient` (its locale: exact, then language, then the type default; its fields as `recipient.*`). Reports missing variables per channel and variables some channels use but others do not. Sends nothing.","operationId":"TypesController_preview","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"type_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the type.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewDto"}}}},"responses":{"200":{"description":"The rendered channels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is invalid.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`NOTIFICATION_TYPE_NOT_FOUND`, or `RECIPIENT_NOT_FOUND`: no recipient with this external id.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","NOTIFICATION_TYPE_NOT_FOUND","RECIPIENT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Preview a notification type","tags":["notification-types"],"x-pc-sdk":{"resource":"notificationTypes","method":"preview","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/notification-types/{type_id}/templates/{channel}/{locale}":{"get":{"operationId":"TypesController_getTemplate","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"type_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the type.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"channel","required":true,"in":"path","description":"Template channel.","schema":{"enum":["inbox","email","push","webpush"],"type":"string"},"example":"inbox"},{"name":"locale","required":true,"in":"path","description":"Template locale (BCP 47).","schema":{"type":"string","example":"pt-PT"},"example":"pt-PT"}],"responses":{"200":{"description":"The template.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: unknown channel or malformed locale.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`NOTIFICATION_TYPE_NOT_FOUND`, or `TEMPLATE_NOT_FOUND`: the type has no template for this channel and locale.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","NOTIFICATION_TYPE_NOT_FOUND","TEMPLATE_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get a template","tags":["notification-types"],"x-pc-sdk":{"resource":"notificationTypes.templates","method":"retrieve","bind":["workspace_id"],"paginated":false}},"put":{"description":"Sets the template of one channel and locale. Every change is kept as a version (the last 20); writing the same body again changes nothing. For `email`, the body names a Mail template (`mail_template_name`); setup status reports one that does not exist.","operationId":"TypesController_putTemplate","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"type_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the type.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"channel","required":true,"in":"path","description":"Template channel.","schema":{"enum":["inbox","email","push","webpush"],"type":"string"},"example":"inbox"},{"name":"locale","required":true,"in":"path","description":"Template locale (BCP 47).","schema":{"type":"string","example":"pt-PT"},"example":"pt-PT"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutTemplateDto"}}}},"responses":{"200":{"description":"Replaced (or unchanged).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"201":{"description":"Created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: unknown channel or malformed locale.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`NOTIFICATION_TYPE_NOT_FOUND`: no notification type with this id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","NOTIFICATION_TYPE_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"`TEMPLATE_INVALID`: a field is missing, unknown or does not parse (the message names it); `UNSAFE_URL`: a URL field is not https; `LIMIT_EXCEEDED`: the template is over 32 KB or the type has 100 templates.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY","TEMPLATE_INVALID","UNSAFE_URL","LIMIT_EXCEEDED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Create or replace a template","tags":["notification-types"],"x-pc-sdk":{"resource":"notificationTypes.templates","method":"update","bind":["workspace_id"],"paginated":false}},"delete":{"description":"Deletes the template and its versions. Sends to that channel and locale fall back along the locale chain.","operationId":"TypesController_deleteTemplate","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"type_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the type.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"channel","required":true,"in":"path","description":"Template channel.","schema":{"enum":["inbox","email","push","webpush"],"type":"string"},"example":"inbox"},{"name":"locale","required":true,"in":"path","description":"Template locale (BCP 47).","schema":{"type":"string","example":"pt-PT"},"example":"pt-PT"}],"responses":{"204":{"description":"Deleted.","headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: unknown channel or malformed locale.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`NOTIFICATION_TYPE_NOT_FOUND`, or `TEMPLATE_NOT_FOUND`: the type has no template for this channel and locale.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","NOTIFICATION_TYPE_NOT_FOUND","TEMPLATE_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Delete a template","tags":["notification-types"],"x-pc-sdk":{"resource":"notificationTypes.templates","method":"delete","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/notification-types/{type_id}/templates/{channel}/{locale}/versions":{"get":{"description":"The last 20 versions, newest first.","operationId":"TypesController_versions","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"type_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the type.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"channel","required":true,"in":"path","description":"Template channel.","schema":{"enum":["inbox","email","push","webpush"],"type":"string"},"example":"inbox"},{"name":"locale","required":true,"in":"path","description":"Template locale (BCP 47).","schema":{"type":"string","example":"pt-PT"},"example":"pt-PT"}],"responses":{"200":{"description":"The versions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateVersionListDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: unknown channel or malformed locale.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`NOTIFICATION_TYPE_NOT_FOUND`, or `TEMPLATE_NOT_FOUND`: the type has no template for this channel and locale.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","NOTIFICATION_TYPE_NOT_FOUND","TEMPLATE_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List a template's versions","tags":["notification-types"],"x-pc-sdk":{"resource":"notificationTypes.templates","method":"versions","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/notification-types/{type_id}/templates/{channel}/{locale}/revert":{"post":{"description":"Restores an earlier version as a new version (audited as `notify.template.reverted`).","operationId":"TypesController_revert","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"type_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the type.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"channel","required":true,"in":"path","description":"Template channel.","schema":{"enum":["inbox","email","push","webpush"],"type":"string"},"example":"inbox"},{"name":"locale","required":true,"in":"path","description":"Template locale (BCP 47).","schema":{"type":"string","example":"pt-PT"},"example":"pt-PT"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevertTemplateDto"}}}},"responses":{"200":{"description":"The template after the revert.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: unknown channel, malformed locale or version.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`NOTIFICATION_TYPE_NOT_FOUND`, `TEMPLATE_NOT_FOUND`, or `TEMPLATE_VERSION_NOT_FOUND`: that version is not kept.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","NOTIFICATION_TYPE_NOT_FOUND","TEMPLATE_NOT_FOUND","TEMPLATE_VERSION_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Revert a template","tags":["notification-types"],"x-pc-sdk":{"resource":"notificationTypes.templates","method":"revert","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/export":{"get":{"description":"Categories, notification types with every template, and settings, in the shapes the create and `PUT /notification-types/by-key/{key}` calls accept: apply it to another workspace to promote a setup. Recipients are exported with `GET /recipients?include=preferences`.","operationId":"ExportController_export","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"200":{"description":"The configuration.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Export the configuration","tags":["export"],"x-pc-sdk":{"resource":"export","method":"retrieve","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/notifications":{"post":{"description":"Notifies up to 1,000 recipients with one notification type, across the type's channels (in-app inbox, email, push, web push), honouring each recipient's preferences. Returns 202 as soon as the send is stored; delivery happens in the background (see `GET /deliveries`). Unknown external ids do not fail the call: they are listed in `skipped` (`create_missing: true` creates them instead); the call fails only when no recipient can be notified. An `Idempotency-Key` is **required** (a deterministic one such as `invoice.paid:inv_123` is best): the same key and body return the same notification, even when retried much later; the same key with a different body is a 409. `dry_run: true` returns the plan per recipient and channel with the reason for every skip, and stores nothing (200, no key needed). Limits: 3,000 recipients per minute per workspace; 30 notifications per minute per recipient (critical types have a separate 30), beyond which deliveries are skipped as `rate_limited`.","operationId":"NotificationsController_send","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"Idempotency-Key","in":"header","description":"Required unless `dry_run` is true. 1..256 chars of [A-Za-z0-9_\\-:]; the same key with the same body returns the original notification.","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendNotificationDto"}}}},"responses":{"200":{"description":"`dry_run`: the plan; nothing stored or sent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DryRunResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"202":{"description":"Accepted (or scheduled).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendNotificationResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`IDEMPOTENCY_KEY_REQUIRED`: no `Idempotency-Key`; `TOO_MANY_RECIPIENTS`: over 1,000; `VALIDATION_ERROR`: a field is invalid.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR","IDEMPOTENCY_KEY_REQUIRED","TOO_MANY_RECIPIENTS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`NOTIFICATION_TYPE_NOT_FOUND`: no type with this key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","NOTIFICATION_TYPE_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"`IDEMPOTENCY_KEY_REUSE`: the key was used with a different body. Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"413":{"description":"`DATA_TOO_LARGE`: `data` is over 16 KB.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["PAYLOAD_TOO_LARGE","DATA_TOO_LARGE"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"`RECIPIENT_NOT_FOUND`: no recipient can be notified (`details.skipped` lists why); `NOTIFICATION_TYPE_ARCHIVED`; `TEMPLATE_MISSING`: no template for any requested channel; `SEND_AT_OUT_OF_RANGE`; `UNSAFE_URL`; `VALIDATION_ERROR`: a channel outside the type's routing.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY","RECIPIENT_NOT_FOUND","NOTIFICATION_TYPE_ARCHIVED","TEMPLATE_MISSING","SEND_AT_OUT_OF_RANGE","UNSAFE_URL","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"`RATE_LIMITED`: over 3,000 recipients a minute in this workspace; see `Retry-After`. Per-IP limit: more than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Send a notification","tags":["notifications"],"x-pc-sdk":{"resource":"notifications","method":"send","bind":["workspace_id"],"paginated":false}},"get":{"description":"Newest first. Filter by `type`, `tag` (`key:value` or `key`), `tenant` or `cancellation_key`. Pass `next_cursor` as `cursor` until `has_more` is false.","operationId":"NotificationsController_list","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"type","required":false,"in":"query","description":"Only this notification type key.","schema":{"example":"invoice.paid","type":"string"},"example":"invoice.paid"},{"name":"tag","required":false,"in":"query","description":"Only notifications with this tag: `key:value`, or `key` for any value.","schema":{"example":"invoice:inv_123","type":"string"},"example":"invoice:inv_123"},{"name":"tenant","required":false,"in":"query","description":"Only this tenant.","schema":{"example":"org_42","type":"string"},"example":"org_42"},{"name":"cancellation_key","required":false,"in":"query","description":"Only notifications sent with this cancellation key.","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor for pagination: the `next_cursor` of the previous page.","schema":{"type":"string"},"example":"eyJjcmVhdGVkX2F0IjoiMjAyNi0xMS0wMVQwMDowMDowMFoifQ"},{"name":"limit","required":false,"in":"query","description":"Page size.","schema":{"minimum":1,"maximum":100,"default":25,"type":"integer"},"example":25}],"responses":{"200":{"description":"A page of notifications.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationListResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR` on a bad filter, or `BAD_REQUEST`: invalid cursor.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List notifications (message log)","tags":["notifications"],"x-pc-sdk":{"resource":"notifications","method":"list","bind":["workspace_id"],"paginated":true}}},"/v1/workspaces/{workspace_id}/notifications/cancel":{"post":{"description":"Cancels every live notification sent with this `cancellation_key`: deliveries still pending, scheduled or retrying end `skipped: cancelled`. Already delivered messages stay delivered (retract inbox items with `…/retract`). Safe to repeat.","operationId":"NotificationsController_cancelByKey","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelByKeyDto"}}}},"responses":{"200":{"description":"What was cancelled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelByKeyResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: `cancellation_key` is missing.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Cancel notifications by cancellation key","tags":["notifications"],"x-pc-sdk":{"resource":"notifications","method":"cancelByKey","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/notifications/{notification_id}":{"get":{"description":"With its data and the number of deliveries per channel and state.","operationId":"NotificationsController_retrieve","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"notification_id","required":true,"in":"path","description":"The notification id.","schema":{"type":"string","format":"uuid"},"example":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a01"}],"responses":{"200":{"description":"The notification.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`NOTIFICATION_NOT_FOUND`: no notification with this id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","NOTIFICATION_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get a notification","tags":["notifications"],"x-pc-sdk":{"resource":"notifications","method":"retrieve","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/notifications/{notification_id}/cancel":{"post":{"description":"Deliveries still pending, scheduled or retrying end `skipped: cancelled`, and recipients not planned yet are never notified. Delivered messages stay (retract inbox items with `…/retract`). Safe to repeat.","operationId":"NotificationsController_cancel","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"notification_id","required":true,"in":"path","description":"The notification id.","schema":{"type":"string","format":"uuid"},"example":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a01"}],"responses":{"200":{"description":"The cancelled notification.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`NOTIFICATION_NOT_FOUND`: no notification with this id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","NOTIFICATION_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Cancel a notification","tags":["notifications"],"x-pc-sdk":{"resource":"notifications","method":"cancel","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/notifications/{notification_id}/retract":{"post":{"description":"Archives the notification's inbox items for every recipient (for example once an invite is accepted) and updates their unread counts; open inboxes drop the items. Does not cancel other channels (use `…/cancel`). Safe to repeat.","operationId":"NotificationsController_retract","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"notification_id","required":true,"in":"path","description":"The notification id.","schema":{"type":"string","format":"uuid"},"example":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a01"}],"responses":{"200":{"description":"Retracted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetractResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`NOTIFICATION_NOT_FOUND`: no notification with this id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","NOTIFICATION_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Retract a notification from inboxes","tags":["notifications"],"x-pc-sdk":{"resource":"notifications","method":"retract","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/deliveries":{"get":{"description":"Newest first. Filter by `notification_id`, `recipient` (external id), `state` or `channel`. Every skip, retry and failure carries a `reason` with an `explanation`.","operationId":"DeliveriesController_list","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"notification_id","required":false,"in":"query","description":"Only this notification.","schema":{"format":"uuid","type":"string"},"example":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a01"},{"name":"recipient","required":false,"in":"query","description":"Only this recipient (external id).","schema":{"example":"user_123","type":"string"},"example":"user_123"},{"name":"state","required":false,"in":"query","description":"Only this state.","schema":{"$ref":"#/components/schemas/DeliveryState"},"example":"pending"},{"name":"channel","required":false,"in":"query","description":"Only this channel.","schema":{"$ref":"#/components/schemas/NotifyChannel"},"example":"inbox"},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor for pagination: the `next_cursor` of the previous page.","schema":{"type":"string"},"example":"eyJjcmVhdGVkX2F0IjoiMjAyNi0xMS0wMVQwMDowMDowMFoifQ"},{"name":"limit","required":false,"in":"query","description":"Page size.","schema":{"minimum":1,"maximum":100,"default":25,"type":"integer"},"example":25}],"responses":{"200":{"description":"A page of deliveries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryListResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR` on a bad filter, or `BAD_REQUEST`: invalid cursor.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List deliveries","tags":["deliveries"],"x-pc-sdk":{"resource":"deliveries","method":"list","bind":["workspace_id"],"paginated":true}}},"/v1/workspaces/{workspace_id}/deliveries/{delivery_id}":{"get":{"description":"With every provider attempt (status, code, duration; no message content).","operationId":"DeliveriesController_retrieve","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"delivery_id","required":true,"in":"path","description":"The delivery id.","schema":{"type":"string","format":"uuid"},"example":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a02"}],"responses":{"200":{"description":"The delivery.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryDetailDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`DELIVERY_NOT_FOUND`: no delivery with this id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","DELIVERY_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get a delivery","tags":["deliveries"],"x-pc-sdk":{"resource":"deliveries","method":"retrieve","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/recipients/{external_id}/inbox":{"get":{"description":"What the recipient sees in their in-app inbox, newest first, with their unseen and unread counts. `status` picks active (default), archived or all items. Read-only: recipients mark items through the client API.","operationId":"RecipientMessagesController_inbox","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"status","required":false,"in":"query","description":"`active` (not archived), `archived` or `all`.","schema":{"$ref":"#/components/schemas/InboxItemStatusFilter"},"example":"active"},{"name":"tenant","required":false,"in":"query","description":"Only this tenant.","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor for pagination: the `next_cursor` of the previous page.","schema":{"type":"string"},"example":"eyJjcmVhdGVkX2F0IjoiMjAyNi0xMS0wMVQwMDowMDowMFoifQ"},{"name":"limit","required":false,"in":"query","description":"Page size.","schema":{"minimum":1,"maximum":100,"default":25,"type":"integer"},"example":25},{"name":"external_id","required":true,"in":"path","description":"Your user id for the recipient (URL-encoded): 1-255 of `A-Z a-z 0-9 . _ : @ | -`.","schema":{"type":"string","pattern":"^[A-Za-z0-9._:@|-]{1,255}$","example":"user_123"},"example":"user_123"}],"responses":{"200":{"description":"A page of inbox items.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboxListResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR` on a bad filter, or `BAD_REQUEST`: invalid cursor.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`RECIPIENT_NOT_FOUND`: no recipient with this external id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","RECIPIENT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List a recipient's inbox items","tags":["recipients"],"x-pc-sdk":{"resource":"recipients.inbox","method":"list","bind":["workspace_id"],"paginated":true}}},"/v1/workspaces/{workspace_id}/recipients/{external_id}/timeline":{"get":{"description":"Every delivery to this recipient across notifications, newest first, with the reason for each skip or failure, the device's state and its last successful delivery; the first page also lists device token moves in and out of the recipient.","operationId":"RecipientMessagesController_timeline","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor for pagination: the `next_cursor` of the previous page.","schema":{"type":"string"},"example":"eyJjcmVhdGVkX2F0IjoiMjAyNi0xMS0wMVQwMDowMDowMFoifQ"},{"name":"limit","required":false,"in":"query","description":"Page size.","schema":{"minimum":1,"maximum":100,"default":25,"type":"integer"},"example":25},{"name":"external_id","required":true,"in":"path","description":"Your user id for the recipient (URL-encoded): 1-255 of `A-Z a-z 0-9 . _ : @ | -`.","schema":{"type":"string","pattern":"^[A-Za-z0-9._:@|-]{1,255}$","example":"user_123"},"example":"user_123"}],"responses":{"200":{"description":"A page of the timeline.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimelineResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`BAD_REQUEST`: invalid cursor.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`RECIPIENT_NOT_FOUND`: no recipient with this external id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","RECIPIENT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get a recipient's delivery timeline","tags":["recipients"],"x-pc-sdk":{"resource":"recipients","method":"timeline","bind":["workspace_id"],"paginated":true}}},"/v1/workspaces/{workspace_id}/settings":{"get":{"description":"Email on/off and, after an automatic pause, when and why email was paused; the default time zone and quiet hours for recipients without their own.","operationId":"SettingsController_retrieve","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"200":{"description":"The settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotifySettingsDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get Notify settings","tags":["settings"],"x-pc-sdk":{"resource":"settings","method":"retrieve","bind":["workspace_id"],"paginated":false}},"patch":{"description":"Turn email on or off, set the default time zone and quiet hours (`null` clears either), replace the browser origins allowed on the client API (`client_origins`), or resume email after an automatic pause with `{ \"email_paused\": false }`. Resuming starts a fresh pause window: only emails sent after it count. Fix the cause first (where the addresses come from, whether people expect the email), or the next complaints pause it again. Calling it again with the same body changes nothing.","operationId":"SettingsController_update","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateNotifySettingsDto"}}}},"responses":{"200":{"description":"The settings after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotifySettingsDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is invalid (`errors` names it).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update Notify settings","tags":["settings"],"x-pc-sdk":{"resource":"settings","method":"update","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/setup-status":{"get":{"description":"Rows, in order: `notification_type` (not_set until an active type exists), `email` (attention while email is off, blocked while Notify has paused it), and, once a type uses email, `mail_sender` (the Mail workspace default sender: pass when its domain is verified) and `email_templates` (every Mail template name your types use exists in Mail). Notify sends email through ProductCraft Mail and holds no credential for it: Mail is read with your own access, so a key without Mail read access shows those rows as `attention`. `complete` is true once no row is `not_set` or `blocked`. Each `fix.href` is a console path. Computed on every request.","operationId":"SetupStatusController_get","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"200":{"description":"The checklist, computed now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetupStatusDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks `notify.channel.read`, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get the setup checklist for Notify: what works, what is missing, and where to fix it.","tags":["setup-status"],"x-pc-sdk":{"resource":"setupStatus","method":"retrieve","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/recipient-tokens":{"post":{"description":"Returns a short-lived token (default 1 hour, at most 24 hours) that lets one recipient read their inbox, register devices, manage their preferences and open the real-time stream through the client API (`/v1/client/…`). Mint it on your backend with your API key after you have signed the user in, and hand it to the app or browser: your secret key never ships in an app. A recipient that does not exist yet is created (with the `upsert` fields if given). Limited to 3,000 mints per minute per workspace: cache a token per user for most of its lifetime. Not idempotent by design (each call returns a fresh token), so it takes no `Idempotency-Key`.","operationId":"RecipientTokensController_create","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRecipientTokenDto"}}}},"responses":{"201":{"description":"The token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientTokenDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is invalid (`errors` names it).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"`RECIPIENT_ERASED`: the recipient was erased in the last 24 hours (its linked Auth user was deleted).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","RECIPIENT_ERASED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"`RATE_LIMITED`: over the mint limit; `Retry-After` gives the seconds to wait. Per-IP limit: more than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Mint a recipient token","tags":["recipient-tokens"],"x-pc-sdk":{"resource":"recipientTokens","method":"mint","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/recipients/{external_id}/revoke-tokens":{"post":{"description":"Every recipient token minted for this recipient so far stops working on its next call (`401 RECIPIENT_TOKEN_INVALID`), open real-time streams included. Tokens you mint afterwards work. Use it when the user signs out everywhere or you suspect a token leaked. Calling it again is harmless.","operationId":"RecipientTokensController_revoke","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"external_id","required":true,"in":"path","description":"Your user id for the recipient (URL-encoded): 1-255 of `A-Z a-z 0-9 . _ : @ | -`.","schema":{"type":"string","pattern":"^[A-Za-z0-9._:@|-]{1,255}$","example":"user_123"},"example":"user_123"}],"responses":{"204":{"description":"Revoked.","headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`RECIPIENT_NOT_FOUND`: no recipient with this external id in this workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","RECIPIENT_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Revoke a recipient's tokens","tags":["recipient-tokens"],"x-pc-sdk":{"resource":"recipients","method":"revokeTokens","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/settings/revoke-all-tokens":{"post":{"description":"Every recipient token of the workspace stops working on its next call (`401 RECIPIENT_TOKEN_INVALID`), open real-time streams included; your apps get a new token from your backend. Use it after a suspected leak of many tokens.","operationId":"RecipientTokensController_revokeAll","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"204":{"description":"Revoked.","headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Revoke every recipient token","tags":["recipient-tokens"],"x-pc-sdk":{"resource":"settings","method":"revokeAllTokens","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/channels":{"get":{"description":"Whether each channel (inbox, email, push, web push) can deliver now, and the metadata of its credentials. Never returns a secret: credentials show a fingerprint only.","operationId":"ChannelsController_list","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"200":{"description":"Every channel.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelListResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List channels","tags":["channels"],"x-pc-sdk":{"resource":"channels","method":"list","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/channels/webpush":{"get":{"description":"The VAPID public key browsers subscribe with (`applicationServerKey`), or `configured: false` before one is generated or imported.","operationId":"ChannelsController_webPush","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"200":{"description":"The web push channel.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebPushChannelDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get the web push public key","tags":["channels"],"x-pc-sdk":{"resource":"channels.webpush","method":"retrieve","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/channels/webpush/vapid-keys":{"post":{"description":"Generates a P-256 key pair for web push (RFC 8292) and returns its public key; the private key is stored encrypted and never returned. If the workspace already has a key, this rotates it and needs `confirm: \"rotate\"`: every existing browser subscription stops working and every web push device is marked `invalid` (`vapid_rotated`) until the browser subscribes again with the new key.","operationId":"ChannelsController_generate","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"Idempotency-Key","in":"header","description":"A key (1..256 chars, [A-Za-z0-9_\\-:]) makes retries safe: a replay returns the original response for 24 h.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateVapidKeysDto"}}}},"responses":{"201":{"description":"The new active key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VapidKeyDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is invalid (`errors` names it).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"`CONFIRMATION_REQUIRED`: the workspace already has a VAPID key and the body has no `confirm: \"rotate\"`; `CONFLICT`: another request changed the key at the same time; `IDEMPOTENCY_KEY_REUSE`: the `Idempotency-Key` was used with a different body. Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","CONFIRMATION_REQUIRED","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Generate the web push VAPID key","tags":["channels"],"x-pc-sdk":{"resource":"channels.webpush","method":"generateVapidKeys","bind":["workspace_id"],"paginated":false}},"put":{"description":"Imports an existing VAPID key pair (for example from another push provider) so subscriptions made with it keep working. The pair is checked to be a matching P-256 pair. The private key is stored encrypted and never returned. Replacing a different existing key needs `confirm: \"rotate\"` (see generate); importing the active pair again changes nothing.","operationId":"ChannelsController_import","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportVapidKeysDto"}}}},"responses":{"200":{"description":"The active key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VapidKeyDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is invalid (`errors` names it).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"`CONFIRMATION_REQUIRED`: a different key is active and the body has no `confirm: \"rotate\"`; `CONFLICT`: another request changed the key at the same time.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","CONFIRMATION_REQUIRED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"`CREDENTIAL_INVALID`: the keys are not base64url P-256 keys, or they are not a matching pair.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY","CREDENTIAL_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Import a web push VAPID key","tags":["channels"],"x-pc-sdk":{"resource":"channels.webpush","method":"importVapidKeys","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/channels/apns/credentials":{"put":{"description":"Uploads an APNs authentication key (`.p8`, token auth) for one bundle id. You still create the key in your Apple developer account; Notify validates it with Apple before activating it (a background push to a placeholder token that reaches no device), stores it encrypted and never returns it. An upload replaces the active or invalid key of the same bundle id whose environment overlaps (`replaced_credential_ids`), and resumes push for that bundle id if Apple had refused the previous key. Uploading the active key again unchanged only re-validates it. Nothing is stored when Apple refuses the key or cannot be reached.","operationId":"ChannelsController_uploadApns","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"Idempotency-Key","in":"header","description":"A key (1..256 chars, [A-Za-z0-9_\\-:]) makes retries safe: a replay returns the original response for 24 h.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadApnsCredentialDto"}}}},"responses":{"200":{"description":"The active key for the bundle id (metadata only).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApnsCredentialDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is malformed (`errors` names it).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"`CONFLICT`: another upload for the same bundle id ran at the same time; `IDEMPOTENCY_KEY_REUSE`: the `Idempotency-Key` was used with a different body. Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"`CREDENTIAL_INVALID`: the `.p8` is not a P-256 private key, or Apple rejected it (wrong `key_id`/`team_id`, revoked key); `CREDENTIAL_MISMATCH`: Apple accepted the key but not for this `bundle_id`. Nothing was stored.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY","CREDENTIAL_INVALID","CREDENTIAL_MISMATCH"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"502":{"description":"`BAD_GATEWAY`: Apple could not be reached or is throttling; nothing was stored, retry.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_GATEWAY"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Upload an APNs key","tags":["channels"],"x-pc-sdk":{"resource":"channels.apns","method":"uploadCredential","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/channels/apns/credentials/{credential_id}":{"delete":{"description":"Deletes an APNs credential and erases its key immediately. Push to devices of its bundle id is skipped (`credential_invalid`) until another key is uploaded.","operationId":"ChannelsController_deleteApns","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"credential_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the credential.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"204":{"description":"Deleted.","headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`CREDENTIAL_NOT_FOUND`: no APNs credential with this id in the workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","CREDENTIAL_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Delete an APNs key","tags":["channels"],"x-pc-sdk":{"resource":"channels.apns","method":"deleteCredential","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/channels/fcm/credentials":{"put":{"description":"Uploads the JSON key of a Google service account for one Firebase project (FCM HTTP v1). You still create the service account and its key in your Google Cloud project; Notify validates it with Google before activating it (an OAuth token request and a `validate_only` message to a placeholder token that reaches no device), stores the private key encrypted and never returns it. Use a dedicated service account with only the \"Firebase Cloud Messaging API Admin\" role: the Firebase Admin SDK account (`firebase-adminsdk-*`) is accepted with a `warnings` entry because it can do far more than send push. An upload replaces the active or invalid credential of the same project (`replaced_credential_ids`) and resumes push for that project if Google had refused the previous one. Uploading the active account again unchanged only re-validates it. Nothing is stored when Google refuses the account or cannot be reached.","operationId":"ChannelsController_uploadFcm","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"Idempotency-Key","in":"header","description":"A key (1..256 chars, [A-Za-z0-9_\\-:]) makes retries safe: a replay returns the original response for 24 h.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadFcmCredentialDto"}}}},"responses":{"200":{"description":"The active credential for the project (metadata only).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FcmCredentialDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"Idempotent-Replay":{"description":"`true` when this response is a replay of an earlier request with the same `Idempotency-Key`.","schema":{"type":"string","enum":["true"]}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is malformed (`errors` names it).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"`CONFLICT`: another upload for the same project ran at the same time; `IDEMPOTENCY_KEY_REUSE`: the `Idempotency-Key` was used with a different body. Idempotency: `IDEMPOTENCY_KEY_REUSE` when this `Idempotency-Key` was already used with a different body or route; `IDEMPOTENCY_IN_PROGRESS` while another request with the same key is still running (retry after `Retry-After` seconds, with the same key).","headers":{"Retry-After":{"description":"Seconds to wait before retrying with the same key (sent with `IDEMPOTENCY_IN_PROGRESS`).","schema":{"type":"integer"}},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["CONFLICT","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_IN_PROGRESS"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"`CREDENTIAL_INVALID`: the JSON is not a service-account key file, or Google rejected it (deleted account or key, missing Firebase Cloud Messaging role, FCM API not enabled, unknown project). Nothing was stored.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY","CREDENTIAL_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"502":{"description":"`BAD_GATEWAY`: Google could not be reached or is throttling; nothing was stored, retry.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_GATEWAY"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Upload an FCM service account","tags":["channels"],"x-pc-sdk":{"resource":"channels.fcm","method":"uploadCredential","bind":["workspace_id"],"paginated":false}}},"/v1/workspaces/{workspace_id}/channels/fcm/credentials/{credential_id}":{"delete":{"description":"Deletes an FCM credential and erases its private key immediately. Push to Android devices of its project is skipped (`credential_invalid`) until another service account is uploaded.","operationId":"ChannelsController_deleteFcm","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"credential_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the credential.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"204":{"description":"Deleted.","headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks the action, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`CREDENTIAL_NOT_FOUND`: no FCM credential with this id in the workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","CREDENTIAL_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Delete an FCM service account","tags":["channels"],"x-pc-sdk":{"resource":"channels.fcm","method":"deleteCredential","bind":["workspace_id"],"paginated":false}}},"/v1/client/inbox":{"get":{"description":"The recipient's in-app inbox, newest first, with the unseen and unread counts for the badge. Active items by default; `archived=true` lists the archived ones. Items the sender retracted are left out.","operationId":"ClientController_list","parameters":[{"name":"status","required":false,"in":"query","description":"`all`, or only `unread` or `unseen` items.","schema":{"$ref":"#/components/schemas/ClientInboxStatusFilter"},"example":"all"},{"name":"category","required":false,"in":"query","description":"Only this category (its key).","schema":{"example":"billing","type":"string"},"example":"billing"},{"name":"tenant","required":false,"in":"query","description":"Only this tenant (the `tenant` of the send).","schema":{"example":"org_42","type":"string"},"example":"org_42"},{"name":"archived","required":false,"in":"query","description":"`true` lists archived items instead of the active ones.","schema":{"default":false,"type":"boolean"},"example":false},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor for pagination: the `next_cursor` of the previous page.","schema":{"type":"string"},"example":"eyJjcmVhdGVkX2F0IjoiMjAyNi0xMS0wMVQwMDowMDowMFoifQ"},{"name":"limit","required":false,"in":"query","description":"Page size.","schema":{"minimum":1,"maximum":100,"default":25,"type":"integer"},"example":25}],"responses":{"200":{"description":"A page of items.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientInboxListDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a query parameter is invalid, or the cursor is not one this route returned.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"`RECIPIENT_TOKEN_INVALID`: no recipient token, a malformed or forged one, an API key, or a revoked token (mint a new one); `RECIPIENT_TOKEN_EXPIRED`: the token expired (mint a new one).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID","RECIPIENT_TOKEN_EXPIRED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"`RECIPIENT_TOKEN_SCOPE`: the token lacks the `inbox:read` scope; `ORIGIN_NOT_ALLOWED`: the browser origin is not in the workspace client origins; `SERVICE_NOT_ENABLED`: Notify is turned off for the workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","RECIPIENT_TOKEN_SCOPE","ORIGIN_NOT_ALLOWED","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"`RATE_LIMITED`: over 60 client calls per minute for this recipient; `Retry-After` gives the seconds to wait. Per-IP limit: more than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"recipient_token":[]}],"summary":"List the inbox","tags":["client"],"x-pc-sdk":{"resource":"me.inbox","method":"list","bind":[],"paginated":true}}},"/v1/client/inbox/counts":{"get":{"description":"Unseen and unread counts for the badge, cheap enough to poll every 30 seconds or more when the real-time stream is not available. Send the last `ETag` as `If-None-Match` to get `304` while nothing changed.","operationId":"ClientController_counts","parameters":[{"name":"if-none-match","required":false,"in":"header","description":"The `ETag` of a previous response.","schema":{"type":"string"}}],"responses":{"200":{"description":"The counts (with an `ETag`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboxCountsDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"304":{"description":"Not modified since the `ETag` you sent.","headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"`RECIPIENT_TOKEN_INVALID`: no recipient token, a malformed or forged one, an API key, or a revoked token (mint a new one); `RECIPIENT_TOKEN_EXPIRED`: the token expired (mint a new one).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID","RECIPIENT_TOKEN_EXPIRED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"`RECIPIENT_TOKEN_SCOPE`: the token lacks the `inbox:read` scope; `ORIGIN_NOT_ALLOWED`: the browser origin is not in the workspace client origins; `SERVICE_NOT_ENABLED`: Notify is turned off for the workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","RECIPIENT_TOKEN_SCOPE","ORIGIN_NOT_ALLOWED","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"`RATE_LIMITED`: over 60 client calls per minute for this recipient; `Retry-After` gives the seconds to wait. Per-IP limit: more than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"recipient_token":[]}],"summary":"Get the inbox counts","tags":["client"],"x-pc-sdk":{"resource":"me.inbox","method":"counts","bind":[],"paginated":false}}},"/v1/client/inbox/mark":{"post":{"description":"Marks up to 100 items (`ids`), or every item up to and including `before_id` (\"mark all as read\"), as `seen`, `read`, `unread`, `archived` or `unarchived`, and returns the new counts. Marking `read` also marks seen. Open streams get `item.updated` and `counts`. Safe to repeat: items already in that state do not change.","operationId":"ClientController_mark","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkInboxDto"}}}},"responses":{"200":{"description":"How many items changed, and the counts after.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkInboxResultDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is invalid, or not exactly one of `ids` and `before_id` was given.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"`RECIPIENT_TOKEN_INVALID`: no recipient token, a malformed or forged one, an API key, or a revoked token (mint a new one); `RECIPIENT_TOKEN_EXPIRED`: the token expired (mint a new one).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID","RECIPIENT_TOKEN_EXPIRED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"`RECIPIENT_TOKEN_SCOPE`: the token lacks the `inbox:write` scope; `ORIGIN_NOT_ALLOWED`: the browser origin is not in the workspace client origins; `SERVICE_NOT_ENABLED`: Notify is turned off for the workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","RECIPIENT_TOKEN_SCOPE","ORIGIN_NOT_ALLOWED","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"`RATE_LIMITED`: over 60 client calls per minute for this recipient; `Retry-After` gives the seconds to wait. Per-IP limit: more than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"recipient_token":[]}],"summary":"Mark inbox items","tags":["client"],"x-pc-sdk":{"resource":"me.inbox","method":"mark","bind":[],"paginated":false}}},"/v1/client/inbox/{item_id}/interact":{"post":{"description":"Call it when the recipient presses the item or one of its buttons (`action_key`). Marks the item seen and read, records `interacted_at` and the key, and sends the `inbox_item.interacted` webhook so your backend can act on it (for example \"Accept invite\"). Repeating the same press changes nothing.","operationId":"ClientController_interact","parameters":[{"name":"item_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the item.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InteractInboxDto"}}}},"responses":{"200":{"description":"The item after the press.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboxItemDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"`RECIPIENT_TOKEN_INVALID`: no recipient token, a malformed or forged one, an API key, or a revoked token (mint a new one); `RECIPIENT_TOKEN_EXPIRED`: the token expired (mint a new one).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID","RECIPIENT_TOKEN_EXPIRED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"`RECIPIENT_TOKEN_SCOPE`: the token lacks the `inbox:write` scope; `ORIGIN_NOT_ALLOWED`: the browser origin is not in the workspace client origins; `SERVICE_NOT_ENABLED`: Notify is turned off for the workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","RECIPIENT_TOKEN_SCOPE","ORIGIN_NOT_ALLOWED","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`INBOX_ITEM_NOT_FOUND`: the recipient has no item with this id (or it was retracted).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","INBOX_ITEM_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"`VALIDATION_ERROR`: the item has no button with this `action_key`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"`RATE_LIMITED`: over 60 client calls per minute for this recipient; `Retry-After` gives the seconds to wait. Per-IP limit: more than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"recipient_token":[]}],"summary":"Record a press on an inbox item","tags":["client"],"x-pc-sdk":{"resource":"me.inbox","method":"interact","bind":[],"paginated":false}}},"/v1/client/devices":{"post":{"description":"Registers the app's APNs or FCM token, or the browser's web push subscription, for the signed-in recipient (call it on every app start; the same token again refreshes it). A token registered to another recipient moves to this one, at most 3 times per token per UTC day; a recipient registers at most 10 times per hour. Call `DELETE /v1/client/devices/{device_id}` when the user signs out on this device. Safe to repeat, so it takes no `Idempotency-Key`.","operationId":"ClientController_registerDevice","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDeviceDto"}}}},"responses":{"200":{"description":"Already registered to this recipient: refreshed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"201":{"description":"Registered (new, or moved from another recipient).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is invalid (`errors` names it).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"`RECIPIENT_TOKEN_INVALID`: no recipient token, a malformed or forged one, an API key, or a revoked token (mint a new one); `RECIPIENT_TOKEN_EXPIRED`: the token expired (mint a new one).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID","RECIPIENT_TOKEN_EXPIRED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"`RECIPIENT_TOKEN_SCOPE`: the token lacks the `devices:write` scope; `ORIGIN_NOT_ALLOWED`: the browser origin is not in the workspace client origins; `SERVICE_NOT_ENABLED`: Notify is turned off for the workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","RECIPIENT_TOKEN_SCOPE","ORIGIN_NOT_ALLOWED","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"`DEVICE_TOKEN_INVALID`: the token or subscription is malformed for its channel (or `environment` is missing for APNs); `WEBPUSH_ENDPOINT_NOT_ALLOWED`: the endpoint is not https on a known push service.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY","DEVICE_TOKEN_INVALID","WEBPUSH_ENDPOINT_NOT_ALLOWED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"`RATE_LIMITED`: over 60 client calls per minute for this recipient; `Retry-After` gives the seconds to wait. Per-IP limit: more than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"recipient_token":[]}],"summary":"Register this device","tags":["client"],"x-pc-sdk":{"resource":"me.devices","method":"register","bind":[],"paginated":false}}},"/v1/client/devices/{device_id}":{"delete":{"description":"Call it when the user signs out on the device: pushes to it stop at once.","operationId":"ClientController_removeDevice","parameters":[{"name":"device_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the device.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"}],"responses":{"204":{"description":"Removed.","headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"`RECIPIENT_TOKEN_INVALID`: no recipient token, a malformed or forged one, an API key, or a revoked token (mint a new one); `RECIPIENT_TOKEN_EXPIRED`: the token expired (mint a new one).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID","RECIPIENT_TOKEN_EXPIRED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"`RECIPIENT_TOKEN_SCOPE`: the token lacks the `devices:write` scope; `ORIGIN_NOT_ALLOWED`: the browser origin is not in the workspace client origins; `SERVICE_NOT_ENABLED`: Notify is turned off for the workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","RECIPIENT_TOKEN_SCOPE","ORIGIN_NOT_ALLOWED","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`DEVICE_NOT_FOUND`: the recipient has no device with this id.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","DEVICE_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"`RATE_LIMITED`: over 60 client calls per minute for this recipient; `Retry-After` gives the seconds to wait. Per-IP limit: more than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"recipient_token":[]}],"summary":"Remove this device","tags":["client"],"x-pc-sdk":{"resource":"me.devices","method":"delete","bind":[],"paginated":false}}},"/v1/client/webpush/vapid-public-key":{"get":{"description":"The VAPID public key the browser subscribes with (`pushManager.subscribe({ applicationServerKey })`).","operationId":"ClientController_vapidPublicKey","parameters":[],"responses":{"200":{"description":"The public key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VapidPublicKeyDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"`RECIPIENT_TOKEN_INVALID`: no recipient token, a malformed or forged one, an API key, or a revoked token (mint a new one); `RECIPIENT_TOKEN_EXPIRED`: the token expired (mint a new one).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID","RECIPIENT_TOKEN_EXPIRED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"`RECIPIENT_TOKEN_SCOPE`: the token lacks the `devices:write` scope; `ORIGIN_NOT_ALLOWED`: the browser origin is not in the workspace client origins; `SERVICE_NOT_ENABLED`: Notify is turned off for the workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","RECIPIENT_TOKEN_SCOPE","ORIGIN_NOT_ALLOWED","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"`CHANNEL_NOT_CONFIGURED`: the workspace has no web push key yet (generate one in the console or with `POST /channels/webpush/vapid-keys`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["NOT_FOUND","CHANNEL_NOT_CONFIGURED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"`RATE_LIMITED`: over 60 client calls per minute for this recipient; `Retry-After` gives the seconds to wait. Per-IP limit: more than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"recipient_token":[]}],"summary":"Get the web push key","tags":["client"],"x-pc-sdk":{"resource":"me.webpush","method":"vapidPublicKey","bind":[],"paginated":false}}},"/v1/client/preferences":{"get":{"description":"The workspace's categories with their per-channel defaults (`mandatory` channels cannot be turned off: show them read-only), the cells the recipient set, and their time zone and quiet hours.","operationId":"ClientController_getPreferences","parameters":[],"responses":{"200":{"description":"The preferences.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientPreferencesDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"`RECIPIENT_TOKEN_INVALID`: no recipient token, a malformed or forged one, an API key, or a revoked token (mint a new one); `RECIPIENT_TOKEN_EXPIRED`: the token expired (mint a new one).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID","RECIPIENT_TOKEN_EXPIRED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"`RECIPIENT_TOKEN_SCOPE`: the token lacks the `preferences:read` scope; `ORIGIN_NOT_ALLOWED`: the browser origin is not in the workspace client origins; `SERVICE_NOT_ENABLED`: Notify is turned off for the workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","RECIPIENT_TOKEN_SCOPE","ORIGIN_NOT_ALLOWED","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"`RATE_LIMITED`: over 60 client calls per minute for this recipient; `Retry-After` gives the seconds to wait. Per-IP limit: more than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"recipient_token":[]}],"summary":"Get my preferences","tags":["client"],"x-pc-sdk":{"resource":"me.preferences","method":"retrieve","bind":[],"paginated":false}},"patch":{"description":"Sets or (with `enabled: null`) removes individual cells, and the time zone, quiet hours and unsubscribe-all. Cells you leave out are kept, so two open tabs cannot overwrite each other. Every change is recorded as consent evidence (source `recipient_token`).","operationId":"ClientController_updatePreferences","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateClientPreferencesDto"}}}},"responses":{"200":{"description":"The preferences after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientPreferencesDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a field is invalid (`errors` names it).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"`RECIPIENT_TOKEN_INVALID`: no recipient token, a malformed or forged one, an API key, or a revoked token (mint a new one); `RECIPIENT_TOKEN_EXPIRED`: the token expired (mint a new one).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID","RECIPIENT_TOKEN_EXPIRED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"`RECIPIENT_TOKEN_SCOPE`: the token lacks the `preferences:write` scope; `ORIGIN_NOT_ALLOWED`: the browser origin is not in the workspace client origins; `SERVICE_NOT_ENABLED`: Notify is turned off for the workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","RECIPIENT_TOKEN_SCOPE","ORIGIN_NOT_ALLOWED","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"`PREFERENCE_LOCKED`: the channel is mandatory for the category; `CATEGORY_NOT_FOUND` / `NOTIFICATION_TYPE_NOT_FOUND`: no category or type with that key.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNPROCESSABLE_ENTITY","PREFERENCE_LOCKED","CATEGORY_NOT_FOUND","NOTIFICATION_TYPE_NOT_FOUND"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"`RATE_LIMITED`: over 60 client calls per minute for this recipient; `Retry-After` gives the seconds to wait. Per-IP limit: more than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"recipient_token":[]}],"summary":"Update my preferences","tags":["client"],"x-pc-sdk":{"resource":"me.preferences","method":"update","bind":[],"paginated":false}}},"/v1/client/inbox/stream":{"get":{"description":"Server-Sent Events for the recipient's inbox: `counts` `{unseen, unread}` on connect and after every change, `item.created` `{item}` (with the item id as the event id), `item.updated` `{ids, action}` (`read`, `seen`, `archived`, `retracted`, …; `ids` is null for very large changes: refetch), `heartbeat` every 25 seconds, and `auth_expired` `{reason}` when the token expires (`expired`) or is revoked (`revoked`): mint a new token and reconnect; `service_disabled` means Notify was turned off for the workspace (at the next heartbeat). Send `Authorization: Bearer <token>` (use a fetch-stream reader: `EventSource` cannot send headers). Reconnect with `Last-Event-ID` to receive the items created while you were away (up to 100). Delivery is best effort: refetch the counts after a reconnect. At most 5 open streams per recipient (`429 STREAM_LIMIT`); poll `GET /v1/client/inbox/counts` instead when refused.","operationId":"InboxStreamController_stream","parameters":[{"name":"last-event-id","required":false,"in":"header","description":"The id of the last `item.created` event received.","schema":{"type":"string"}}],"responses":{"200":{"description":"The event stream (`text/event-stream`).","content":{"text/event-stream":{"schema":{"type":"string"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"401":{"description":"`RECIPIENT_TOKEN_INVALID`: no recipient token, a malformed or forged one, an API key, or a revoked token (mint a new one); `RECIPIENT_TOKEN_EXPIRED`: the token expired (mint a new one).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID","RECIPIENT_TOKEN_EXPIRED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"`RECIPIENT_TOKEN_SCOPE`: the token lacks the `stream` scope; `ORIGIN_NOT_ALLOWED`: the browser origin is not in the workspace client origins; `SERVICE_NOT_ENABLED`: Notify is turned off for the workspace.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","RECIPIENT_TOKEN_SCOPE","ORIGIN_NOT_ALLOWED","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"`STREAM_LIMIT`: too many open streams for this recipient (5), workspace or server; close one or poll `GET /v1/client/inbox/counts`. `RATE_LIMITED`: over 60 client calls per minute for this recipient; `Retry-After` gives the seconds to wait. Per-IP limit: more than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED","STREAM_LIMIT"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"503":{"description":"`SERVICE_UNAVAILABLE`: the server is restarting; reconnect after a few seconds.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["SERVICE_UNAVAILABLE"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"recipient_token":[]}],"summary":"Stream inbox events","tags":["client"],"x-pc-sdk":false}},"/v1/workspaces/{workspace_id}/stats":{"get":{"description":"Daily delivery counts per channel for a range of UTC days (default the last 30, at most 90): planned, sent, delivered, failed, skipped, and inbox items read. Counted by the day a delivery was planned, from an hourly rollup: today and yesterday are refreshed every hour, so the newest counts can be up to an hour old (`updated_at`). Kept beyond the 30 days that delivery history is kept.","operationId":"StatsController_get","parameters":[{"name":"workspace_id","required":true,"in":"path","schema":{"format":"uuid","type":"string"},"description":"Id of the workspace.","example":"3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"from","required":false,"in":"query","description":"First UTC day (inclusive). Default: 29 days before `to`.","schema":{"format":"date","example":"2026-11-01","type":"string"},"example":"2026-11-01"},{"name":"to","required":false,"in":"query","description":"Last UTC day (inclusive). Default: today. At most 90 days after `from`.","schema":{"format":"date","example":"2026-11-10","type":"string"},"example":"2026-11-10"}],"responses":{"200":{"description":"The stats.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotifyStatsResponseDto"}}},"headers":{"X-RateLimit-Limit-short":{"$ref":"#/components/headers/X-RateLimit-Limit-short"},"X-RateLimit-Remaining-short":{"$ref":"#/components/headers/X-RateLimit-Remaining-short"},"X-RateLimit-Reset-short":{"$ref":"#/components/headers/X-RateLimit-Reset-short"},"X-RateLimit-Limit-long":{"$ref":"#/components/headers/X-RateLimit-Limit-long"},"X-RateLimit-Remaining-long":{"$ref":"#/components/headers/X-RateLimit-Remaining-long"},"X-RateLimit-Reset-long":{"$ref":"#/components/headers/X-RateLimit-Reset-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}}},"400":{"description":"`VALIDATION_ERROR`: a date is invalid, `from` is after `to`, or the range is longer than 90 days.","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["BAD_REQUEST","VALIDATION_ERROR"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"401":{"description":"Missing, expired, revoked or invalid bearer token (`UNAUTHORIZED`): send a workspace API key or a console session as `Authorization: Bearer <token>`. A recipient token is refused here with `RECIPIENT_TOKEN_INVALID`: it works only on the client API (`/v1/client/…`).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["UNAUTHORIZED","RECIPIENT_TOKEN_INVALID"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"The caller lacks `notify.message.read`, or the workspace has not enabled Notify (`SERVICE_NOT_ENABLED`). Also `FORBIDDEN` when the credential belongs to another workspace than the URL names, or the URL uses the workspace slug instead of its id (a UUID).","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["FORBIDDEN","SERVICE_NOT_ENABLED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"429":{"description":"More than 20 requests per second or 600 per minute from this client IP (`RATE_LIMITED`); wait `Retry-After` seconds.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Retry-After-short":{"$ref":"#/components/headers/Retry-After-short"},"Retry-After-long":{"$ref":"#/components/headers/Retry-After-long"},"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"x-pc-error-codes":["RATE_LIMITED"],"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get Notify stats","tags":["stats"],"x-pc-sdk":{"resource":"stats","method":"retrieve","bind":["workspace_id"],"paginated":false}}}},"info":{"title":"Notify API","description":"Notifications for the ProductCraft platform: one call notifies a user across an in-app inbox, email (through ProductCraft Mail), web push, APNs and FCM, with per-user preferences, fallbacks, quiet hours, templates per channel, delivery history and signed webhooks. Push to Apple and Google devices uses your own APNs key and Firebase service account, which you upload once (stored encrypted, never returned).\n\n## Rate limits\n\nEvery `/v1` route is limited per client IP to 20 requests per second and 600 per minute. Each response carries `X-RateLimit-Limit-short`, `X-RateLimit-Remaining-short`, `X-RateLimit-Reset-short` (the one-second window) and the same three with `-long` (the one-minute window). Over either limit the response is `429` with code `RATE_LIMITED` and a `Retry-After` header in seconds.","version":"0.1.0","contact":{}},"tags":[{"name":"categories","description":"Groups of notification types that recipients opt in or out of together, with a default per channel (`on`, `off` or `mandatory`)."},{"name":"channels","description":"Which channels can deliver now, and their credentials (metadata only; secrets are write-only). Web push uses a VAPID key pair that Notify generates for you, or that you import from another provider."},{"name":"client","description":"The client API, called from your users' apps and browsers with a recipient token (`Authorization: Bearer`), never an API key: their inbox (list, counts, mark, button presses, the real-time stream), their devices and web push subscription, and their preferences. Browser calls must come from the workspace's `client_origins`."},{"name":"deliveries","description":"One row per notification, recipient, channel and target (the inbox, an email address or a device), with its state, the reason for every skip, retry or failure, and each provider attempt."},{"name":"export","description":"The whole Notify configuration of a workspace as JSON, to keep in source control or apply to another workspace."},{"name":"notification-types","description":"What you send: a key, a category, routing (`always` channels and ordered `fallback` steps), templates per channel and locale with version history, and a preview that renders without sending."},{"name":"notifications","description":"Send a notification to up to 1,000 recipients with one call (Idempotency-Key required), explain a send with `dry_run`, and read, cancel or retract what was sent (the message log)."},{"name":"recipient-tokens","description":"Short-lived tokens your backend mints for one signed-in user, so their app or browser can call the client API without your secret key. Revoke one recipient's tokens, or every token of the workspace."},{"name":"recipients","description":"The people you notify, keyed by your own user id (`external_id`), with their email, phone, locale, time zone, quiet hours, your custom properties, their devices (APNs and FCM tokens, web push subscriptions) and their preferences. Erase one for GDPR with `DELETE`."},{"name":"settings","description":"Workspace settings: email on or off, and the automatic email pause (more than 0.1 % complaints or 5 % hard bounces among recent emails) with how to resume it."},{"name":"setup-status","description":"A checklist of what Notify needs before notifications reach anyone (a notification type, the Mail sender and templates for email), with where to fix each item."},{"name":"stats","description":"Daily delivery counts per channel from an hourly rollup (planned, sent, delivered, failed, skipped, inbox reads), kept beyond the 30 days of delivery history."},{"name":"webhooks","description":"Signed webhooks (Standard Webhooks) for sends, delivery outcomes (sent, delivered, failed, bounced, skipped with the reason), inbox reads, archives and clicks, pruned and moved devices, refused credentials, the email auto-pause and recipient erasure, with an event log, retries, Send test and replay. Payloads carry ids, the type key, channel, state and reason: never rendered text, addresses or tokens."}],"servers":[{"url":"https://api.notify.productcraft.co","description":"Notify API"}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"},"recipient_token":{"scheme":"bearer","bearerFormat":"JWT","type":"http","description":"A recipient token minted by your backend with `POST /v1/workspaces/{workspace_id}/recipient-tokens`. Only the client API (`/v1/client/…`) accepts it."}},"schemas":{"NotifyNotificationAcceptedDataDto":{"type":"object","properties":{"notification_id":{"type":"string","format":"uuid","example":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a01","description":"The notification."},"type_key":{"type":"string","example":"invoice.paid","description":"The notification type key."},"recipient_count":{"type":"integer","example":3,"description":"Recipients the notification will be planned for."},"skipped_count":{"type":"integer","example":0,"description":"Recipients skipped at accept (unknown or recently erased ids)."},"send_at":{"type":"string","format":"date-time","nullable":true,"example":null,"description":"When a scheduled send goes out; null for an immediate send."},"tenant":{"type":"string","nullable":true,"example":null,"description":"The tenant the send was scoped to, if any."}},"required":["notification_id","type_key","recipient_count","skipped_count","send_at","tenant"],"x-pc-sdk-model":"NotifyNotificationAcceptedData"},"NotifyWebhookRecipientDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a10","description":"Notify's id for the recipient."},"external_id":{"type":"string","nullable":true,"example":"user_123","description":"Your id for the recipient (`external_id`); null when the recipient was erased since."}},"required":["id","external_id"],"x-pc-sdk-model":"NotifyWebhookRecipient"},"NotifyWebhookChannel":{"type":"string","enum":["inbox","email","push","webpush"]},"NotifyWebhookDeliveryState":{"type":"string","enum":["sent","delivered","failed","bounced","skipped","pruned"]},"NotifyDeliveryEventDataDto":{"type":"object","properties":{"delivery_id":{"type":"string","format":"uuid","example":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a02","description":"The delivery."},"notification_id":{"type":"string","format":"uuid","example":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a01","description":"The notification it belongs to."},"type_key":{"type":"string","example":"invoice.paid","description":"The notification type key."},"recipient":{"description":"Who it was for.","allOf":[{"$ref":"#/components/schemas/NotifyWebhookRecipientDto"}]},"channel":{"example":"push","description":"The channel (`push` is APNs or FCM).","allOf":[{"$ref":"#/components/schemas/NotifyWebhookChannel"}]},"provider":{"type":"string","nullable":true,"example":"apns","description":"The provider behind the channel: `inbox`, `mail`, `apns`, `fcm` or `webpush`."},"device_id":{"type":"string","format":"uuid","nullable":true,"example":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a20","description":"The device, for push and web push."},"state":{"example":"sent","description":"The state the delivery reached. `delivery.failed` also carries `pruned` (the device token is dead; `device.pruned` follows).","allOf":[{"$ref":"#/components/schemas/NotifyWebhookDeliveryState"}]},"reason":{"type":"string","nullable":true,"example":null,"description":"Why it was skipped or failed (a `NotifyDeliveryReason` code); null on success."},"is_fallback":{"type":"boolean","example":false,"description":"True when this delivery is a fallback step (e.g. the email after an unanswered push)."},"attempts":{"type":"integer","example":1,"description":"Provider attempts so far."}},"required":["delivery_id","notification_id","type_key","recipient","channel","provider","device_id","state","reason","is_fallback","attempts"],"x-pc-sdk-model":"NotifyDeliveryEventData"},"NotifyInboxItemEventDataDto":{"type":"object","properties":{"inbox_item_id":{"type":"string","format":"uuid","example":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a30","description":"The inbox item."},"notification_id":{"type":"string","format":"uuid","example":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a01","description":"The notification it came from."},"type_key":{"type":"string","example":"invoice.paid","description":"The notification type key."},"recipient":{"description":"Whose inbox.","allOf":[{"$ref":"#/components/schemas/NotifyWebhookRecipientDto"}]},"interaction_key":{"type":"string","nullable":true,"example":null,"description":"For `inbox_item.interacted`: the `action_key` of the button pressed, null for a click on the item."}},"required":["inbox_item_id","notification_id","type_key","recipient","interaction_key"],"x-pc-sdk-model":"NotifyInboxItemEventData"},"NotifyWebhookDeviceChannel":{"type":"string","enum":["apns","fcm","webpush"]},"NotifyDeviceEventDataDto":{"type":"object","properties":{"device_id":{"type":"string","format":"uuid","example":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a20","description":"The device."},"channel":{"example":"apns","description":"The device channel.","allOf":[{"$ref":"#/components/schemas/NotifyWebhookDeviceChannel"}]},"platform":{"type":"string","nullable":true,"example":"ios","description":"The platform the device registered with."},"recipient":{"description":"The recipient the device belongs to now.","allOf":[{"$ref":"#/components/schemas/NotifyWebhookRecipientDto"}]},"reason":{"type":"string","nullable":true,"example":"Unregistered","description":"For `device.pruned`: why (the provider code, `stale_270d` for a device not seen for 270 days, `evicted` beyond 20 devices)."},"previous_recipient":{"nullable":true,"description":"For `device.moved`: the recipient the device moved away from.","allOf":[{"$ref":"#/components/schemas/NotifyWebhookRecipientDto"}]},"move_count_day":{"type":"integer","nullable":true,"example":null,"description":"For `device.moved`: moves of this token today (UTC); at most 3."}},"required":["device_id","channel","platform","recipient","reason","previous_recipient","move_count_day"],"x-pc-sdk-model":"NotifyDeviceEventData"},"NotifyWebhookCredentialProvider":{"type":"string","enum":["apns","fcm","vapid"]},"NotifyCredentialInvalidDataDto":{"type":"object","properties":{"credential_id":{"type":"string","format":"uuid","example":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a40","description":"The credential."},"provider":{"example":"apns","description":"The provider.","allOf":[{"$ref":"#/components/schemas/NotifyWebhookCredentialProvider"}]},"app_id":{"type":"string","example":"com.example.app","description":"The bundle id, FCM project id, or `-`."},"environment":{"type":"string","nullable":true,"example":"production","description":"The APNs environment; null for FCM and VAPID."},"error_code":{"type":"string","nullable":true,"example":"InvalidProviderToken","description":"The provider's error code."}},"required":["credential_id","provider","app_id","environment","error_code"],"x-pc-sdk-model":"NotifyCredentialInvalidData"},"NotifyEmailPauseReason":{"type":"string","enum":["complaints","bounces"]},"NotifyEmailAutoPausedDataDto":{"type":"object","properties":{"reason":{"example":"complaints","description":"What crossed the threshold.","allOf":[{"$ref":"#/components/schemas/NotifyEmailPauseReason"}]},"complaints":{"type":"integer","example":2,"description":"Complaints in the window."},"bounces":{"type":"integer","example":3,"description":"Hard bounces in the window."},"window":{"type":"integer","example":500,"description":"Emails in the window."}},"required":["reason","complaints","bounces","window"],"x-pc-sdk-model":"NotifyEmailAutoPausedData"},"NotifyRecipientDeletionCause":{"type":"string","enum":["api","auth_user_deleted"]},"NotifyRecipientDeletedDataDto":{"type":"object","properties":{"external_id":{"type":"string","example":"user_123","description":"Your id for the erased recipient."},"cause":{"example":"api","description":"`api`: erased through `DELETE /recipients/{external_id}`; `auth_user_deleted`: the linked Auth end user was deleted.","allOf":[{"$ref":"#/components/schemas/NotifyRecipientDeletionCause"}]}},"required":["external_id","cause"],"x-pc-sdk-model":"NotifyRecipientDeletedData"},"WebhookEventType":{"type":"string","enum":["notification.accepted","delivery.sent","delivery.delivered","delivery.failed","delivery.bounced","delivery.skipped","inbox_item.read","inbox_item.archived","inbox_item.interacted","device.pruned","device.moved","credential.invalid","email.auto_paused","recipient.deleted"],"description":"Every event type this product sends; `x-webhooks` maps each to its payload schema. New types can be added, so treat it as open."},"WebhookEventTypeDto":{"type":"object","properties":{"type":{"example":"notification.accepted","description":"The event type (one of this product's catalogue).","allOf":[{"$ref":"#/components/schemas/WebhookEventType"}]},"description":{"type":"string","example":"A user signed up or was created by an admin.","description":"When this event is sent."},"category":{"type":"string","example":"Users","description":"Group shown in the event picker."},"example":{"type":"object","additionalProperties":true,"description":"An example `data` object; \"Send test\" delivers exactly this."}},"x-pc-sdk-model":"WebhookEventTypeInfo","required":["type","description","category","example"]},"ListWebhookEventTypesResponseDto":{"type":"object","properties":{"data":{"description":"Every event type this product sends.","type":"array","items":{"$ref":"#/components/schemas/WebhookEventTypeDto"}}},"x-pc-sdk-model":"WebhookEventTypeList","x-pc-sdk-model-aliases":["ListWebhookEventTypes"],"required":["data"]},"WebhookEndpointStatus":{"type":"string","enum":["enabled","disabled","auto_disabled"]},"WebhookEndpointDisabledReason":{"type":"string","enum":["manual","failing"]},"WebhookEndpointDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The endpoint's id."},"scope_type":{"type":"string","example":"workspace","description":"What owns the endpoint: `workspace`, `app` or `community`."},"scope_id":{"type":"string","description":"Id of the owning workspace, app or community."},"url":{"type":"string","format":"uri","example":"https://api.acme.com/webhooks","description":"Where events are sent."},"description":{"type":"string","nullable":true,"example":"Production receiver","description":"A label for people; null when unset."},"event_types":{"description":"Subscribed event types. Empty means every event, including types added later.","example":[],"type":"array","items":{"type":"string"}},"enabled":{"type":"boolean","description":"The on/off switch (`PATCH … { enabled }`)."},"status":{"description":"`enabled` receives events. `disabled` was switched off. `auto_disabled` was switched off after 20 failed deliveries in a row or 24 hours of failures; `PATCH { enabled: true }` turns it back on.","allOf":[{"$ref":"#/components/schemas/WebhookEndpointStatus"}]},"disabled_at":{"type":"string","format":"date-time","nullable":true,"description":"When it was switched off; null while enabled."},"disabled_reason":{"nullable":true,"description":"`manual`: switched off by a caller. `failing`: switched off automatically after failed deliveries. Null while enabled.","allOf":[{"$ref":"#/components/schemas/WebhookEndpointDisabledReason"}]},"consecutive_failures":{"type":"number","description":"Failed automatic deliveries in a row; a success resets it."},"first_failure_at":{"type":"string","format":"date-time","nullable":true,"description":"Start of the current failure streak."},"signing_secret_hint":{"type":"string","description":"Last 4 characters of the signing secret.","example":"x9Q="},"prev_signing_secret_active":{"type":"boolean","description":"True while the previous secret still signs too (after a rotation, for its grace period)."},"prev_signing_secret_expires_at":{"type":"string","format":"date-time","nullable":true,"description":"Until then the previous secret signs too (after a rotation)."},"legacy_signature":{"type":"boolean","description":"True for endpoints carried over from the product's previous webhook system: they also receive the old signature header until its sunset date."},"created_at":{"type":"string","format":"date-time","description":"When the endpoint was created."},"updated_at":{"type":"string","format":"date-time","description":"When the endpoint last changed."}},"required":["id","scope_type","scope_id","url","description","event_types","enabled","status","disabled_at","disabled_reason","consecutive_failures","first_failure_at","signing_secret_hint","prev_signing_secret_active","prev_signing_secret_expires_at","legacy_signature","created_at","updated_at"],"x-pc-sdk-model":"WebhookEndpoint"},"WebhookPaginationDto":{"type":"object","properties":{"next_cursor":{"type":"string","nullable":true,"description":"Pass as `cursor` to get the next page; null on the last page."},"has_more":{"type":"boolean","description":"Whether another page follows."}},"required":["next_cursor","has_more"],"x-pc-sdk-model":"WebhookPagination"},"ListWebhookEndpointsResponseDto":{"type":"object","properties":{"data":{"description":"The endpoints, newest first.","type":"array","items":{"$ref":"#/components/schemas/WebhookEndpointDto"}},"pagination":{"description":"Always one page (an endpoint limit applies).","allOf":[{"$ref":"#/components/schemas/WebhookPaginationDto"}]}},"x-pc-sdk-model":"WebhookEndpointList","x-pc-sdk-model-aliases":["ListWebhookEndpoints"],"required":["data","pagination"]},"CreateWebhookEndpointDto":{"type":"object","properties":{"url":{"type":"string","description":"Where to send events: an absolute https:// URL on a public host.","example":"https://api.acme.com/webhooks","maxLength":2048,"format":"uri"},"description":{"type":"string","nullable":true,"maxLength":200,"example":"Production receiver","description":"A label for people."},"event_types":{"type":"array","description":"Event types from `GET …/webhooks/event-types`. Empty or omitted means every event, including types added later.","example":["notification.accepted","delivery.sent"],"items":{"type":"string","enum":["notification.accepted","delivery.sent","delivery.delivered","delivery.failed","delivery.bounced","delivery.skipped","inbox_item.read","inbox_item.archived","inbox_item.interacted","device.pruned","device.moved","credential.invalid","email.auto_paused","recipient.deleted"]}},"enabled":{"type":"boolean","default":true,"example":true,"description":"Send `false` to create it switched off."}},"required":["url"]},"WebhookEndpointWithSecretDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The endpoint's id."},"scope_type":{"type":"string","example":"workspace","description":"What owns the endpoint: `workspace`, `app` or `community`."},"scope_id":{"type":"string","description":"Id of the owning workspace, app or community."},"url":{"type":"string","format":"uri","example":"https://api.acme.com/webhooks","description":"Where events are sent."},"description":{"type":"string","nullable":true,"example":"Production receiver","description":"A label for people; null when unset."},"event_types":{"description":"Subscribed event types. Empty means every event, including types added later.","example":[],"type":"array","items":{"type":"string"}},"enabled":{"type":"boolean","description":"The on/off switch (`PATCH … { enabled }`)."},"status":{"description":"`enabled` receives events. `disabled` was switched off. `auto_disabled` was switched off after 20 failed deliveries in a row or 24 hours of failures; `PATCH { enabled: true }` turns it back on.","allOf":[{"$ref":"#/components/schemas/WebhookEndpointStatus"}]},"disabled_at":{"type":"string","format":"date-time","nullable":true,"description":"When it was switched off; null while enabled."},"disabled_reason":{"nullable":true,"description":"`manual`: switched off by a caller. `failing`: switched off automatically after failed deliveries. Null while enabled.","allOf":[{"$ref":"#/components/schemas/WebhookEndpointDisabledReason"}]},"consecutive_failures":{"type":"number","description":"Failed automatic deliveries in a row; a success resets it."},"first_failure_at":{"type":"string","format":"date-time","nullable":true,"description":"Start of the current failure streak."},"signing_secret_hint":{"type":"string","description":"Last 4 characters of the signing secret.","example":"x9Q="},"prev_signing_secret_active":{"type":"boolean","description":"True while the previous secret still signs too (after a rotation, for its grace period)."},"prev_signing_secret_expires_at":{"type":"string","format":"date-time","nullable":true,"description":"Until then the previous secret signs too (after a rotation)."},"legacy_signature":{"type":"boolean","description":"True for endpoints carried over from the product's previous webhook system: they also receive the old signature header until its sunset date."},"created_at":{"type":"string","format":"date-time","description":"When the endpoint was created."},"updated_at":{"type":"string","format":"date-time","description":"When the endpoint last changed."},"signing_secret":{"type":"string","description":"The signing secret, shown once: pass it unchanged to any Standard Webhooks verifier. Store it now; it cannot be shown again.","example":"whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw"}},"required":["id","scope_type","scope_id","url","description","event_types","enabled","status","disabled_at","disabled_reason","consecutive_failures","first_failure_at","signing_secret_hint","prev_signing_secret_active","prev_signing_secret_expires_at","legacy_signature","created_at","updated_at","signing_secret"],"x-pc-sdk-model":"WebhookEndpointWithSecret"},"UpdateWebhookEndpointDto":{"type":"object","properties":{"url":{"type":"string","maxLength":2048,"format":"uri","example":"https://api.acme.com/webhooks","description":"A new absolute https:// URL on a public host."},"description":{"type":"string","nullable":true,"maxLength":200,"example":"Production receiver","description":"A new label; null clears it."},"event_types":{"type":"array","description":"Replaces the subscription. Event types from `GET …/webhooks/event-types`. Empty or omitted means every event, including types added later.","example":["notification.accepted","delivery.sent"],"items":{"type":"string","enum":["notification.accepted","delivery.sent","delivery.delivered","delivery.failed","delivery.bounced","delivery.skipped","inbox_item.read","inbox_item.archived","inbox_item.interacted","device.pruned","device.moved","credential.invalid","email.auto_paused","recipient.deleted"]}},"enabled":{"type":"boolean","description":"Switch it on or off. Switching on also clears an automatic disable.","example":true}}},"RotateWebhookSecretDto":{"type":"object","properties":{"grace_hours":{"type":"number","default":24,"minimum":0,"maximum":168,"description":"Hours the previous secret keeps signing too (0 ends it now).","example":24}}},"SendWebhookTestDto":{"type":"object","properties":{"event_type":{"type":"string","enum":["notification.accepted","delivery.sent","delivery.delivered","delivery.failed","delivery.bounced","delivery.skipped","inbox_item.read","inbox_item.archived","inbox_item.interacted","device.pruned","device.moved","credential.invalid","email.auto_paused","recipient.deleted"],"description":"Which event to send; its catalogue example is the `data`.","example":"notification.accepted"}},"required":["event_type"]},"WebhookAttemptTrigger":{"type":"string","enum":["automatic","replay","test"]},"WebhookDeliveryAttemptDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The attempt's id."},"event_id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"The event that was delivered."},"endpoint_id":{"type":"string","format":"uuid","description":"The endpoint it was delivered to."},"event_type":{"type":"string","example":"user.created","description":"The event's type."},"attempt_number":{"type":"number","description":"1 for the first delivery; retries and replays count up."},"trigger":{"description":"`automatic` (first delivery or a retry), `replay` or `test`.","allOf":[{"$ref":"#/components/schemas/WebhookAttemptTrigger"}]},"url":{"type":"string","format":"uri","description":"Where it was sent."},"status_code":{"type":"number","nullable":true,"description":"HTTP status; null when no response came back."},"succeeded":{"type":"boolean","description":"True for a 2xx response."},"error_message":{"type":"string","nullable":true,"example":"request timed out","description":"Why the attempt failed when no 2xx came back; null on success."},"response_body":{"type":"string","nullable":true,"description":"The first 1 KiB of the response body."},"latency_ms":{"type":"number","nullable":true,"description":"Milliseconds until the response; null when none came back."},"attempted_at":{"type":"string","format":"date-time","description":"When the attempt was made."}},"required":["id","event_id","endpoint_id","event_type","attempt_number","trigger","url","status_code","succeeded","error_message","response_body","latency_ms","attempted_at"],"x-pc-sdk-model":"WebhookDeliveryAttempt"},"WebhookEventStatus":{"type":"string","enum":["pending","delivered","failed","not_sent"]},"WebhookEventDeliveryDto":{"type":"object","properties":{"endpoint_id":{"type":"string","format":"uuid","description":"The endpoint."},"attempt_number":{"type":"number","description":"Number of the latest attempt; retries and replays count up."},"status_code":{"type":"number","nullable":true,"description":"HTTP status of the latest attempt; null when no response came back."},"succeeded":{"type":"boolean","description":"True when the latest attempt got a 2xx."},"attempted_at":{"type":"string","format":"date-time","description":"When the latest attempt was made."},"next_attempt_at":{"type":"string","format":"date-time","nullable":true,"description":"When the next automatic attempt is due; null when none is queued."}},"required":["endpoint_id","attempt_number","status_code","succeeded","attempted_at","next_attempt_at"],"x-pc-sdk-model":"WebhookEventDelivery"},"WebhookEventDto":{"type":"object","properties":{"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"Sent as `webhook-id`; the same on every retry and replay."},"type":{"example":"notification.accepted","description":"The event type (one of this product's catalogue).","allOf":[{"$ref":"#/components/schemas/WebhookEventType"}]},"created_at":{"type":"string","format":"date-time","description":"When the event happened."},"test":{"type":"boolean","description":"Made by \"Send test\"."},"status":{"description":"`pending`: a delivery is queued. `failed`: the latest attempt to some endpoint failed and none is queued. `delivered`: every endpoint answered 2xx. `not_sent`: no endpoint subscribed.","allOf":[{"$ref":"#/components/schemas/WebhookEventStatus"}]},"payload":{"type":"object","additionalProperties":true,"description":"The JSON body exactly as sent."},"deliveries":{"description":"Latest attempt per endpoint (an endpoint whose first attempt is still queued is not listed yet).","type":"array","items":{"$ref":"#/components/schemas/WebhookEventDeliveryDto"}}},"required":["id","type","created_at","test","status","payload","deliveries"],"x-pc-sdk-model":"WebhookEvent"},"ListWebhookEventsResponseDto":{"type":"object","properties":{"data":{"description":"The events, newest first.","type":"array","items":{"$ref":"#/components/schemas/WebhookEventDto"}},"pagination":{"description":"The cursor for the next page.","allOf":[{"$ref":"#/components/schemas/WebhookPaginationDto"}]}},"x-pc-sdk-model":"WebhookEventList","x-pc-sdk-model-aliases":["ListWebhookEvents"],"required":["data","pagination"]},"WebhookEventDetailDto":{"type":"object","properties":{"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"Sent as `webhook-id`; the same on every retry and replay."},"type":{"example":"notification.accepted","description":"The event type (one of this product's catalogue).","allOf":[{"$ref":"#/components/schemas/WebhookEventType"}]},"created_at":{"type":"string","format":"date-time","description":"When the event happened."},"test":{"type":"boolean","description":"Made by \"Send test\"."},"status":{"description":"`pending`: a delivery is queued. `failed`: the latest attempt to some endpoint failed and none is queued. `delivered`: every endpoint answered 2xx. `not_sent`: no endpoint subscribed.","allOf":[{"$ref":"#/components/schemas/WebhookEventStatus"}]},"payload":{"type":"object","additionalProperties":true,"description":"The JSON body exactly as sent."},"deliveries":{"description":"Latest attempt per endpoint (an endpoint whose first attempt is still queued is not listed yet).","type":"array","items":{"$ref":"#/components/schemas/WebhookEventDeliveryDto"}},"attempts":{"description":"Every attempt, newest first.","type":"array","items":{"$ref":"#/components/schemas/WebhookDeliveryAttemptDto"}}},"required":["id","type","created_at","test","status","payload","deliveries","attempts"],"x-pc-sdk-model":"WebhookEventDetail"},"ReplayWebhookEventResponseDto":{"type":"object","properties":{"event_id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","description":"The replayed event (the same id as the original)."},"attempts":{"description":"One attempt per endpoint it was sent to.","type":"array","items":{"$ref":"#/components/schemas/WebhookDeliveryAttemptDto"}}},"x-pc-sdk-model":"WebhookEventReplay","x-pc-sdk-model-aliases":["ReplayWebhookEvent"],"required":["event_id","attempts"]},"QuietHoursDto":{"type":"object","properties":{"start":{"type":"string","description":"Start of the quiet window, `HH:MM` 24-hour, in the recipient time zone.","example":"22:00"},"end":{"type":"string","description":"End of the quiet window, `HH:MM`. An end before the start spans midnight.","example":"08:00"}},"required":["start","end"],"x-pc-sdk-model":"QuietHours"},"DeviceChannel":{"type":"string","enum":["apns","fcm","webpush"]},"WebPushKeysDto":{"type":"object","properties":{"p256dh":{"type":"string","description":"The subscription's P-256 public key (`keys.p256dh`), base64url, 65 bytes decoded.","example":"BNcRdreALRFXTkOOUHK1EtK2wtaz5Ry4YfYCA_0QTpQtUbVlUls0VJXg7A8u-Ts1XbjhazAkj7I99e8QcYP7DkM","maxLength":128},"auth":{"type":"string","description":"The subscription's auth secret (`keys.auth`), base64url, 16 bytes decoded.","example":"tBHItJI5svbpez7KI4CCXg","maxLength":64}},"required":["p256dh","auth"]},"WebPushSubscriptionDto":{"type":"object","properties":{"endpoint":{"type":"string","format":"uri","maxLength":2048,"description":"The push service endpoint. Must be https on a known push service (Google FCM, Mozilla, Microsoft WNS, Apple): anything else is refused with `WEBPUSH_ENDPOINT_NOT_ALLOWED`.","example":"https://fcm.googleapis.com/fcm/send/dQw4w9WgXcQ:APA91bH"},"keys":{"description":"The encryption keys of the subscription.","allOf":[{"$ref":"#/components/schemas/WebPushKeysDto"}]}},"required":["endpoint","keys"]},"ApnsEnvironment":{"type":"string","enum":["sandbox","production"]},"DevicePlatform":{"type":"string","enum":["ios","ipados","macos","watchos","tvos","visionos","android","web"]},"RegisterDeviceDto":{"type":"object","properties":{"channel":{"description":"`apns` (Apple), `fcm` (Firebase Cloud Messaging) or `webpush` (a browser subscription).","allOf":[{"$ref":"#/components/schemas/DeviceChannel"}]},"token":{"type":"string","description":"The device token, for `apns` (hex) and `fcm`. Stored hashed and encrypted; responses only show its last 6 characters. Not used for `webpush`.","maxLength":4096,"example":"740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad"},"subscription":{"description":"The browser push subscription, for `webpush` only.","allOf":[{"$ref":"#/components/schemas/WebPushSubscriptionDto"}]},"app_id":{"type":"string","description":"The app the token belongs to: the iOS bundle id for `apns`, the Firebase project id for `fcm`, the site origin for `webpush`. Push is sent with the credential configured for this app.","maxLength":255,"example":"com.example.app"},"environment":{"description":"APNs only, required there: `sandbox` for development builds, `production` for TestFlight and App Store builds. A token sent to the wrong environment fails with `BadDeviceToken`.","allOf":[{"$ref":"#/components/schemas/ApnsEnvironment"}]},"platform":{"description":"The operating system. Defaults to `ios` for `apns`, `android` for `fcm` and `web` for `webpush`.","allOf":[{"$ref":"#/components/schemas/DevicePlatform"}]},"timezone":{"type":"string","description":"The device's IANA time zone, used for quiet hours when the recipient has none.","example":"Europe/Lisbon"}},"required":["channel","app_id"]},"PreferenceScope":{"type":"string","enum":["all","category","type"]},"PreferenceChannel":{"type":"string","enum":["inbox","email","push","webpush","*"]},"PreferenceCellInputDto":{"type":"object","properties":{"scope":{"description":"`all` (every type), `category` or `type`.","allOf":[{"$ref":"#/components/schemas/PreferenceScope"}]},"key":{"type":"string","description":"The category or type key (required unless `scope` is `all`).","example":"billing"},"channel":{"description":"A channel, or `*` for every channel (not with `scope: all`: use `unsubscribed_all`). `push` covers iOS and Android.","allOf":[{"$ref":"#/components/schemas/PreferenceChannel"}]},"enabled":{"type":"boolean","description":"Whether the recipient wants it."}},"required":["scope","channel","enabled"]},"BulkRecipientDto":{"type":"object","properties":{"auth_user_id":{"type":"string","description":"Link to an Auth end user (its user id) in a ProductCraft Auth app. At most one recipient per end user in a workspace. The id is not looked up: link the id your Auth app gave you.","format":"uuid","nullable":true},"email":{"type":"string","description":"Email address for the email channel.","format":"email","maxLength":320,"nullable":true,"example":"ada@example.com"},"phone":{"type":"string","description":"Phone number in E.164 form (`+351912345678`), kept for the SMS channel planned after v1.","nullable":true,"example":"+351912345678"},"name":{"type":"string","description":"Display name.","maxLength":255,"nullable":true,"example":"Ada Lovelace"},"locale":{"type":"string","description":"BCP 47 language tag; templates fall back from `pt-PT` to `pt` to the type default.","nullable":true,"example":"pt-PT"},"timezone":{"type":"string","description":"IANA time zone, used for quiet hours.","nullable":true,"example":"Europe/Lisbon"},"quiet_hours":{"nullable":true,"description":"Hours when push and web push are held until the window ends (the inbox is never held).","allOf":[{"$ref":"#/components/schemas/QuietHoursDto"}]},"properties":{"type":"object","additionalProperties":true,"description":"Your own fields, returned exactly as sent (keys are not re-cased). At most 4 KB as JSON. Replaced whole when present.","example":{"plan":"pro","team_id":"team_42"}},"external_id":{"type":"string","description":"Your user id for this recipient: 1-255 of `A-Z a-z 0-9 . _ : @ | -`.","example":"user_123"},"devices":{"maxItems":20,"description":"Devices to register for this recipient (same rules as `POST …/devices`).","type":"array","items":{"$ref":"#/components/schemas/RegisterDeviceDto"}},"preferences":{"maxItems":500,"description":"The recipient's complete preference set (same as `PUT …/preferences`); left out keeps the current cells. A bad cell fails only this row.","type":"array","items":{"$ref":"#/components/schemas/PreferenceCellInputDto"}}},"required":["external_id"]},"BulkUpsertRecipientsDto":{"type":"object","properties":{"recipients":{"minItems":1,"maxItems":1000,"description":"Up to 1,000 recipients. Each row is validated and written on its own: a bad row is reported in `data` and never fails the others. A row repeating an earlier `external_id` in the same call is refused.","type":"array","items":{"$ref":"#/components/schemas/BulkRecipientDto"}}},"required":["recipients"]},"BulkRowStatus":{"type":"string","enum":["created","updated","error"]},"BulkRowErrorDto":{"type":"object","properties":{"code":{"type":"string","description":"Error code, as in the error envelope.","example":"VALIDATION_ERROR"},"message":{"type":"string","description":"What was wrong with the row.","example":"phone must be an E.164 number such as +351912345678"}},"required":["code","message"],"x-pc-sdk-model":"BulkRowError"},"BulkRowResultDto":{"type":"object","properties":{"external_id":{"type":"string","description":"The row external id (as sent; empty when it was missing).","example":"user_123"},"status":{"description":"What happened to the row.","allOf":[{"$ref":"#/components/schemas/BulkRowStatus"}]},"error":{"description":"Why the row was refused (status `error` only).","allOf":[{"$ref":"#/components/schemas/BulkRowErrorDto"}]}},"required":["external_id","status"],"x-pc-sdk-model":"BulkRowResult"},"BulkUpsertRecipientsResponseDto":{"type":"object","properties":{"data":{"description":"One result per row, in request order.","type":"array","items":{"$ref":"#/components/schemas/BulkRowResultDto"}},"created":{"type":"number","description":"Rows created."},"updated":{"type":"number","description":"Rows updated."},"failed":{"type":"number","description":"Rows refused."}},"required":["data","created","updated","failed"],"x-pc-sdk-model":"BulkUpsertRecipients"},"RecipientInclude":{"type":"string","enum":["preferences"]},"PreferenceCellDto":{"type":"object","properties":{"scope":{"description":"Scope.","allOf":[{"$ref":"#/components/schemas/PreferenceScope"}]},"key":{"type":"string","nullable":true,"description":"Category or type key; null for `all`."},"channel":{"description":"Channel or `*`.","allOf":[{"$ref":"#/components/schemas/PreferenceChannel"}]},"enabled":{"type":"boolean","description":"Whether the recipient wants it."}},"required":["scope","key","channel","enabled"],"x-pc-sdk-model":"PreferenceCell"},"RecipientDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Notify's id for the recipient."},"external_id":{"type":"string","description":"Your user id for the recipient (the path key).","example":"user_123"},"auth_user_id":{"type":"string","format":"uuid","nullable":true,"description":"The linked Auth end user."},"email":{"type":"string","format":"email","nullable":true,"description":"Email address."},"phone":{"type":"string","nullable":true,"description":"Phone number (E.164)."},"name":{"type":"string","nullable":true,"description":"Display name."},"locale":{"type":"string","nullable":true,"description":"BCP 47 language tag."},"timezone":{"type":"string","nullable":true,"description":"IANA time zone."},"quiet_hours":{"nullable":true,"description":"Quiet window.","allOf":[{"$ref":"#/components/schemas/QuietHoursDto"}]},"properties":{"type":"object","additionalProperties":true,"description":"Your own fields, exactly as sent."},"email_invalid_at":{"type":"string","format":"date-time","nullable":true,"description":"When the address hard-bounced; email to it stops until the address changes."},"email_complained_at":{"type":"string","format":"date-time","nullable":true,"description":"When the recipient marked an email as spam; email to it stops until the address changes."},"last_seen_at":{"type":"string","format":"date-time","nullable":true,"description":"Last client (recipient-token) activity."},"unsubscribed_all":{"type":"boolean","description":"Opted out of every non-critical notification."},"preferences":{"description":"Preference cells; only on `GET /recipients?include=preferences`.","type":"array","items":{"$ref":"#/components/schemas/PreferenceCellDto"}},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last change."}},"required":["id","external_id","auth_user_id","email","phone","name","locale","timezone","quiet_hours","properties","email_invalid_at","email_complained_at","last_seen_at","unsubscribed_all","created_at","updated_at"],"x-pc-sdk-model":"Recipient"},"RecipientListPaginationDto":{"type":"object","properties":{"next_cursor":{"type":"string","nullable":true,"description":"Pass as `cursor` to get the next page; null on the last page."},"has_more":{"type":"boolean","description":"Whether another page follows."}},"required":["next_cursor","has_more"],"x-pc-sdk-model":"RecipientListPagination"},"RecipientListResponseDto":{"type":"object","properties":{"data":{"description":"Recipients, newest first.","type":"array","items":{"$ref":"#/components/schemas/RecipientDto"}},"pagination":{"description":"The cursor for the next page.","allOf":[{"$ref":"#/components/schemas/RecipientListPaginationDto"}]}},"required":["data","pagination"],"x-pc-sdk-model":"RecipientList"},"UpsertRecipientDto":{"type":"object","properties":{"auth_user_id":{"type":"string","description":"Link to an Auth end user (its user id) in a ProductCraft Auth app. At most one recipient per end user in a workspace. The id is not looked up: link the id your Auth app gave you.","format":"uuid","nullable":true},"email":{"type":"string","description":"Email address for the email channel.","format":"email","maxLength":320,"nullable":true,"example":"ada@example.com"},"phone":{"type":"string","description":"Phone number in E.164 form (`+351912345678`), kept for the SMS channel planned after v1.","nullable":true,"example":"+351912345678"},"name":{"type":"string","description":"Display name.","maxLength":255,"nullable":true,"example":"Ada Lovelace"},"locale":{"type":"string","description":"BCP 47 language tag; templates fall back from `pt-PT` to `pt` to the type default.","nullable":true,"example":"pt-PT"},"timezone":{"type":"string","description":"IANA time zone, used for quiet hours.","nullable":true,"example":"Europe/Lisbon"},"quiet_hours":{"nullable":true,"description":"Hours when push and web push are held until the window ends (the inbox is never held).","allOf":[{"$ref":"#/components/schemas/QuietHoursDto"}]},"properties":{"type":"object","additionalProperties":true,"description":"Your own fields, returned exactly as sent (keys are not re-cased). At most 4 KB as JSON. Replaced whole when present.","example":{"plan":"pro","team_id":"team_42"}}}},"UpdateRecipientDto":{"type":"object","properties":{"auth_user_id":{"type":"string","description":"Link to an Auth end user (its user id) in a ProductCraft Auth app. At most one recipient per end user in a workspace. The id is not looked up: link the id your Auth app gave you.","format":"uuid","nullable":true},"email":{"type":"string","description":"Email address for the email channel.","format":"email","maxLength":320,"nullable":true,"example":"ada@example.com"},"phone":{"type":"string","description":"Phone number in E.164 form (`+351912345678`), kept for the SMS channel planned after v1.","nullable":true,"example":"+351912345678"},"name":{"type":"string","description":"Display name.","maxLength":255,"nullable":true,"example":"Ada Lovelace"},"locale":{"type":"string","description":"BCP 47 language tag; templates fall back from `pt-PT` to `pt` to the type default.","nullable":true,"example":"pt-PT"},"timezone":{"type":"string","description":"IANA time zone, used for quiet hours.","nullable":true,"example":"Europe/Lisbon"},"quiet_hours":{"nullable":true,"description":"Hours when push and web push are held until the window ends (the inbox is never held).","allOf":[{"$ref":"#/components/schemas/QuietHoursDto"}]},"properties":{"type":"object","additionalProperties":true,"description":"Your own fields, returned exactly as sent (keys are not re-cased). At most 4 KB as JSON. Replaced whole when present.","example":{"plan":"pro","team_id":"team_42"}}}},"RecipientErasureStatus":{"type":"string","enum":["erased"]},"RecipientErasureDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The erased recipient id."},"external_id":{"type":"string","description":"The erased external id; it can be used again for a new recipient.","example":"user_123"},"status":{"description":"Always `erased`: the data is gone when this returns.","allOf":[{"$ref":"#/components/schemas/RecipientErasureStatus"}]}},"required":["id","external_id","status"],"x-pc-sdk-model":"RecipientErasure"},"DeviceState":{"type":"string","enum":["active","invalid","pruned"]},"DeviceDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The device's id."},"channel":{"description":"The channel.","allOf":[{"$ref":"#/components/schemas/DeviceChannel"}]},"token_last6":{"type":"string","description":"The last 6 characters of the token (for `webpush`, of the endpoint). The full token is never returned.","example":"bb78ad"},"app_id":{"type":"string","description":"Bundle id, Firebase project id or site origin.","example":"com.example.app"},"environment":{"nullable":true,"description":"APNs environment; null for other channels.","allOf":[{"$ref":"#/components/schemas/ApnsEnvironment"}]},"platform":{"description":"The operating system.","allOf":[{"$ref":"#/components/schemas/DevicePlatform"}]},"timezone":{"type":"string","nullable":true,"description":"The device's IANA time zone.","example":"Europe/Lisbon"},"state":{"description":"`active` receives pushes; `invalid` was rejected by the provider; `pruned` was removed (the provider reported it dead, or it was evicted as the oldest of more than 20).","allOf":[{"$ref":"#/components/schemas/DeviceState"}]},"state_reason":{"type":"string","nullable":true,"description":"Why the device is not active (`evicted`, `unregistered`, `bad_device_token` …)."},"last_seen_at":{"type":"string","format":"date-time","description":"Last registration or refresh."},"moved_at":{"type":"string","format":"date-time","nullable":true,"description":"When the token last moved here from another recipient (a shared device signed in as someone else)."},"created_at":{"type":"string","format":"date-time","description":"When the device was first registered."},"updated_at":{"type":"string","format":"date-time","description":"Last change."}},"required":["id","channel","token_last6","app_id","environment","platform","timezone","state","state_reason","last_seen_at","moved_at","created_at","updated_at"],"x-pc-sdk-model":"Device"},"DeviceListResponseDto":{"type":"object","properties":{"data":{"description":"The recipient's devices, most recently seen first: at most 20 active plus any invalid or pruned ones not yet reaped.","type":"array","items":{"$ref":"#/components/schemas/DeviceDto"}}},"required":["data"],"x-pc-sdk-model":"DeviceList"},"PreferenceSetDto":{"type":"object","properties":{"external_id":{"type":"string","description":"The recipient external id."},"unsubscribed_all":{"type":"boolean","description":"Opted out of every non-critical notification."},"timezone":{"type":"string","nullable":true,"description":"IANA time zone."},"quiet_hours":{"nullable":true,"description":"Quiet window.","allOf":[{"$ref":"#/components/schemas/QuietHoursDto"}]},"preferences":{"description":"Cells the recipient set (anything else follows the category defaults).","type":"array","items":{"$ref":"#/components/schemas/PreferenceCellDto"}}},"required":["external_id","unsubscribed_all","timezone","quiet_hours","preferences"],"x-pc-sdk-model":"PreferenceSet"},"ReplacePreferencesDto":{"type":"object","properties":{"preferences":{"maxItems":500,"description":"The recipient's complete set of cells: cells not listed are removed (back to the category defaults).","type":"array","items":{"$ref":"#/components/schemas/PreferenceCellInputDto"}},"unsubscribed_all":{"type":"boolean","description":"Opt out of every non-critical notification; left out keeps the current value."}},"required":["preferences"]},"PreferenceCellPatchDto":{"type":"object","properties":{"scope":{"description":"`all` (every type), `category` or `type`.","allOf":[{"$ref":"#/components/schemas/PreferenceScope"}]},"key":{"type":"string","description":"The category or type key (required unless `scope` is `all`).","example":"billing"},"channel":{"description":"A channel, or `*` for every channel (not with `scope: all`: use `unsubscribed_all`). `push` covers iOS and Android.","allOf":[{"$ref":"#/components/schemas/PreferenceChannel"}]},"enabled":{"type":"boolean","description":"Whether the recipient wants it; `null` removes the cell (back to the default).","nullable":true}},"required":["scope","channel","enabled"]},"UpdatePreferencesDto":{"type":"object","properties":{"preferences":{"maxItems":500,"description":"Cells to set or (with `enabled: null`) remove; other cells are kept.","type":"array","items":{"$ref":"#/components/schemas/PreferenceCellPatchDto"}},"unsubscribed_all":{"type":"boolean","description":"Opt out of (or back into) every non-critical notification."},"timezone":{"type":"string","nullable":true,"description":"IANA time zone of the recipient (quiet hours).","example":"Europe/Lisbon"},"quiet_hours":{"nullable":true,"description":"Quiet window; null removes it.","allOf":[{"$ref":"#/components/schemas/QuietHoursDto"}]}}},"CategoryDefault":{"type":"string","enum":["on","off","mandatory"]},"CategoryDefaultsDto":{"type":"object","properties":{"inbox":{"default":"on","description":"In-app inbox. `on`: delivered unless the recipient opts out. `off`: only when the recipient opts in. `mandatory`: always delivered and shown locked in preference screens (opt-outs are refused).","allOf":[{"$ref":"#/components/schemas/CategoryDefault"}]},"email":{"default":"on","description":"Email.","allOf":[{"$ref":"#/components/schemas/CategoryDefault"}]},"push":{"default":"on","description":"Mobile push (APNs and FCM).","allOf":[{"$ref":"#/components/schemas/CategoryDefault"}]},"webpush":{"default":"on","description":"Web push.","allOf":[{"$ref":"#/components/schemas/CategoryDefault"}]}}},"CreateCategoryDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name, shown in preference screens.","maxLength":200,"example":"Security"},"description":{"type":"string","description":"What the category covers.","maxLength":1000,"nullable":true},"defaults":{"description":"Default per channel; a channel left out is `on`.","allOf":[{"$ref":"#/components/schemas/CategoryDefaultsDto"}]},"ordinal":{"type":"integer","description":"Sort order in preference screens (lower first).","default":0},"android_channel_id":{"type":"string","description":"Android notification channel id for FCM pushes of this category; null means the category key.","maxLength":100,"nullable":true},"transactional":{"type":"boolean","description":"Transactional categories (receipts, security notices) send email without a List-Unsubscribe header.","default":false},"key":{"type":"string","description":"Stable key, unique in the workspace: must start with a-z and use only a-z 0-9 _ . - (at most 100 characters).","example":"security"}},"required":["key"]},"CategoryDefaultsViewDto":{"type":"object","properties":{"inbox":{"description":"In-app inbox.","allOf":[{"$ref":"#/components/schemas/CategoryDefault"}]},"email":{"description":"Email.","allOf":[{"$ref":"#/components/schemas/CategoryDefault"}]},"push":{"description":"Mobile push (APNs and FCM).","allOf":[{"$ref":"#/components/schemas/CategoryDefault"}]},"webpush":{"description":"Web push.","allOf":[{"$ref":"#/components/schemas/CategoryDefault"}]}},"required":["inbox","email","push","webpush"],"x-pc-sdk-model":"CategoryDefaultsView"},"CategoryDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Category id."},"key":{"type":"string","description":"Stable key.","example":"security"},"name":{"type":"string","description":"Display name."},"description":{"type":"string","nullable":true,"description":"Description."},"defaults":{"description":"Default per channel.","allOf":[{"$ref":"#/components/schemas/CategoryDefaultsViewDto"}]},"ordinal":{"type":"integer","description":"Sort order."},"android_channel_id":{"type":"string","description":"Android notification channel id (the key unless set)."},"transactional":{"type":"boolean","description":"Whether email goes out without List-Unsubscribe."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last change."}},"required":["id","key","name","description","defaults","ordinal","android_channel_id","transactional","created_at","updated_at"],"x-pc-sdk-model":"Category"},"PaginationDto":{"type":"object","properties":{"next_cursor":{"type":"string","nullable":true,"description":"Pass as `cursor` to get the next page; null on the last page."},"has_more":{"type":"boolean","description":"Whether another page follows."}},"required":["next_cursor","has_more"],"x-pc-sdk-model":"Pagination"},"CategoryListResponseDto":{"type":"object","properties":{"data":{"description":"Categories by `ordinal`, then oldest first.","type":"array","items":{"$ref":"#/components/schemas/CategoryDto"}},"pagination":{"description":"The cursor for the next page.","allOf":[{"$ref":"#/components/schemas/PaginationDto"}]}},"required":["data","pagination"],"x-pc-sdk-model":"CategoryList"},"UpdateCategoryDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name, shown in preference screens.","maxLength":200,"example":"Security"},"description":{"type":"string","description":"What the category covers.","maxLength":1000,"nullable":true},"defaults":{"description":"Default per channel; a channel left out is `on`.","allOf":[{"$ref":"#/components/schemas/CategoryDefaultsDto"}]},"ordinal":{"type":"integer","description":"Sort order in preference screens (lower first).","default":0},"android_channel_id":{"type":"string","description":"Android notification channel id for FCM pushes of this category; null means the category key.","maxLength":100,"nullable":true},"transactional":{"type":"boolean","description":"Transactional categories (receipts, security notices) send email without a List-Unsubscribe header.","default":false}}},"NotifyChannel":{"type":"string","enum":["inbox","email","push","webpush"]},"FallbackStepDto":{"type":"object","properties":{"channel":{"description":"Channel tried at this step.","allOf":[{"$ref":"#/components/schemas/NotifyChannel"}]},"after_unread_seconds":{"type":"integer","minimum":60,"maximum":604800,"description":"Wait this long after the send and plan the step only if the inbox item is still unread (needs `inbox` in `always`), even when earlier steps succeeded. Without it the step runs when every delivery of the previous step failed or was skipped."}},"required":["channel"]},"RoutingDto":{"type":"object","properties":{"always":{"type":"array","description":"Channels planned for every recipient.","example":["inbox"],"items":{"$ref":"#/components/schemas/NotifyChannel"}},"fallback":{"maxItems":4,"description":"Tried in order when the previous step failed for the recipient (no device, no address, opted out, provider failure). `inbox` only as the last step.","type":"array","items":{"$ref":"#/components/schemas/FallbackStepDto"}}},"required":["always","fallback"]},"ThrottleDto":{"type":"object","properties":{"max":{"type":"integer","minimum":1,"maximum":1000,"description":"At most this many sends of the type per recipient…"},"per_seconds":{"type":"integer","minimum":1,"maximum":604800,"description":"…in this many seconds. Excess deliveries are skipped as `throttled`."}},"required":["max","per_seconds"]},"CreateNotificationTypeDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name.","maxLength":200,"example":"Invoice paid"},"description":{"type":"string","description":"What the notification is for.","maxLength":1000,"nullable":true},"category_key":{"type":"string","description":"Key of the category the type belongs to.","example":"billing"},"routing":{"description":"Which channels are tried, and in what order.","allOf":[{"$ref":"#/components/schemas/RoutingDto"}]},"critical":{"type":"boolean","description":"Critical types (security notices) ignore opt-outs and quiet hours and use their own rate budget. They never bypass hard bounces, spam complaints or Mail suppression.","default":false},"ttl_seconds":{"type":"integer","minimum":60,"maximum":604800,"default":86400,"description":"Deliveries not sent within this many seconds expire."},"default_locale":{"type":"string","description":"Locale used when the recipient locale has no template (BCP 47).","default":"en","example":"en"},"email_quiet_hours":{"type":"boolean","description":"Hold email during the recipient quiet hours too (push is always held, the inbox never).","default":false},"throttle":{"nullable":true,"description":"Per-recipient cap for this type; null removes it.","allOf":[{"$ref":"#/components/schemas/ThrottleDto"}]},"key":{"type":"string","description":"Key used when sending, unique in the workspace: must start with a-z and use only a-z 0-9 _ . - (at most 100 characters).","example":"invoice.paid"}},"required":["key"]},"FallbackStepViewDto":{"type":"object","properties":{"channel":{"description":"Channel.","allOf":[{"$ref":"#/components/schemas/NotifyChannel"}]},"after_unread_seconds":{"type":"integer","description":"Unread wait in seconds, when set."}},"required":["channel"],"x-pc-sdk-model":"FallbackStepView"},"RoutingViewDto":{"type":"object","properties":{"always":{"type":"array","description":"Always planned.","items":{"$ref":"#/components/schemas/NotifyChannel"}},"fallback":{"description":"Fallback steps in order.","type":"array","items":{"$ref":"#/components/schemas/FallbackStepViewDto"}}},"required":["always","fallback"],"x-pc-sdk-model":"RoutingView"},"ThrottleViewDto":{"type":"object","properties":{"max":{"type":"integer","description":"Sends per recipient…"},"per_seconds":{"type":"integer","description":"…per this many seconds."}},"required":["max","per_seconds"],"x-pc-sdk-model":"ThrottleView"},"TemplateSummaryDto":{"type":"object","properties":{"channel":{"description":"Channel.","allOf":[{"$ref":"#/components/schemas/NotifyChannel"}]},"locale":{"type":"string","description":"Locale (BCP 47).","example":"pt-PT"},"version":{"type":"integer","description":"Current version."},"updated_at":{"type":"string","format":"date-time","description":"Last change."}},"required":["channel","locale","version","updated_at"],"x-pc-sdk-model":"TemplateSummary"},"NotificationTypeDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Type id."},"key":{"type":"string","description":"Key used when sending.","example":"invoice.paid"},"name":{"type":"string","description":"Display name."},"description":{"type":"string","nullable":true,"description":"Description."},"category_id":{"type":"string","format":"uuid","description":"Category id."},"category_key":{"type":"string","description":"Category key."},"routing":{"description":"Routing.","allOf":[{"$ref":"#/components/schemas/RoutingViewDto"}]},"critical":{"type":"boolean","description":"Whether the type is critical."},"ttl_seconds":{"type":"integer","description":"Time to live of a delivery, in seconds."},"default_locale":{"type":"string","description":"Fallback locale."},"email_quiet_hours":{"type":"boolean","description":"Whether email is held during quiet hours."},"throttle":{"nullable":true,"description":"Per-recipient cap.","allOf":[{"$ref":"#/components/schemas/ThrottleViewDto"}]},"archived_at":{"type":"string","format":"date-time","nullable":true,"description":"When archived; archived types cannot be sent."},"version":{"type":"integer","description":"Increases on every change of the type."},"templates":{"description":"The templates (bodies under `…/templates/{channel}/{locale}`).","type":"array","items":{"$ref":"#/components/schemas/TemplateSummaryDto"}},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last change."}},"required":["id","key","name","description","category_id","category_key","routing","critical","ttl_seconds","default_locale","email_quiet_hours","throttle","archived_at","version","templates","created_at","updated_at"],"x-pc-sdk-model":"NotificationType"},"ReplaceNotificationTypeDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name.","maxLength":200,"example":"Invoice paid"},"description":{"type":"string","description":"What the notification is for.","maxLength":1000,"nullable":true},"category_key":{"type":"string","description":"Key of the category; it must exist (create categories first, or apply an export).","example":"billing"},"routing":{"description":"Routing.","allOf":[{"$ref":"#/components/schemas/RoutingDto"}]},"critical":{"type":"boolean","description":"Critical types (security notices) ignore opt-outs and quiet hours and use their own rate budget. They never bypass hard bounces, spam complaints or Mail suppression.","default":false},"ttl_seconds":{"type":"integer","minimum":60,"maximum":604800,"default":86400,"description":"Deliveries not sent within this many seconds expire."},"default_locale":{"type":"string","description":"Locale used when the recipient locale has no template (BCP 47).","default":"en","example":"en"},"email_quiet_hours":{"type":"boolean","description":"Hold email during the recipient quiet hours too (push is always held, the inbox never).","default":false},"throttle":{"nullable":true,"description":"Per-recipient cap for this type; null removes it.","allOf":[{"$ref":"#/components/schemas/ThrottleDto"}]},"templates":{"type":"array","maxItems":100,"items":{"type":"object","required":["channel","locale","body"],"properties":{"channel":{"type":"string","enum":["inbox","email","push","webpush"],"description":"Channel."},"locale":{"type":"string","description":"Locale (BCP 47)."},"body":{"type":"object","additionalProperties":true,"description":"The channel template, field names as shown (stored and returned exactly as sent). Text fields use the Mustache subset `{{var}}`, `{{#section}}`, `{{^inverted}}` (no partials). `inbox`: `{title, body (Markdown), action_url?, avatar_url?, actions?: [{label, url? | action_key?}] (≤ 2)}`. `email`: `{mail_template_name, data_mapping?}` (the HTML lives in Mail; `data_mapping` maps Mail variables to Notify variables). `push`: `{title?, body, url?, image_url?, badge?: \"unread_count\" | number, sound?, thread_id?, data_only?, apns_overrides?, fcm_overrides?}`. `webpush`: `{title, body?, url?, icon?, tag?}`. URL fields must be https (http only for localhost). At most 32 KB."}}},"description":"The complete template set: templates not listed are deleted, unchanged ones keep their version. Each `body` is kept exactly as sent."}},"required":["templates"]},"NotificationTypeListResponseDto":{"type":"object","properties":{"data":{"description":"Types, newest first.","type":"array","items":{"$ref":"#/components/schemas/NotificationTypeDto"}},"pagination":{"description":"The cursor for the next page.","allOf":[{"$ref":"#/components/schemas/PaginationDto"}]}},"required":["data","pagination"],"x-pc-sdk-model":"NotificationTypeList"},"UpdateNotificationTypeDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name.","maxLength":200,"example":"Invoice paid"},"description":{"type":"string","description":"What the notification is for.","maxLength":1000,"nullable":true},"category_key":{"type":"string","description":"Key of the category the type belongs to (preferences are per category).","example":"billing"},"routing":{"description":"Which channels are tried, and in what order.","allOf":[{"$ref":"#/components/schemas/RoutingDto"}]},"critical":{"type":"boolean","description":"Critical types (security notices) ignore opt-outs and quiet hours and use their own rate budget. They never bypass hard bounces, spam complaints or Mail suppression.","default":false},"ttl_seconds":{"type":"integer","minimum":60,"maximum":604800,"default":86400,"description":"Deliveries not sent within this many seconds expire."},"default_locale":{"type":"string","description":"Locale used when the recipient locale has no template (BCP 47).","default":"en","example":"en"},"email_quiet_hours":{"type":"boolean","description":"Hold email during the recipient quiet hours too (push is always held, the inbox never).","default":false},"throttle":{"nullable":true,"description":"Per-recipient cap for this type; null removes it.","allOf":[{"$ref":"#/components/schemas/ThrottleDto"}]}}},"PreviewDto":{"type":"object","properties":{"data":{"type":"object","additionalProperties":true,"description":"Sample `data`, as a send would pass it (keys kept as sent). At most 16 KB.","example":{"amount":"12.00","invoice_id":"inv_123"}},"recipient":{"type":"string","description":"Render as this recipient (its locale chain and fields under `recipient.*`). Nothing is sent.","example":"user_123"},"locale":{"type":"string","description":"Locale to render when no recipient is given (or to override it).","example":"pt-PT"}}},"PreviewChannelDto":{"type":"object","properties":{"channel":{"description":"Channel.","allOf":[{"$ref":"#/components/schemas/NotifyChannel"}]},"locale":{"type":"string","nullable":true,"description":"The template locale used (exact → language → the type default), or null when none exists."},"rendered":{"type":"object","additionalProperties":true,"nullable":true,"description":"The rendered body (email: the Mail template name and mapped data)."},"missing_variables":{"description":"Variables the template uses that the data does not have (they render empty).","type":"array","items":{"type":"string"}},"warnings":{"description":"Problems such as `unsafe_url:<field>` (a URL field rendered to a non-https URL and is dropped).","type":"array","items":{"type":"string"}}},"required":["channel","locale","rendered","missing_variables","warnings"],"x-pc-sdk-model":"PreviewChannel"},"PreviewResponseDto":{"type":"object","properties":{"channels":{"description":"One entry per routed channel.","type":"array","items":{"$ref":"#/components/schemas/PreviewChannelDto"}},"cross_channel_mismatches":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string","enum":["inbox","email","push","webpush"]}},"description":"Variables used by some channels but not others: variable → the channels missing it."}},"required":["channels","cross_channel_mismatches"],"x-pc-sdk-model":"Preview"},"TemplateDto":{"type":"object","properties":{"channel":{"description":"Channel.","allOf":[{"$ref":"#/components/schemas/NotifyChannel"}]},"locale":{"type":"string","description":"Locale (BCP 47).","example":"pt-PT"},"version":{"type":"integer","description":"Current version."},"updated_at":{"type":"string","format":"date-time","description":"Last change."},"body":{"type":"object","additionalProperties":true,"description":"The channel template, field names as shown (stored and returned exactly as sent). Text fields use the Mustache subset `{{var}}`, `{{#section}}`, `{{^inverted}}` (no partials). `inbox`: `{title, body (Markdown), action_url?, avatar_url?, actions?: [{label, url? | action_key?}] (≤ 2)}`. `email`: `{mail_template_name, data_mapping?}` (the HTML lives in Mail; `data_mapping` maps Mail variables to Notify variables). `push`: `{title?, body, url?, image_url?, badge?: \"unread_count\" | number, sound?, thread_id?, data_only?, apns_overrides?, fcm_overrides?}`. `webpush`: `{title, body?, url?, icon?, tag?}`. URL fields must be https (http only for localhost). At most 32 KB."},"variables":{"description":"Variables the template uses.","type":"array","items":{"type":"string"}}},"required":["channel","locale","version","updated_at","body","variables"],"x-pc-sdk-model":"Template"},"PutTemplateDto":{"type":"object","properties":{"body":{"type":"object","additionalProperties":true,"description":"The channel template, field names as shown (stored and returned exactly as sent). Text fields use the Mustache subset `{{var}}`, `{{#section}}`, `{{^inverted}}` (no partials). `inbox`: `{title, body (Markdown), action_url?, avatar_url?, actions?: [{label, url? | action_key?}] (≤ 2)}`. `email`: `{mail_template_name, data_mapping?}` (the HTML lives in Mail; `data_mapping` maps Mail variables to Notify variables). `push`: `{title?, body, url?, image_url?, badge?: \"unread_count\" | number, sound?, thread_id?, data_only?, apns_overrides?, fcm_overrides?}`. `webpush`: `{title, body?, url?, icon?, tag?}`. URL fields must be https (http only for localhost). At most 32 KB.","example":{"title":"Invoice {{invoice_id}} paid","body":"We received **{{amount}}**."}}},"required":["body"]},"TemplateVersionDto":{"type":"object","properties":{"version":{"type":"integer","description":"Version number."},"body":{"type":"object","additionalProperties":true,"description":"The body saved in that version."},"actor":{"type":"string","nullable":true,"description":"Who saved it (`pak:<id>` or `platform:<user id>`)."},"created_at":{"type":"string","format":"date-time","description":"Saved."}},"required":["version","body","actor","created_at"],"x-pc-sdk-model":"TemplateVersion"},"TemplateVersionListDto":{"type":"object","properties":{"data":{"description":"The last 20 versions, newest first.","type":"array","items":{"$ref":"#/components/schemas/TemplateVersionDto"}}},"required":["data"],"x-pc-sdk-model":"TemplateVersionList"},"RevertTemplateDto":{"type":"object","properties":{"version":{"type":"integer","minimum":1,"description":"The version to restore (from `…/versions`). The restore is saved as a new version."}},"required":["version"]},"ExportNotificationTypeDto":{"type":"object","properties":{"key":{"type":"string","description":"Key."},"name":{"type":"string","description":"Display name."},"description":{"type":"string","nullable":true,"description":"Description."},"category_key":{"type":"string","description":"Category key."},"routing":{"description":"Routing.","allOf":[{"$ref":"#/components/schemas/RoutingViewDto"}]},"critical":{"type":"boolean","description":"Critical."},"ttl_seconds":{"type":"integer","description":"TTL in seconds."},"default_locale":{"type":"string","description":"Fallback locale."},"email_quiet_hours":{"type":"boolean","description":"Email held in quiet hours."},"throttle":{"nullable":true,"description":"Per-recipient cap.","allOf":[{"$ref":"#/components/schemas/ThrottleViewDto"}]},"archived_at":{"type":"string","format":"date-time","nullable":true,"description":"When archived."},"templates":{"type":"array","items":{"type":"object","required":["channel","locale","body"],"properties":{"channel":{"type":"string","enum":["inbox","email","push","webpush"],"description":"Channel."},"locale":{"type":"string","description":"Locale."},"body":{"type":"object","additionalProperties":true,"description":"Body, as stored."}}},"description":"Every template, in the shape `PUT /notification-types/by-key/{key}` accepts."}},"required":["key","name","description","category_key","routing","critical","ttl_seconds","default_locale","email_quiet_hours","throttle","archived_at","templates"],"x-pc-sdk-model":"ExportNotificationType"},"ExportSettingsDto":{"type":"object","properties":{"default_timezone":{"type":"string","nullable":true,"description":"Default time zone."},"default_quiet_hours":{"type":"object","additionalProperties":true,"nullable":true,"description":"Default quiet hours `{start, end}`."},"client_origins":{"description":"Origins allowed to call the client API.","type":"array","items":{"type":"string"}},"email_enabled":{"type":"boolean","description":"Whether the email channel is on."}},"required":["default_timezone","default_quiet_hours","client_origins","email_enabled"],"x-pc-sdk-model":"ExportSettings"},"ExportDto":{"type":"object","properties":{"categories":{"description":"Every category.","type":"array","items":{"$ref":"#/components/schemas/CategoryDto"}},"notification_types":{"description":"Every notification type with its templates.","type":"array","items":{"$ref":"#/components/schemas/ExportNotificationTypeDto"}},"settings":{"description":"Workspace settings (defaults written by the settings API).","allOf":[{"$ref":"#/components/schemas/ExportSettingsDto"}]},"exported_at":{"type":"string","format":"date-time","description":"When the export was made."}},"required":["categories","notification_types","settings","exported_at"],"x-pc-sdk-model":"Export"},"SendStatus":{"type":"string","enum":["accepted","scheduled"]},"NotifyDeliveryReason":{"type":"string","enum":["suppressed_preference","unsubscribed_all","held_quiet_hours","scheduled_send","no_device","no_address","email_invalid","email_complained","email_paused","email_budget","email_backlog_full","channel_not_configured","channel_restricted","type_archived","credential_invalid","template_missing","rate_limited","throttled","read_in_inbox","awaiting_unread","actor_excluded","recipient_not_found","recipient_erased","mail_suppressed","mail_refused","pruned","payload_too_large","credential_error","circuit_open","expired","cancelled","provider_error"],"description":"Why a delivery was skipped, held, retried or failed. Open enum: new reasons may be added.\n\n- `suppressed_preference`: The recipient turned this channel off for this type or its category. Fix: Nothing to fix: the recipient chose this. Mark the category channel `mandatory` only for messages they must receive.\n- `unsubscribed_all`: The recipient unsubscribed from all non-critical notifications. Fix: Only types marked `critical` still reach them; otherwise the recipient has to opt back in.\n- `held_quiet_hours`: Held until the recipient's quiet hours end. Fix: It is sent when the window ends. Mark the type `critical` if it must not wait.\n- `scheduled_send`: Waiting for the send time given in `send_at` or `delay_seconds`. Fix: It is sent at that time. Cancel it with the notification id or its `cancellation_key`.\n- `no_device`: The recipient has no active device for this channel. Fix: Register the device token from your app (`POST …/devices`) after the user grants permission.\n- `no_address`: The recipient has no email address. Fix: Set `email` on the recipient, or pass it inline in the send.\n- `email_invalid`: An earlier email to this address hard-bounced. Fix: Update the recipient's email address; a new address clears the bounce.\n- `email_complained`: The recipient marked an earlier email as spam. Fix: Email stays off for this recipient; reach them through another channel.\n- `email_paused`: Email is paused for this workspace (high bounce or complaint rate). Fix: Check your sending in the console and resume email in Notify settings.\n- `email_budget`: The workspace email budget is spent for now; the email waits for the next window. Fix: It is sent automatically when the budget allows. Ask us to raise the budget if this is routine.\n- `email_backlog_full`: The workspace already holds 10,000 emails waiting for budget, so this one was not queued. Fix: Send fewer emails or spread them out; ask us to raise the budget if this volume is routine.\n- `channel_not_configured`: The channel is not set up in this workspace. Fix: Upload APNs or FCM credentials, generate web push keys, or enable Mail with a verified sender.\n- `channel_restricted`: The send's `channels` list left this channel out. Fix: Add the channel to `channels`, or omit `channels` to use every channel of the type.\n- `type_archived`: The notification type is archived. Fix: Unarchive the type or send a different one.\n- `credential_invalid`: No valid credential matches this device's app or environment. Fix: Upload the APNs key or FCM service account for this bundle id or project, in the right environment.\n- `template_missing`: The type has no template for this channel. Fix: Add a template for the channel (any locale) to the notification type.\n- `rate_limited`: The recipient already received the per-minute maximum of notifications. Fix: Send fewer notifications to one person, or batch them; critical types have their own budget.\n- `throttled`: The type's own throttle for this recipient was reached. Fix: Raise or remove `throttle` on the notification type if every message must go out.\n- `read_in_inbox`: The recipient read the inbox item first, so the follow-up was not needed. Fix: Nothing to fix: this is how `after_unread_seconds` works.\n- `awaiting_unread`: A follow-up step waiting to see whether the recipient reads the inbox item first. Fix: Nothing to fix: it is sent when the `after_unread_seconds` timer ends and the item is still unread, and skipped as `read_in_inbox` otherwise.\n- `actor_excluded`: The recipient is the actor who caused the notification. Fix: Pass `exclude_actor: false` to notify the actor too.\n- `recipient_not_found`: No recipient with this external id exists in the workspace. Fix: Create the recipient first (`PUT /recipients/{external_id}`), pass it inline with an email, or send `create_missing: true`.\n- `recipient_erased`: The recipient was erased in the last 24 hours and cannot be recreated by a send yet. Fix: Wait 24 hours after the erasure, then create the recipient again.\n- `mail_suppressed`: ProductCraft Mail suppressed the address (earlier bounces or complaints). Fix: Check the suppression list in Mail; remove the address only if you know it is valid.\n- `mail_refused`: ProductCraft Mail refused the message (the code after the colon says why). Fix: Look up the Mail error code; usually the template name or sender needs fixing.\n- `pruned`: The push provider said the device token is no longer valid; the device was removed. Fix: Nothing to fix: the app registers a fresh token the next time it opens.\n- `payload_too_large`: The rendered push payload is over the provider's size limit. Fix: Shorten the template or move data out of the push payload.\n- `credential_error`: The push provider rejected the workspace credential. Fix: Upload a fresh APNs key or FCM service account; the channel resumes as soon as it validates.\n- `circuit_open`: The provider is failing for this credential, so sends are paused briefly and retried. Fix: Nothing to fix: it retries on its own. Check the provider's status page if it lasts.\n- `expired`: The notification's time to live passed before it could be delivered. Fix: Raise `ttl_seconds` on the type if late delivery is still useful.\n- `cancelled`: The notification was cancelled, or the recipient was erased, before delivery. Fix: Nothing to fix.\n- `provider_error`: The provider kept failing until retries ran out. Fix: Check the attempts for the provider status and code.","x-pc-reasons":{"suppressed_preference":{"sentence":"The recipient turned this channel off for this type or its category.","fix":"Nothing to fix: the recipient chose this. Mark the category channel `mandatory` only for messages they must receive."},"unsubscribed_all":{"sentence":"The recipient unsubscribed from all non-critical notifications.","fix":"Only types marked `critical` still reach them; otherwise the recipient has to opt back in."},"held_quiet_hours":{"sentence":"Held until the recipient's quiet hours end.","fix":"It is sent when the window ends. Mark the type `critical` if it must not wait."},"scheduled_send":{"sentence":"Waiting for the send time given in `send_at` or `delay_seconds`.","fix":"It is sent at that time. Cancel it with the notification id or its `cancellation_key`."},"no_device":{"sentence":"The recipient has no active device for this channel.","fix":"Register the device token from your app (`POST …/devices`) after the user grants permission."},"no_address":{"sentence":"The recipient has no email address.","fix":"Set `email` on the recipient, or pass it inline in the send."},"email_invalid":{"sentence":"An earlier email to this address hard-bounced.","fix":"Update the recipient's email address; a new address clears the bounce."},"email_complained":{"sentence":"The recipient marked an earlier email as spam.","fix":"Email stays off for this recipient; reach them through another channel."},"email_paused":{"sentence":"Email is paused for this workspace (high bounce or complaint rate).","fix":"Check your sending in the console and resume email in Notify settings."},"email_budget":{"sentence":"The workspace email budget is spent for now; the email waits for the next window.","fix":"It is sent automatically when the budget allows. Ask us to raise the budget if this is routine."},"email_backlog_full":{"sentence":"The workspace already holds 10,000 emails waiting for budget, so this one was not queued.","fix":"Send fewer emails or spread them out; ask us to raise the budget if this volume is routine."},"channel_not_configured":{"sentence":"The channel is not set up in this workspace.","fix":"Upload APNs or FCM credentials, generate web push keys, or enable Mail with a verified sender."},"channel_restricted":{"sentence":"The send's `channels` list left this channel out.","fix":"Add the channel to `channels`, or omit `channels` to use every channel of the type."},"type_archived":{"sentence":"The notification type is archived.","fix":"Unarchive the type or send a different one."},"credential_invalid":{"sentence":"No valid credential matches this device's app or environment.","fix":"Upload the APNs key or FCM service account for this bundle id or project, in the right environment."},"template_missing":{"sentence":"The type has no template for this channel.","fix":"Add a template for the channel (any locale) to the notification type."},"rate_limited":{"sentence":"The recipient already received the per-minute maximum of notifications.","fix":"Send fewer notifications to one person, or batch them; critical types have their own budget."},"throttled":{"sentence":"The type's own throttle for this recipient was reached.","fix":"Raise or remove `throttle` on the notification type if every message must go out."},"read_in_inbox":{"sentence":"The recipient read the inbox item first, so the follow-up was not needed.","fix":"Nothing to fix: this is how `after_unread_seconds` works."},"awaiting_unread":{"sentence":"A follow-up step waiting to see whether the recipient reads the inbox item first.","fix":"Nothing to fix: it is sent when the `after_unread_seconds` timer ends and the item is still unread, and skipped as `read_in_inbox` otherwise."},"actor_excluded":{"sentence":"The recipient is the actor who caused the notification.","fix":"Pass `exclude_actor: false` to notify the actor too."},"recipient_not_found":{"sentence":"No recipient with this external id exists in the workspace.","fix":"Create the recipient first (`PUT /recipients/{external_id}`), pass it inline with an email, or send `create_missing: true`."},"recipient_erased":{"sentence":"The recipient was erased in the last 24 hours and cannot be recreated by a send yet.","fix":"Wait 24 hours after the erasure, then create the recipient again."},"mail_suppressed":{"sentence":"ProductCraft Mail suppressed the address (earlier bounces or complaints).","fix":"Check the suppression list in Mail; remove the address only if you know it is valid."},"mail_refused":{"sentence":"ProductCraft Mail refused the message (the code after the colon says why).","fix":"Look up the Mail error code; usually the template name or sender needs fixing."},"pruned":{"sentence":"The push provider said the device token is no longer valid; the device was removed.","fix":"Nothing to fix: the app registers a fresh token the next time it opens."},"payload_too_large":{"sentence":"The rendered push payload is over the provider's size limit.","fix":"Shorten the template or move data out of the push payload."},"credential_error":{"sentence":"The push provider rejected the workspace credential.","fix":"Upload a fresh APNs key or FCM service account; the channel resumes as soon as it validates."},"circuit_open":{"sentence":"The provider is failing for this credential, so sends are paused briefly and retried.","fix":"Nothing to fix: it retries on its own. Check the provider's status page if it lasts."},"expired":{"sentence":"The notification's time to live passed before it could be delivered.","fix":"Raise `ttl_seconds` on the type if late delivery is still useful."},"cancelled":{"sentence":"The notification was cancelled, or the recipient was erased, before delivery.","fix":"Nothing to fix."},"provider_error":{"sentence":"The provider kept failing until retries ran out.","fix":"Check the attempts for the provider status and code."}}},"SkippedRecipientDto":{"type":"object","properties":{"external_id":{"type":"string","description":"The external id that was not notified.","example":"user_999"},"reason":{"description":"Why (`recipient_not_found`, `recipient_erased`, `actor_excluded`).","allOf":[{"$ref":"#/components/schemas/NotifyDeliveryReason"}]}},"required":["external_id","reason"],"x-pc-sdk-model":"SkippedRecipient"},"SendNotificationResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The notification id."},"type":{"type":"string","description":"The notification type key.","example":"invoice.paid"},"recipient_count":{"type":"integer","description":"Recipients that will be notified."},"status":{"description":"`scheduled` when `send_at`/`delay_seconds` holds it.","allOf":[{"$ref":"#/components/schemas/SendStatus"}]},"skipped":{"description":"Recipients that were left out, with the reason.","type":"array","items":{"$ref":"#/components/schemas/SkippedRecipientDto"}}},"required":["id","type","recipient_count","status","skipped"],"x-pc-sdk-model":"SendNotification"},"PlanDecision":{"type":"string","enum":["deliver","schedule","skip"]},"PlanRowDto":{"type":"object","properties":{"recipient":{"type":"string","description":"The recipient external id.","example":"user_123"},"channel":{"description":"The channel.","allOf":[{"$ref":"#/components/schemas/NotifyChannel"}]},"decision":{"description":"`deliver`, `schedule` (held, sent later) or `skip`.","allOf":[{"$ref":"#/components/schemas/PlanDecision"}]},"reason":{"nullable":true,"description":"Why it is skipped or held; null when it is delivered.","allOf":[{"$ref":"#/components/schemas/NotifyDeliveryReason"}]},"explanation":{"type":"string","nullable":true,"description":"What the reason means and how to fix it."},"target":{"type":"string","description":"`inbox`, `email:<address hash>`, `device:<device id>`, or the channel when there is no target."},"fallback_step":{"type":"integer","description":"0 for an `always` channel; n for the n-th fallback step."},"not_before":{"type":"string","format":"date-time","description":"For `held_quiet_hours`: when the recipient quiet hours end. A real send releases the delivery up to 10 minutes after this, so a large morning backlog spreads out."}},"required":["recipient","channel","decision","reason","explanation","target","fallback_step"],"x-pc-sdk-model":"PlanRow"},"DryRunResponseDto":{"type":"object","properties":{"dry_run":{"type":"boolean","description":"Always true: nothing was stored or sent."},"type":{"type":"string","description":"The notification type key."},"plan":{"description":"One row per recipient, channel and target, in recipient order.","type":"array","items":{"$ref":"#/components/schemas/PlanRowDto"}},"skipped":{"description":"Recipients that would be left out.","type":"array","items":{"$ref":"#/components/schemas/SkippedRecipientDto"}}},"required":["dry_run","type","plan","skipped"],"x-pc-sdk-model":"DryRun"},"InlineRecipientDto":{"type":"object","properties":{"auth_user_id":{"type":"string","description":"Link to an Auth end user (its user id) in a ProductCraft Auth app. At most one recipient per end user in a workspace. The id is not looked up: link the id your Auth app gave you.","format":"uuid","nullable":true},"email":{"type":"string","description":"Email address for the email channel.","format":"email","maxLength":320,"nullable":true,"example":"ada@example.com"},"phone":{"type":"string","description":"Phone number in E.164 form (`+351912345678`), kept for the SMS channel planned after v1.","nullable":true,"example":"+351912345678"},"name":{"type":"string","description":"Display name.","maxLength":255,"nullable":true,"example":"Ada Lovelace"},"locale":{"type":"string","description":"BCP 47 language tag; templates fall back from `pt-PT` to `pt` to the type default.","nullable":true,"example":"pt-PT"},"timezone":{"type":"string","description":"IANA time zone, used for quiet hours.","nullable":true,"example":"Europe/Lisbon"},"quiet_hours":{"nullable":true,"description":"Hours when push and web push are held until the window ends (the inbox is never held).","allOf":[{"$ref":"#/components/schemas/QuietHoursDto"}]},"properties":{"type":"object","additionalProperties":true,"description":"Your own fields, returned exactly as sent (keys are not re-cased). At most 4 KB as JSON. Replaced whole when present.","example":{"plan":"pro","team_id":"team_42"}},"external_id":{"type":"string","description":"Your user id for the recipient.","pattern":"^[A-Za-z0-9._:@|-]{1,255}$","example":"user_456"}},"required":["external_id"]},"ActorDto":{"type":"object","properties":{"external_id":{"type":"string","description":"The recipient who caused the notification. Their name and avatar fill the inbox item, and with `exclude_actor` (default true) they are not notified.","example":"user_9"},"name":{"type":"string","description":"Display name shown in the inbox (defaults to the actor recipient's name).","maxLength":200,"example":"Billing"},"avatar_url":{"type":"string","description":"Avatar image URL (https).","format":"uri","maxLength":2048}}},"SendNotificationDto":{"type":"object","properties":{"type":{"type":"string","description":"The notification type key.","example":"invoice.paid","pattern":"^[a-z][a-z0-9_.-]{0,99}$"},"recipients":{"type":"array","description":"Who to notify: up to 1,000 external ids, or objects with an `external_id` and profile fields (created or updated with the send). Unknown ids do not fail the call: they come back in `skipped` (pass `create_missing: true` to create bare recipients instead).","items":{"anyOf":[{"type":"string","pattern":"^[A-Za-z0-9._:@|-]{1,255}$"},{"$ref":"#/components/schemas/InlineRecipientDto"}]},"minItems":1,"maxItems":1000,"example":["user_123",{"external_id":"user_456","email":"a@example.com","locale":"pt-PT"}]},"data":{"type":"object","additionalProperties":true,"description":"Template variables, exactly as sent (keys are not re-cased). At most 16 KB as JSON.","example":{"amount":"12.00","invoice_url":"https://example.com/i/123"}},"channels":{"type":"array","description":"Only these channels (a subset of the type's routing).","items":{"$ref":"#/components/schemas/NotifyChannel"}},"actor":{"description":"Who caused the notification.","allOf":[{"$ref":"#/components/schemas/ActorDto"}]},"exclude_actor":{"type":"boolean","description":"Leave the actor out of the recipients (default true when `actor.external_id` is set).","default":true},"send_at":{"type":"string","description":"Send at this time instead of now (UTC, ISO 8601), at most the type TTL ahead. Not with `delay_seconds`.","format":"date-time"},"delay_seconds":{"type":"integer","minimum":1,"maximum":604800,"description":"Send this many seconds from now (at most the type TTL). Not with `send_at`."},"cancellation_key":{"type":"string","description":"Your key for cancelling later without storing our id (`POST /notifications/cancel`).","maxLength":255,"example":"invite:inv_77"},"tenant":{"type":"string","description":"Your tenant (organisation, team): inbox items can be filtered by it.","maxLength":100,"example":"org_42"},"tags":{"type":"object","additionalProperties":{"type":"string","maxLength":200},"description":"Up to 10 string pairs, searchable in the message log (`GET /notifications?tag=key:value`). Keys: 1-64 of `A-Z a-z 0-9 _ . : -`.","example":{"invoice":"inv_123"}},"create_missing":{"type":"boolean","description":"Create a bare recipient for each unknown external id instead of skipping it.","default":false},"dry_run":{"type":"boolean","description":"Plan and explain per recipient and channel without storing or sending anything (no `Idempotency-Key` needed, no rate limit used).","default":false}},"required":["type","recipients"]},"NotificationStatus":{"type":"string","enum":["scheduled","accepted","planned","cancelled"]},"NotificationActorDto":{"type":"object","properties":{"external_id":{"type":"string","nullable":true,"description":"The actor recipient external id."},"name":{"type":"string","nullable":true,"description":"Display name."},"avatar_url":{"type":"string","nullable":true,"description":"Avatar URL."}},"required":["external_id","name","avatar_url"],"x-pc-sdk-model":"NotificationActor"},"ChannelCountsDto":{"type":"object","properties":{"channel":{"description":"The channel.","allOf":[{"$ref":"#/components/schemas/NotifyChannel"}]},"states":{"type":"object","additionalProperties":{"type":"integer"},"description":"Deliveries per state (`pending`, `sent`, `delivered`, `failed`, `skipped`, …).","example":{"delivered":2,"skipped":1}}},"required":["channel","states"],"x-pc-sdk-model":"ChannelCounts"},"NotificationDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The notification id."},"type":{"type":"string","description":"The notification type key."},"status":{"description":"`scheduled` (waiting for `send_at`), `accepted` (planning), `planned`, or `cancelled`.","allOf":[{"$ref":"#/components/schemas/NotificationStatus"}]},"recipient_count":{"type":"integer","description":"Recipients notified."},"skipped_count":{"type":"integer","description":"Recipients left out at accept."},"channels":{"type":"array","nullable":true,"description":"The channel restriction, if the send had one.","items":{"$ref":"#/components/schemas/NotifyChannel"}},"tenant":{"type":"string","nullable":true,"description":"Tenant."},"tags":{"type":"object","additionalProperties":{"type":"string"},"description":"Tags, exactly as sent."},"actor":{"nullable":true,"description":"Who caused it.","allOf":[{"$ref":"#/components/schemas/NotificationActorDto"}]},"exclude_actor":{"type":"boolean","description":"Whether the actor was left out of the recipients."},"cancellation_key":{"type":"string","nullable":true,"description":"The cancellation key."},"idempotency_key":{"type":"string","nullable":true,"description":"The send's `Idempotency-Key`."},"caller":{"type":"string","description":"Who sent it: `pak:<id>`, `platform:<user id>`."},"send_at":{"type":"string","format":"date-time","nullable":true,"description":"When it is (or was) released."},"expires_at":{"type":"string","format":"date-time","description":"Deliveries not made by then end `failed: expired`."},"planned_at":{"type":"string","format":"date-time","nullable":true,"description":"When every recipient was planned."},"cancelled_at":{"type":"string","format":"date-time","nullable":true,"description":"When it was cancelled."},"retracted_at":{"type":"string","format":"date-time","nullable":true,"description":"When its inbox items were retracted."},"created_at":{"type":"string","format":"date-time","description":"Accepted."},"data":{"type":"object","additionalProperties":true,"description":"The template data, exactly as sent (only on retrieve)."},"counts":{"description":"Deliveries per channel and state: what became of the send (on list and retrieve).","type":"array","items":{"$ref":"#/components/schemas/ChannelCountsDto"}}},"required":["id","type","status","recipient_count","skipped_count","channels","tenant","tags","actor","exclude_actor","cancellation_key","idempotency_key","caller","send_at","expires_at","planned_at","cancelled_at","retracted_at","created_at"],"x-pc-sdk-model":"Notification"},"ListPaginationDto":{"type":"object","properties":{"next_cursor":{"type":"string","nullable":true,"description":"Pass as `cursor` to get the next page; null on the last page."},"has_more":{"type":"boolean","description":"Whether another page follows."}},"required":["next_cursor","has_more"],"x-pc-sdk-model":"ListPagination"},"NotificationListResponseDto":{"type":"object","properties":{"data":{"description":"Notifications, newest first.","type":"array","items":{"$ref":"#/components/schemas/NotificationDto"}},"pagination":{"description":"The cursor for the next page.","allOf":[{"$ref":"#/components/schemas/ListPaginationDto"}]}},"required":["data","pagination"],"x-pc-sdk-model":"NotificationList"},"CancelByKeyDto":{"type":"object","properties":{"cancellation_key":{"type":"string","description":"The `cancellation_key` given at send.","example":"invite:inv_77"}},"required":["cancellation_key"]},"CancelByKeyResponseDto":{"type":"object","properties":{"cancelled":{"description":"Notifications that were cancelled (empty when none was live).","type":"array","items":{"type":"string","format":"uuid"}},"deliveries_cancelled":{"type":"integer","description":"Deliveries moved to `skipped: cancelled`."}},"required":["cancelled","deliveries_cancelled"],"x-pc-sdk-model":"CancelByKey"},"RetractResponseDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The notification id."},"items_retracted":{"type":"integer","description":"Inbox items archived by this call (0 when already retracted)."}},"required":["id","items_retracted"],"x-pc-sdk-model":"Retract"},"DeliveryState":{"type":"string","enum":["pending","scheduled","sending","retry","sent","delivered","failed","bounced","skipped","pruned"]},"RenderedPushDto":{"type":"object","properties":{"title":{"type":"string","description":"Rendered title (at most 512 bytes)."},"body":{"type":"string","description":"Rendered body (at most 512 bytes)."}},"required":["title","body"],"x-pc-sdk-model":"RenderedPush"},"DeliveryDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The delivery id."},"notification_id":{"type":"string","format":"uuid","description":"The notification."},"recipient_id":{"type":"string","format":"uuid","description":"The recipient id."},"recipient_external_id":{"type":"string","nullable":true,"description":"The recipient external id (null after an erasure)."},"channel":{"description":"The channel.","allOf":[{"$ref":"#/components/schemas/NotifyChannel"}]},"provider":{"type":"string","nullable":true,"description":"`inbox`, `mail`, `apns`, `fcm` or `webpush`."},"device_id":{"type":"string","format":"uuid","nullable":true,"description":"The device, for push and web push."},"target":{"type":"string","description":"`inbox`, `email:<address hash>` or `device:<id>`; `email:erased:<delivery id>` once the recipient was erased."},"fallback_step":{"type":"integer","description":"0 for an `always` channel; n for the n-th fallback step."},"is_fallback":{"type":"boolean","description":"Planned because an earlier step failed."},"state":{"description":"The state.","allOf":[{"$ref":"#/components/schemas/DeliveryState"}]},"reason":{"nullable":true,"description":"Why it was skipped, held, retried or failed (`mail_refused:<code>` carries Mail's code).","allOf":[{"$ref":"#/components/schemas/NotifyDeliveryReason"}]},"explanation":{"type":"string","nullable":true,"description":"What the reason means and how to fix it."},"attempts":{"type":"integer","description":"Provider calls made."},"next_attempt_at":{"type":"string","format":"date-time","nullable":true,"description":"Next try (retry, scheduled)."},"provider_message_id":{"type":"string","nullable":true,"description":"Provider message id (APNs `apns-id`, FCM message name, Mail message id)."},"provider_unique_id":{"type":"string","nullable":true,"description":"APNs `apns-unique-id` (sandbox only)."},"rendered":{"nullable":true,"description":"The push or web push title and body as sent (kept 30 days).","allOf":[{"$ref":"#/components/schemas/RenderedPushDto"}]},"warnings":{"description":"Render warnings: `template_variable_missing:<name>`, `unsafe_url:<field>`.","type":"array","items":{"type":"string"}},"sent_at":{"type":"string","format":"date-time","nullable":true,"description":"Handed to the provider (or written to the inbox)."},"final_at":{"type":"string","format":"date-time","nullable":true,"description":"Reached its final state."},"created_at":{"type":"string","format":"date-time","description":"Planned."}},"required":["id","notification_id","recipient_id","recipient_external_id","channel","provider","device_id","target","fallback_step","is_fallback","state","reason","explanation","attempts","next_attempt_at","provider_message_id","provider_unique_id","rendered","warnings","sent_at","final_at","created_at"],"x-pc-sdk-model":"Delivery"},"DeliveryListResponseDto":{"type":"object","properties":{"data":{"description":"Deliveries, newest first.","type":"array","items":{"$ref":"#/components/schemas/DeliveryDto"}},"pagination":{"description":"The cursor for the next page.","allOf":[{"$ref":"#/components/schemas/ListPaginationDto"}]}},"required":["data","pagination"],"x-pc-sdk-model":"DeliveryList"},"DeliveryAttemptDto":{"type":"object","properties":{"attempt":{"type":"integer","description":"Attempt number, from 1."},"outcome":{"type":"string","description":"`sent`, `delivered`, `retry`, `pruned`, `failed` or `credential_error`."},"provider_status":{"type":"integer","nullable":true,"description":"Provider HTTP status."},"provider_code":{"type":"string","nullable":true,"description":"Provider reason code (e.g. `Unregistered`)."},"retry_after_s":{"type":"integer","nullable":true,"description":"Provider `Retry-After`, seconds."},"duration_ms":{"type":"integer","description":"Call duration."},"created_at":{"type":"string","format":"date-time","description":"When."}},"required":["attempt","outcome","provider_status","provider_code","retry_after_s","duration_ms","created_at"],"x-pc-sdk-model":"DeliveryAttempt"},"DeliveryDetailDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The delivery id."},"notification_id":{"type":"string","format":"uuid","description":"The notification."},"recipient_id":{"type":"string","format":"uuid","description":"The recipient id."},"recipient_external_id":{"type":"string","nullable":true,"description":"The recipient external id (null after an erasure)."},"channel":{"description":"The channel.","allOf":[{"$ref":"#/components/schemas/NotifyChannel"}]},"provider":{"type":"string","nullable":true,"description":"`inbox`, `mail`, `apns`, `fcm` or `webpush`."},"device_id":{"type":"string","format":"uuid","nullable":true,"description":"The device, for push and web push."},"target":{"type":"string","description":"`inbox`, `email:<address hash>` or `device:<id>`; `email:erased:<delivery id>` once the recipient was erased."},"fallback_step":{"type":"integer","description":"0 for an `always` channel; n for the n-th fallback step."},"is_fallback":{"type":"boolean","description":"Planned because an earlier step failed."},"state":{"description":"The state.","allOf":[{"$ref":"#/components/schemas/DeliveryState"}]},"reason":{"nullable":true,"description":"Why it was skipped, held, retried or failed (`mail_refused:<code>` carries Mail's code).","allOf":[{"$ref":"#/components/schemas/NotifyDeliveryReason"}]},"explanation":{"type":"string","nullable":true,"description":"What the reason means and how to fix it."},"attempts":{"type":"integer","description":"Provider calls made."},"next_attempt_at":{"type":"string","format":"date-time","nullable":true,"description":"Next try (retry, scheduled)."},"provider_message_id":{"type":"string","nullable":true,"description":"Provider message id (APNs `apns-id`, FCM message name, Mail message id)."},"provider_unique_id":{"type":"string","nullable":true,"description":"APNs `apns-unique-id` (sandbox only)."},"rendered":{"nullable":true,"description":"The push or web push title and body as sent (kept 30 days).","allOf":[{"$ref":"#/components/schemas/RenderedPushDto"}]},"warnings":{"description":"Render warnings: `template_variable_missing:<name>`, `unsafe_url:<field>`.","type":"array","items":{"type":"string"}},"sent_at":{"type":"string","format":"date-time","nullable":true,"description":"Handed to the provider (or written to the inbox)."},"final_at":{"type":"string","format":"date-time","nullable":true,"description":"Reached its final state."},"created_at":{"type":"string","format":"date-time","description":"Planned."},"attempts_log":{"description":"Provider calls, oldest first (no PII, no provider bodies).","type":"array","items":{"$ref":"#/components/schemas/DeliveryAttemptDto"}}},"required":["id","notification_id","recipient_id","recipient_external_id","channel","provider","device_id","target","fallback_step","is_fallback","state","reason","explanation","attempts","next_attempt_at","provider_message_id","provider_unique_id","rendered","warnings","sent_at","final_at","created_at","attempts_log"],"x-pc-sdk-model":"DeliveryDetail"},"InboxItemStatusFilter":{"type":"string","enum":["active","archived","all"]},"InboxActionDto":{"type":"object","properties":{"label":{"type":"string","description":"Button label."},"url":{"type":"string","description":"Link the button opens (https)."},"action_key":{"type":"string","description":"Your key, reported back when the button is pressed."}},"required":["label"],"x-pc-sdk-model":"InboxAction"},"InboxItemDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The item id (time-ordered)."},"notification_id":{"type":"string","format":"uuid","description":"The notification."},"type":{"type":"string","description":"The notification type key."},"category":{"type":"string","description":"The category key."},"tenant":{"type":"string","nullable":true,"description":"Tenant."},"title":{"type":"string","description":"Rendered title."},"body":{"type":"string","description":"Rendered body (Markdown subset)."},"action_url":{"type":"string","nullable":true,"description":"Where a click goes."},"avatar_url":{"type":"string","nullable":true,"description":"Avatar URL."},"actions":{"description":"Up to 2 buttons.","type":"array","items":{"$ref":"#/components/schemas/InboxActionDto"}},"actor":{"nullable":true,"description":"Who caused it.","allOf":[{"$ref":"#/components/schemas/NotificationActorDto"}]},"data":{"type":"object","additionalProperties":true,"description":"The send's data, exactly as sent."},"seen_at":{"type":"string","format":"date-time","nullable":true,"description":"Seen (the bell opened)."},"read_at":{"type":"string","format":"date-time","nullable":true,"description":"Read."},"archived_at":{"type":"string","format":"date-time","nullable":true,"description":"Archived."},"interacted_at":{"type":"string","format":"date-time","nullable":true,"description":"When the recipient first pressed the item or one of its buttons."},"interaction_key":{"type":"string","nullable":true,"description":"The `action_key` of the button pressed last (null for a press on the item itself)."},"retracted_at":{"type":"string","format":"date-time","nullable":true,"description":"Retracted by the sender."},"created_at":{"type":"string","format":"date-time","description":"Created."}},"required":["id","notification_id","type","category","tenant","title","body","action_url","avatar_url","actions","actor","data","seen_at","read_at","archived_at","interacted_at","interaction_key","retracted_at","created_at"],"x-pc-sdk-model":"InboxItem"},"InboxCountsDto":{"type":"object","properties":{"unseen":{"type":"integer","description":"Items not yet seen."},"unread":{"type":"integer","description":"Items not yet read."}},"required":["unseen","unread"],"x-pc-sdk-model":"InboxCounts"},"InboxListResponseDto":{"type":"object","properties":{"data":{"description":"Items, newest first.","type":"array","items":{"$ref":"#/components/schemas/InboxItemDto"}},"pagination":{"description":"The cursor for the next page.","allOf":[{"$ref":"#/components/schemas/ListPaginationDto"}]},"counts":{"description":"The recipient's unseen and unread counts.","allOf":[{"$ref":"#/components/schemas/InboxCountsDto"}]}},"required":["data","pagination","counts"],"x-pc-sdk-model":"InboxList"},"TimelineDeviceDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The device id."},"channel":{"type":"string","description":"`apns`, `fcm` or `webpush`."},"state":{"type":"string","description":"`active`, `invalid` or `pruned` (or `deleted`)."},"token_last6":{"type":"string","nullable":true,"description":"Last 6 characters of the token."},"last_success_at":{"type":"string","format":"date-time","nullable":true,"description":"Last successful delivery to this device."}},"required":["id","channel","state","token_last6","last_success_at"],"x-pc-sdk-model":"TimelineDevice"},"TimelineEntryDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The delivery id."},"notification_id":{"type":"string","format":"uuid","description":"The notification."},"recipient_id":{"type":"string","format":"uuid","description":"The recipient id."},"recipient_external_id":{"type":"string","nullable":true,"description":"The recipient external id (null after an erasure)."},"channel":{"description":"The channel.","allOf":[{"$ref":"#/components/schemas/NotifyChannel"}]},"provider":{"type":"string","nullable":true,"description":"`inbox`, `mail`, `apns`, `fcm` or `webpush`."},"device_id":{"type":"string","format":"uuid","nullable":true,"description":"The device, for push and web push."},"target":{"type":"string","description":"`inbox`, `email:<address hash>` or `device:<id>`; `email:erased:<delivery id>` once the recipient was erased."},"fallback_step":{"type":"integer","description":"0 for an `always` channel; n for the n-th fallback step."},"is_fallback":{"type":"boolean","description":"Planned because an earlier step failed."},"state":{"description":"The state.","allOf":[{"$ref":"#/components/schemas/DeliveryState"}]},"reason":{"nullable":true,"description":"Why it was skipped, held, retried or failed (`mail_refused:<code>` carries Mail's code).","allOf":[{"$ref":"#/components/schemas/NotifyDeliveryReason"}]},"explanation":{"type":"string","nullable":true,"description":"What the reason means and how to fix it."},"attempts":{"type":"integer","description":"Provider calls made."},"next_attempt_at":{"type":"string","format":"date-time","nullable":true,"description":"Next try (retry, scheduled)."},"provider_message_id":{"type":"string","nullable":true,"description":"Provider message id (APNs `apns-id`, FCM message name, Mail message id)."},"provider_unique_id":{"type":"string","nullable":true,"description":"APNs `apns-unique-id` (sandbox only)."},"rendered":{"nullable":true,"description":"The push or web push title and body as sent (kept 30 days).","allOf":[{"$ref":"#/components/schemas/RenderedPushDto"}]},"warnings":{"description":"Render warnings: `template_variable_missing:<name>`, `unsafe_url:<field>`.","type":"array","items":{"type":"string"}},"sent_at":{"type":"string","format":"date-time","nullable":true,"description":"Handed to the provider (or written to the inbox)."},"final_at":{"type":"string","format":"date-time","nullable":true,"description":"Reached its final state."},"created_at":{"type":"string","format":"date-time","description":"Planned."},"type":{"type":"string","description":"The notification type key."},"device":{"nullable":true,"description":"The device, for push and web push.","allOf":[{"$ref":"#/components/schemas/TimelineDeviceDto"}]}},"required":["id","notification_id","recipient_id","recipient_external_id","channel","provider","device_id","target","fallback_step","is_fallback","state","reason","explanation","attempts","next_attempt_at","provider_message_id","provider_unique_id","rendered","warnings","sent_at","final_at","created_at","type","device"],"x-pc-sdk-model":"TimelineEntry"},"DeviceMoveDirection":{"type":"string","enum":["moved_in","moved_out"]},"DeviceMoveDto":{"type":"object","properties":{"device_id":{"type":"string","format":"uuid","description":"The device."},"channel":{"type":"string","description":"`apns`, `fcm` or `webpush`."},"direction":{"description":"`moved_in`: the token moved to this recipient; `moved_out`: it left for another.","allOf":[{"$ref":"#/components/schemas/DeviceMoveDirection"}]},"moved_at":{"type":"string","format":"date-time","description":"When."}},"required":["device_id","channel","direction","moved_at"],"x-pc-sdk-model":"DeviceMove"},"TimelineResponseDto":{"type":"object","properties":{"data":{"description":"Every delivery to the recipient, newest first.","type":"array","items":{"$ref":"#/components/schemas/TimelineEntryDto"}},"pagination":{"description":"The cursor for the next page.","allOf":[{"$ref":"#/components/schemas/ListPaginationDto"}]},"device_moves":{"description":"Device token moves in or out of this recipient (latest first, at most 50).","type":"array","items":{"$ref":"#/components/schemas/DeviceMoveDto"}}},"required":["data","pagination","device_moves"],"x-pc-sdk-model":"Timeline"},"NotifySettingsDto":{"type":"object","properties":{"email_enabled":{"type":"boolean","description":"Whether Notify sends email for this workspace. Off: every email delivery is skipped as `channel_not_configured`."},"email_paused_at":{"type":"string","format":"date-time","nullable":true,"description":"Set when Notify paused email automatically: more than 0.1 % complaints or 5 % hard bounces among the last 500 email deliveries (counted once at least 100 exist). While set, email deliveries are skipped as `email_paused`, critical types included. Clear it with `PATCH` `{ \"email_paused\": false }`."},"email_paused_reason":{"type":"string","nullable":true,"description":"Why email was paused, e.g. \"2 complaints in the last 140 emails\"."},"email_resumed_at":{"type":"string","format":"date-time","nullable":true,"description":"When email was last resumed after a pause. The pause check only counts emails sent after it."},"default_timezone":{"type":"string","nullable":true,"example":"Europe/Lisbon","description":"IANA time zone for recipients who have none on their profile (and no time zone on the device, for push). Null: recipients without a time zone are never held for quiet hours."},"default_quiet_hours":{"nullable":true,"description":"Quiet hours for recipients who set none of their own. Push and web push wait until the window ends (plus up to 10 minutes, so a morning backlog spreads out); email waits only for types with `email_quiet_hours`; the inbox and critical types never wait. A hold past the type TTL fails as `expired`.","allOf":[{"$ref":"#/components/schemas/QuietHoursDto"}]},"client_origins":{"example":["https://app.example.com"],"description":"Browser origins allowed to call the client API (`/v1/client/…`) with a recipient token. Empty: only `http://localhost` (any port) is accepted, for development. Calls without an `Origin` header (native apps) are not origin-checked.","type":"array","items":{"type":"string"}}},"required":["email_enabled","email_paused_at","email_paused_reason","email_resumed_at","default_timezone","default_quiet_hours","client_origins"],"x-pc-sdk-model":"NotifySettings"},"UpdateNotifySettingsDto":{"type":"object","properties":{"email_enabled":{"type":"boolean","description":"Turn Notify email on or off for the workspace."},"email_paused":{"type":"number","enum":[false],"description":"Send `false` to resume email after an automatic pause. Pausing by hand is not supported: turn `email_enabled` off instead."},"default_timezone":{"type":"string","nullable":true,"example":"Europe/Lisbon","description":"IANA time zone used for recipients without one. `null` clears it."},"default_quiet_hours":{"nullable":true,"description":"Quiet hours for recipients who set none, e.g. `{ \"start\": \"22:00\", \"end\": \"08:00\" }` (an end before the start spans midnight). `null` clears it.","allOf":[{"$ref":"#/components/schemas/QuietHoursDto"}]},"client_origins":{"maxItems":20,"example":["https://app.example.com","https://www.example.com"],"description":"Replaces the browser origins allowed to call the client API: `https://host[:port]` (or `http://localhost[:port]`), no path. `[]` goes back to localhost only.","type":"array","items":{"type":"string"}}}},"SetupRowState":{"type":"string","enum":["pass","attention","not_set","blocked"]},"SetupRowFixDto":{"type":"object","properties":{"label":{"type":"string","description":"Button text for the fix, in plain words.","example":"Add a redirect origin"},"href":{"type":"string","description":"Console-relative path of the exact place to fix this row (may carry a `#section` anchor). Link to it directly; it is never an absolute URL.","example":"/auth/apps/0b6f3c7e-6a43-4c55-9d4b-2a9a7f5b1c11/settings#auth-config"}},"required":["label","href"],"x-pc-sdk-model":"SetupRowFix"},"SetupRowTestDto":{"type":"object","properties":{"label":{"type":"string","description":"Button text for the test, in plain words.","example":"Send a test event"},"action":{"type":"string","description":"Identifier of the test the console runs for this row (for example `webhook.test` or `sign_in.try`). The product documents each action next to the route that performs it.","example":"webhook.test"}},"required":["label","action"],"x-pc-sdk-model":"SetupRowTest"},"SetupRowDto":{"type":"object","properties":{"id":{"type":"string","description":"Stable row identifier. Safe to key UI state on; new rows may be added.","example":"redirect_origins"},"title":{"type":"string","description":"Short title of the step.","example":"Sites allowed to start sign-in"},"state":{"example":"blocked","description":"`pass`: done. `attention`: works, but worth a look (not blocking). `not_set`: a required step has not been started. `blocked`: set up, but something stops it from working until it is fixed.","allOf":[{"$ref":"#/components/schemas/SetupRowState"}]},"detail":{"type":"string","description":"One or two plain sentences: what is true now and, when the row is not `pass`, what to do about it.","example":"No site is allowed to start sign-in, so web sign-in is refused. Add the origin of your site, for example https://app.example.com."},"fix":{"nullable":true,"description":"Where to fix this row. Null when there is nothing to do or no single place to do it.","allOf":[{"$ref":"#/components/schemas/SetupRowFixDto"}]},"test":{"nullable":true,"description":"A real test that proves this row works, when the product offers one. Null otherwise.","allOf":[{"$ref":"#/components/schemas/SetupRowTestDto"}]}},"required":["id","title","state","detail","fix","test"],"x-pc-sdk-model":"SetupRow"},"SetupStatusDto":{"type":"object","properties":{"checked_at":{"type":"string","format":"date-time","description":"When the server computed these rows.","example":"2026-10-16T12:00:00.000Z"},"complete":{"type":"boolean","description":"True when no row is `not_set` or `blocked`. `attention` rows do not stop setup from being complete.","example":false},"rows":{"description":"Checklist rows in the order they should be shown.","type":"array","items":{"$ref":"#/components/schemas/SetupRowDto"}}},"required":["checked_at","complete","rows"],"x-pc-sdk-model":"SetupStatus"},"RecipientTokenScope":{"type":"string","enum":["inbox:read","inbox:write","devices:write","preferences:read","preferences:write","stream"]},"RecipientTokenUpsertDto":{"type":"object","properties":{"email":{"type":"string","description":"Email address for the email channel.","format":"email","maxLength":320,"nullable":true,"example":"ada@example.com"},"name":{"type":"string","description":"Display name.","maxLength":255,"nullable":true,"example":"Ada Lovelace"},"locale":{"type":"string","description":"BCP 47 language tag; templates fall back from `pt-PT` to `pt` to the type default.","nullable":true,"example":"pt-PT"},"timezone":{"type":"string","description":"IANA time zone, used for quiet hours.","nullable":true,"example":"Europe/Lisbon"}}},"CreateRecipientTokenDto":{"type":"object","properties":{"recipient":{"type":"string","description":"Your user id for the recipient (`external_id`). A recipient that does not exist yet is created (bare, or with the `upsert` fields), so you can mint at sign-in without a separate call.","example":"user_123","pattern":"^[A-Za-z0-9._:@|-]{1,255}$"},"scopes":{"type":"array","description":"What the token allows. Default: every scope. Narrow it for a surface that only reads the inbox, for example.","example":["inbox:read","stream"],"items":{"$ref":"#/components/schemas/RecipientTokenScope"}},"ttl_seconds":{"type":"integer","minimum":60,"maximum":86400,"default":3600,"description":"Lifetime in seconds (60 to 86,400). Cache the token per user for most of its life instead of minting on every page load."},"upsert":{"description":"Profile fields to set on the recipient while minting (created or existing). Left out: the profile is not changed.","allOf":[{"$ref":"#/components/schemas/RecipientTokenUpsertDto"}]}},"required":["recipient"]},"RecipientTokenDto":{"type":"object","properties":{"token":{"type":"string","description":"The recipient token (an RS256 JWT). Send it as `Authorization: Bearer <token>` to the client API (`/v1/client/…`) from the recipient's app or browser. Keep it in memory, never in a URL or browser storage.","example":"eyJhbGciOiJSUzI1NiIsImtpZCI6InJzMjU2LWV4YW1wbGUifQ.eyJzdWIiOiIuLi4ifQ.c2lnbmF0dXJl"},"expires_at":{"type":"string","format":"date-time","description":"When the token stops working; mint a new one before then."},"scopes":{"type":"array","description":"The scopes the token carries.","items":{"$ref":"#/components/schemas/RecipientTokenScope"}},"recipient_id":{"type":"string","format":"uuid","description":"The recipient's Notify id."},"external_id":{"type":"string","description":"Your user id for the recipient."}},"required":["token","expires_at","scopes","recipient_id","external_id"],"x-pc-sdk-model":"RecipientToken"},"ChannelReadiness":{"type":"string","enum":["ready","not_configured","disabled","paused"]},"CredentialProvider":{"type":"string","enum":["apns","fcm","vapid"]},"CredentialEnvironment":{"type":"string","enum":["sandbox","production","both"]},"CredentialOrigin":{"type":"string","enum":["generated","imported","uploaded"]},"CredentialStatus":{"type":"string","enum":["active","invalid","superseded"]},"ChannelCredentialDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The credential id."},"provider":{"description":"The provider the credential is for (`vapid` is web push).","allOf":[{"$ref":"#/components/schemas/CredentialProvider"}]},"app_id":{"type":"string","description":"APNs bundle id or Firebase project id; `-` for VAPID."},"environment":{"nullable":true,"description":"APNs environment; null for other providers.","allOf":[{"$ref":"#/components/schemas/CredentialEnvironment"}]},"origin":{"description":"How the credential was created: `generated` by Notify, `imported` (VAPID) or `uploaded` (APNs, FCM).","allOf":[{"$ref":"#/components/schemas/CredentialOrigin"}]},"fingerprint":{"type":"string","description":"First 16 hex characters of the SHA-256 of the secret."},"status":{"description":"`superseded` credentials are listed for 24 h after they were replaced.","allOf":[{"$ref":"#/components/schemas/CredentialStatus"}]},"public_key":{"type":"string","nullable":true,"description":"VAPID public key (base64url); null for other providers."},"key_id":{"type":"string","nullable":true,"description":"APNs key id."},"team_id":{"type":"string","nullable":true,"description":"APNs team id."},"client_email":{"type":"string","nullable":true,"format":"email","description":"FCM service-account email."},"last_verified_at":{"type":"string","nullable":true,"format":"date-time","description":"When the credential was last validated."},"last_error_code":{"type":"string","nullable":true,"description":"The provider error that made the credential `invalid`."},"created_at":{"type":"string","format":"date-time","description":"When the credential was created."},"superseded_at":{"type":"string","nullable":true,"format":"date-time","description":"When it was replaced."}},"required":["id","provider","app_id","environment","origin","fingerprint","status","public_key","key_id","team_id","client_email","last_verified_at","last_error_code","created_at","superseded_at"],"x-pc-sdk-model":"ChannelCredential"},"ChannelStatusDto":{"type":"object","properties":{"channel":{"description":"The channel (`push` covers APNs and FCM).","allOf":[{"$ref":"#/components/schemas/NotifyChannel"}]},"configured":{"type":"boolean","description":"Whether Notify can deliver on this channel now."},"status":{"description":"`ready`; `not_configured` (no credential, or Mail not set up); `disabled` (email switched off in settings); `paused` (email auto-paused, or every push credential was refused by its provider and needs a new upload).","allOf":[{"$ref":"#/components/schemas/ChannelReadiness"}]},"credentials":{"description":"Credentials behind the channel (metadata only, never secrets). Empty for `inbox` and `email`.","type":"array","items":{"$ref":"#/components/schemas/ChannelCredentialDto"}}},"required":["channel","configured","status","credentials"],"x-pc-sdk-model":"ChannelStatus"},"ChannelListResponseDto":{"type":"object","properties":{"data":{"description":"Every channel, in a fixed order: inbox, email, push, webpush.","type":"array","items":{"$ref":"#/components/schemas/ChannelStatusDto"}}},"required":["data"],"x-pc-sdk-model":"ChannelList"},"WebPushChannelDto":{"type":"object","properties":{"configured":{"type":"boolean","description":"Whether the workspace has an active VAPID key (web push can send)."},"public_key":{"type":"string","nullable":true,"description":"The public key for `applicationServerKey`; null until a key is generated or imported."},"credential_id":{"type":"string","nullable":true,"format":"uuid","description":"The credential id of the active key."},"origin":{"nullable":true,"description":"How the active key was created (`generated` or `imported`).","allOf":[{"$ref":"#/components/schemas/CredentialOrigin"}]},"fingerprint":{"type":"string","nullable":true,"description":"Fingerprint of the active private key."},"created_at":{"type":"string","nullable":true,"format":"date-time","description":"When the active key was created."}},"required":["configured","public_key","credential_id","origin","fingerprint","created_at"],"x-pc-sdk-model":"WebPushChannel"},"GenerateVapidKeysDto":{"type":"object","properties":{"confirm":{"type":"string","enum":["rotate"],"description":"Required when the workspace already has a VAPID key: replacing it orphans every existing browser subscription (they were made with the old public key). Every web push device is marked `invalid` (`vapid_rotated`) and must subscribe again with the new key; the `@productcraft/notify-js` service worker does this on `pushsubscriptionchange`."}}},"VapidKeyDto":{"type":"object","properties":{"credential_id":{"type":"string","format":"uuid","description":"The credential id of the key pair."},"public_key":{"type":"string","description":"The public key (base64url, uncompressed P-256). Pass it as `applicationServerKey` to `pushManager.subscribe`."},"origin":{"description":"`generated` by Notify, or `imported` from another provider.","allOf":[{"$ref":"#/components/schemas/CredentialOrigin"}]},"fingerprint":{"type":"string","description":"First 16 hex characters of the SHA-256 of the private key; the private key itself is never returned."},"created_at":{"type":"string","format":"date-time","description":"When this key became active."},"devices_invalidated":{"type":"integer","description":"Web push devices marked `invalid` (`vapid_rotated`) because this call replaced the previous key; 0 when nothing was replaced."}},"required":["credential_id","public_key","origin","fingerprint","created_at","devices_invalidated"],"x-pc-sdk-model":"VapidKey"},"ImportVapidKeysDto":{"type":"object","properties":{"public_key":{"type":"string","description":"The VAPID public key: an uncompressed P-256 point (65 bytes), base64url. The `applicationServerKey` your existing subscriptions were made with.","example":"BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U"},"private_key":{"type":"string","description":"The VAPID private key: the 32-byte P-256 scalar, base64url. Write-only: stored encrypted and never returned.","writeOnly":true},"confirm":{"type":"string","enum":["rotate"],"description":"Required when the workspace already has a different VAPID key (see `POST /channels/webpush/vapid-keys`). Importing the pair that is already active changes nothing."}},"required":["public_key","private_key"]},"ApnsCredentialEnvironment":{"type":"string","enum":["sandbox","production","both"]},"UploadApnsCredentialDto":{"type":"object","properties":{"key_id":{"type":"string","description":"The 10-character key id Apple shows next to the key (Certificates, Identifiers & Profiles > Keys).","example":"ABC123DEFG","pattern":"^[A-Z0-9]{10}$"},"team_id":{"type":"string","description":"The 10-character team id of the Apple developer account the key belongs to.","example":"DEF123GHIJ","pattern":"^[A-Z0-9]{10}$"},"bundle_id":{"type":"string","description":"The app bundle id the key sends to (the `apns-topic`). Devices register with the same value as `app_id`.","example":"com.example.app","maxLength":155},"environment":{"description":"Which APNs environment the key serves. Token-auth keys work in both; `both` lets debug builds (`sandbox` device tokens) and TestFlight/App Store builds (`production`) share one key.","allOf":[{"$ref":"#/components/schemas/ApnsCredentialEnvironment"}]},"p8":{"type":"string","description":"The full contents of the `.p8` file, including the `-----BEGIN PRIVATE KEY-----` lines (`\\n` escapes are accepted). Write-only: validated with Apple, stored encrypted and never returned; responses show a fingerprint.","writeOnly":true,"maxLength":2000}},"required":["key_id","team_id","bundle_id","environment","p8"]},"ApnsCredentialDto":{"type":"object","properties":{"credential_id":{"type":"string","format":"uuid","description":"The credential id."},"key_id":{"type":"string","description":"APNs key id."},"team_id":{"type":"string","description":"Apple team id."},"bundle_id":{"type":"string","description":"The bundle id the key sends to."},"environment":{"description":"The environments the key serves.","allOf":[{"$ref":"#/components/schemas/ApnsCredentialEnvironment"}]},"fingerprint":{"type":"string","description":"First 16 hex characters of the SHA-256 of the key; the key itself is never returned."},"status":{"description":"`active` after a successful upload; `invalid` once Apple refused it while sending.","allOf":[{"$ref":"#/components/schemas/CredentialStatus"}]},"last_verified_at":{"type":"string","nullable":true,"format":"date-time","description":"When Apple last accepted the key (the upload validation)."},"last_error_code":{"type":"string","nullable":true,"description":"The Apple reason that made the key `invalid` (e.g. `InvalidProviderToken`)."},"created_at":{"type":"string","format":"date-time","description":"When the credential was created."},"replaced_credential_ids":{"description":"APNs credentials of the same bundle id with an overlapping environment that this upload replaced (now `superseded`; their key is erased after 24 h). Empty on a first upload or a no-op re-upload.","type":"array","items":{"type":"string","format":"uuid"}}},"required":["credential_id","key_id","team_id","bundle_id","environment","fingerprint","status","last_verified_at","last_error_code","created_at","replaced_credential_ids"],"x-pc-sdk-model":"ApnsCredential"},"UploadFcmCredentialDto":{"type":"object","properties":{"service_account":{"type":"string","description":"The full text of the service-account JSON key file (Google Cloud console > IAM & Admin > Service Accounts > Keys > Add key > JSON). Use a dedicated service account with only the \"Firebase Cloud Messaging API Admin\" role. Write-only: validated with Google, the private key is stored encrypted and never returned; responses show the project id, the account email and a fingerprint.","writeOnly":true,"maxLength":10000}},"required":["service_account"]},"FcmCredentialDto":{"type":"object","properties":{"credential_id":{"type":"string","format":"uuid","description":"The credential id."},"project_id":{"type":"string","description":"The Firebase project id the account sends in. Android devices register with the same value as `app_id`.","example":"my-app-prod"},"client_email":{"type":"string","format":"email","description":"The service account email.","example":"notify-push@my-app-prod.iam.gserviceaccount.com"},"fingerprint":{"type":"string","description":"First 16 hex characters of the SHA-256 of the private key; the key itself is never returned."},"status":{"description":"`active` after a successful upload; `invalid` once Google refused it while sending.","allOf":[{"$ref":"#/components/schemas/CredentialStatus"}]},"last_verified_at":{"type":"string","nullable":true,"format":"date-time","description":"When Google last accepted the account (the upload validation)."},"last_error_code":{"type":"string","nullable":true,"description":"The Google error that made the credential `invalid` (e.g. `THIRD_PARTY_AUTH_ERROR`, `oauth:invalid_grant`)."},"created_at":{"type":"string","format":"date-time","description":"When the credential was created."},"replaced_credential_ids":{"description":"FCM credentials of the same project that this upload replaced (now `superseded`; their key is erased after 24 h). Empty on a first upload or a no-op re-upload.","type":"array","items":{"type":"string","format":"uuid"}},"warnings":{"description":"Advice about the uploaded account, e.g. when it is the Firebase Admin SDK account (`firebase-adminsdk-*`), which can do far more than send push. The upload is accepted either way.","type":"array","items":{"type":"string"}}},"required":["credential_id","project_id","client_email","fingerprint","status","last_verified_at","last_error_code","created_at","replaced_credential_ids","warnings"],"x-pc-sdk-model":"FcmCredential"},"ClientInboxStatusFilter":{"type":"string","enum":["all","unread","unseen"]},"ClientInboxListDto":{"type":"object","properties":{"data":{"description":"Items, newest first. Items the sender retracted are left out.","type":"array","items":{"$ref":"#/components/schemas/InboxItemDto"}},"pagination":{"description":"The cursor for the next page.","allOf":[{"$ref":"#/components/schemas/ListPaginationDto"}]},"counts":{"description":"The recipient's unseen and unread counts (badge).","allOf":[{"$ref":"#/components/schemas/InboxCountsDto"}]}},"required":["data","pagination","counts"],"x-pc-sdk-model":"ClientInboxList"},"InboxMarkAction":{"type":"string","enum":["seen","read","unread","archived","unarchived"]},"MarkInboxDto":{"type":"object","properties":{"action":{"description":"What to mark the items as.","allOf":[{"$ref":"#/components/schemas/InboxMarkAction"}]},"ids":{"maxItems":100,"description":"The items (at most 100). Give `ids` or `before_id`, not both.","type":"array","items":{"type":"string","format":"uuid"}},"before_id":{"type":"string","format":"uuid","description":"Every item up to and including this one (item ids sort by time): \"mark all as read\" for what the user has loaded. Give `ids` or `before_id`, not both."}},"required":["action"]},"MarkInboxResultDto":{"type":"object","properties":{"updated":{"type":"integer","description":"Items that changed (an item already in that state is not counted)."},"counts":{"description":"The counts after the change.","allOf":[{"$ref":"#/components/schemas/InboxCountsDto"}]}},"required":["updated","counts"],"x-pc-sdk-model":"MarkInboxResult"},"InteractInboxDto":{"type":"object","properties":{"action_key":{"type":"string","description":"The `action_key` of the button pressed; left out for a press on the item itself.","example":"accept","maxLength":100}}},"VapidPublicKeyDto":{"type":"object","properties":{"public_key":{"type":"string","description":"The workspace's VAPID public key (base64url): pass it as `applicationServerKey` to `pushManager.subscribe()`.","example":"BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U"}},"required":["public_key"],"x-pc-sdk-model":"VapidPublicKey"},"ClientCategoryDto":{"type":"object","properties":{"key":{"type":"string","description":"The category key (use it as `key` in a cell).","example":"billing"},"name":{"type":"string","description":"Display name."},"description":{"type":"string","nullable":true,"description":"Description."},"defaults":{"description":"Per channel: `on`, `off`, or `mandatory` (shown read-only: it cannot be turned off).","allOf":[{"$ref":"#/components/schemas/CategoryDefaultsViewDto"}]}},"required":["key","name","description","defaults"],"x-pc-sdk-model":"ClientCategory"},"ClientPreferencesDto":{"type":"object","properties":{"categories":{"description":"The workspace's categories in display order, with their defaults.","type":"array","items":{"$ref":"#/components/schemas/ClientCategoryDto"}},"preferences":{"description":"Cells the recipient set (anything else follows the category defaults).","type":"array","items":{"$ref":"#/components/schemas/PreferenceCellDto"}},"unsubscribed_all":{"type":"boolean","description":"Opted out of every non-critical notification."},"timezone":{"type":"string","nullable":true,"description":"IANA time zone."},"quiet_hours":{"nullable":true,"description":"Quiet window.","allOf":[{"$ref":"#/components/schemas/QuietHoursDto"}]}},"required":["categories","preferences","unsubscribed_all","timezone","quiet_hours"],"x-pc-sdk-model":"ClientPreferences"},"UpdateClientPreferencesDto":{"type":"object","properties":{"preferences":{"maxItems":500,"description":"Cells to set, or (with `enabled: null`) remove; other cells are kept, so two open tabs cannot overwrite each other.","type":"array","items":{"$ref":"#/components/schemas/PreferenceCellPatchDto"}},"unsubscribed_all":{"type":"boolean","description":"Opt out of (or back into) every non-critical notification."},"timezone":{"type":"string","nullable":true,"description":"IANA time zone (quiet hours).","example":"Europe/Lisbon"},"quiet_hours":{"nullable":true,"description":"Quiet window; null removes it.","allOf":[{"$ref":"#/components/schemas/QuietHoursDto"}]}}},"NotifyStatsChannel":{"type":"string","enum":["inbox","email","push","webpush"]},"NotifyStatsDayDto":{"type":"object","properties":{"accepted":{"type":"integer","example":120,"description":"Deliveries planned (one per recipient, channel and target)."},"sent":{"type":"integer","example":96,"description":"Deliveries a provider accepted (sent or delivered)."},"delivered":{"type":"integer","example":40,"description":"Deliveries that reached their target: inbox items, and emails the receiving server accepted."},"failed":{"type":"integer","example":2,"description":"Deliveries that failed, bounced, or hit a dead device token."},"skipped":{"type":"integer","example":22,"description":"Deliveries skipped (opted out, no device, quiet hours expired, cancelled, ...)."},"read":{"type":"integer","example":18,"description":"Inbox items read (inbox channel only)."},"day":{"type":"string","format":"date","example":"2026-11-10","description":"The UTC day the deliveries were planned."},"channel":{"example":"push","description":"The channel.","allOf":[{"$ref":"#/components/schemas/NotifyStatsChannel"}]}},"required":["accepted","sent","delivered","failed","skipped","read","day","channel"],"x-pc-sdk-model":"NotifyStatsDay"},"NotifyStatsCountsDto":{"type":"object","properties":{"accepted":{"type":"integer","example":120,"description":"Deliveries planned (one per recipient, channel and target)."},"sent":{"type":"integer","example":96,"description":"Deliveries a provider accepted (sent or delivered)."},"delivered":{"type":"integer","example":40,"description":"Deliveries that reached their target: inbox items, and emails the receiving server accepted."},"failed":{"type":"integer","example":2,"description":"Deliveries that failed, bounced, or hit a dead device token."},"skipped":{"type":"integer","example":22,"description":"Deliveries skipped (opted out, no device, quiet hours expired, cancelled, ...)."},"read":{"type":"integer","example":18,"description":"Inbox items read (inbox channel only)."}},"required":["accepted","sent","delivered","failed","skipped","read"],"x-pc-sdk-model":"NotifyStatsCounts"},"NotifyStatsResponseDto":{"type":"object","properties":{"from":{"type":"string","format":"date","example":"2026-11-01","description":"First day of the range."},"to":{"type":"string","format":"date","example":"2026-11-10","description":"Last day of the range."},"data":{"description":"One row per day and channel that had deliveries, oldest first. Days without deliveries are absent.","type":"array","items":{"$ref":"#/components/schemas/NotifyStatsDayDto"}},"totals":{"description":"The range summed over every day and channel.","allOf":[{"$ref":"#/components/schemas/NotifyStatsCountsDto"}]},"updated_at":{"type":"string","format":"date-time","nullable":true,"example":"2026-11-10T09:05:00.000Z","description":"When the rollup last wrote this range (it runs hourly; today and yesterday are refreshed each time). Null when nothing was rolled up yet."}},"required":["from","to","data","totals","updated_at"],"x-pc-sdk-model":"NotifyStats"},"ErrorParamDto":{"type":"object","properties":{"param":{"type":"string","description":"Wire path of the invalid field: `email`, `items.0.display_name`.","example":"email"},"message":{"type":"string","description":"What is wrong with it; the same text as the field's entry in an array `message`.","example":"email must be an email"},"code":{"type":"string","description":"The failed rule (class-validator constraint name, e.g. `isEmail`, `isUuid`, `whitelistValidation` for a field the operation does not declare), or `invalid`.","example":"isEmail"}},"required":["param","message","code"]},"ErrorResponseDto":{"type":"object","properties":{"status_code":{"type":"number","description":"HTTP status code, same as the response status.","example":404},"statusCode":{"type":"number","description":"Deprecated alias of `status_code`, same value. Kept so existing clients keep working; read `status_code`. Error responses carry `Deprecation: true` while it is sent.","example":404,"deprecated":true},"message":{"description":"Human-readable error message. A string, or on a `VALIDATION_ERROR` an array with one message per invalid field (the same texts as `errors[].message`). Branch on `code`, not on this.","oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"example":"Domain not found"},"code":{"description":"Stable, machine-readable error code; branch on this, not on `message`. Always present. A route-specific code when the route defines one, otherwise the generic code for the status (`NOT_FOUND`, `VALIDATION_ERROR`, `RATE_LIMITED`, ...). Open set: accept codes you do not know. Each code is documented at https://productcraft.co/docs/errors#<code>.","example":"NOT_FOUND","allOf":[{"$ref":"#/components/schemas/NotifyErrorCode"}]},"request_id":{"type":"string","description":"The request's id, same as the `X-Request-Id` response header. Quote it when contacting support.","pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$","example":"req_6f1c2a9d4b7e4f0a8c3d2e1f0a9b8c7d"},"errors":{"description":"One entry per invalid request field on a `VALIDATION_ERROR`; empty on every other error.","type":"array","items":{"$ref":"#/components/schemas/ErrorParamDto"}},"details":{"type":"object","description":"Structured context for errors a client has to act on, keyed in snake_case and specific to the code (e.g. `{ fqdn, reason }` on `from_domain_not_verified`). Absent on most errors.","additionalProperties":true,"example":{"fqdn":"mail.example.com","reason":"not_verified"}}},"required":["status_code","message","code","request_id","errors"]},"NotificationAcceptedEvent":{"type":"object","title":"NotificationAcceptedEvent","description":"A send was accepted: Notify will plan a delivery per recipient and channel. Carries the recipient counts, not the recipients.","required":["id","type","created_at","data"],"properties":{"workspace_id":{"type":"string","format":"uuid","description":"The workspace the event belongs to, so you can route without opening `data`. Absent on \"Send test\" deliveries."},"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"The event id, also sent as `webhook-id`; the same on every retry and replay."},"type":{"type":"string","enum":["notification.accepted"],"description":"The event type. Switch on it to narrow the event."},"created_at":{"type":"string","format":"date-time","example":"2026-11-05T09:00:00.000Z","description":"When the event happened (the same on every retry)."},"test":{"type":"boolean","description":"Present and true only on a delivery made by \"Send test\"; its `data` is the catalogue example."},"data":{"$ref":"#/components/schemas/NotifyNotificationAcceptedDataDto"}},"example":{"workspace_id":"9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b","id":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","type":"notification.accepted","created_at":"2026-11-05T09:00:00.000Z","data":{"notification_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a01","type_key":"invoice.paid","recipient_count":3,"skipped_count":0,"send_at":null,"tenant":null}},"x-pc-sdk-model":"NotificationAcceptedEvent"},"DeliverySentEvent":{"type":"object","title":"DeliverySentEvent","description":"A provider accepted the delivery (APNs, FCM, the browser push service, or Mail). For push this is final: Apple and Google report acceptance only.","required":["id","type","created_at","data"],"properties":{"workspace_id":{"type":"string","format":"uuid","description":"The workspace the event belongs to, so you can route without opening `data`. Absent on \"Send test\" deliveries."},"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"The event id, also sent as `webhook-id`; the same on every retry and replay."},"type":{"type":"string","enum":["delivery.sent"],"description":"The event type. Switch on it to narrow the event."},"created_at":{"type":"string","format":"date-time","example":"2026-11-05T09:00:00.000Z","description":"When the event happened (the same on every retry)."},"test":{"type":"boolean","description":"Present and true only on a delivery made by \"Send test\"; its `data` is the catalogue example."},"data":{"$ref":"#/components/schemas/NotifyDeliveryEventDataDto"}},"example":{"workspace_id":"9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b","id":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","type":"delivery.sent","created_at":"2026-11-05T09:00:00.000Z","data":{"delivery_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a02","notification_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a01","type_key":"invoice.paid","recipient":{"id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a10","external_id":"user_123"},"channel":"push","provider":"apns","device_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a20","state":"sent","reason":null,"is_fallback":false,"attempts":1}},"x-pc-sdk-model":"DeliverySentEvent"},"DeliveryDeliveredEvent":{"type":"object","title":"DeliveryDeliveredEvent","description":"The delivery reached its target: an item in the in-app inbox, or an email the receiving mail server accepted.","required":["id","type","created_at","data"],"properties":{"workspace_id":{"type":"string","format":"uuid","description":"The workspace the event belongs to, so you can route without opening `data`. Absent on \"Send test\" deliveries."},"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"The event id, also sent as `webhook-id`; the same on every retry and replay."},"type":{"type":"string","enum":["delivery.delivered"],"description":"The event type. Switch on it to narrow the event."},"created_at":{"type":"string","format":"date-time","example":"2026-11-05T09:00:00.000Z","description":"When the event happened (the same on every retry)."},"test":{"type":"boolean","description":"Present and true only on a delivery made by \"Send test\"; its `data` is the catalogue example."},"data":{"$ref":"#/components/schemas/NotifyDeliveryEventDataDto"}},"example":{"workspace_id":"9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b","id":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","type":"delivery.delivered","created_at":"2026-11-05T09:00:00.000Z","data":{"delivery_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a02","notification_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a01","type_key":"invoice.paid","recipient":{"id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a10","external_id":"user_123"},"channel":"inbox","provider":"inbox","device_id":null,"state":"delivered","reason":null,"is_fallback":false,"attempts":0}},"x-pc-sdk-model":"DeliveryDeliveredEvent"},"DeliveryFailedEvent":{"type":"object","title":"DeliveryFailedEvent","description":"The delivery failed for good (retries spent, expired, refused credential, payload too large), or the device token is dead (`state: pruned`). A fallback step, if the type has one, follows.","required":["id","type","created_at","data"],"properties":{"workspace_id":{"type":"string","format":"uuid","description":"The workspace the event belongs to, so you can route without opening `data`. Absent on \"Send test\" deliveries."},"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"The event id, also sent as `webhook-id`; the same on every retry and replay."},"type":{"type":"string","enum":["delivery.failed"],"description":"The event type. Switch on it to narrow the event."},"created_at":{"type":"string","format":"date-time","example":"2026-11-05T09:00:00.000Z","description":"When the event happened (the same on every retry)."},"test":{"type":"boolean","description":"Present and true only on a delivery made by \"Send test\"; its `data` is the catalogue example."},"data":{"$ref":"#/components/schemas/NotifyDeliveryEventDataDto"}},"example":{"workspace_id":"9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b","id":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","type":"delivery.failed","created_at":"2026-11-05T09:00:00.000Z","data":{"delivery_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a02","notification_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a01","type_key":"invoice.paid","recipient":{"id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a10","external_id":"user_123"},"channel":"push","provider":"apns","device_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a20","state":"failed","reason":"expired","is_fallback":false,"attempts":6}},"x-pc-sdk-model":"DeliveryFailedEvent"},"DeliveryBouncedEvent":{"type":"object","title":"DeliveryBouncedEvent","description":"An email bounced or drew a complaint (`reason` says which). Notify stops emailing that address; the recipient's email is marked invalid.","required":["id","type","created_at","data"],"properties":{"workspace_id":{"type":"string","format":"uuid","description":"The workspace the event belongs to, so you can route without opening `data`. Absent on \"Send test\" deliveries."},"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"The event id, also sent as `webhook-id`; the same on every retry and replay."},"type":{"type":"string","enum":["delivery.bounced"],"description":"The event type. Switch on it to narrow the event."},"created_at":{"type":"string","format":"date-time","example":"2026-11-05T09:00:00.000Z","description":"When the event happened (the same on every retry)."},"test":{"type":"boolean","description":"Present and true only on a delivery made by \"Send test\"; its `data` is the catalogue example."},"data":{"$ref":"#/components/schemas/NotifyDeliveryEventDataDto"}},"example":{"workspace_id":"9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b","id":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","type":"delivery.bounced","created_at":"2026-11-05T09:00:00.000Z","data":{"delivery_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a02","notification_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a01","type_key":"invoice.paid","recipient":{"id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a10","external_id":"user_123"},"channel":"email","provider":"mail","device_id":null,"state":"bounced","reason":"email_invalid","is_fallback":false,"attempts":1}},"x-pc-sdk-model":"DeliveryBouncedEvent"},"DeliverySkippedEvent":{"type":"object","title":"DeliverySkippedEvent","description":"Nothing was sent for this channel, with the reason: the recipient opted out, has no device or address, the channel is not configured, the send was cancelled, and so on (`NotifyDeliveryReason`).","required":["id","type","created_at","data"],"properties":{"workspace_id":{"type":"string","format":"uuid","description":"The workspace the event belongs to, so you can route without opening `data`. Absent on \"Send test\" deliveries."},"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"The event id, also sent as `webhook-id`; the same on every retry and replay."},"type":{"type":"string","enum":["delivery.skipped"],"description":"The event type. Switch on it to narrow the event."},"created_at":{"type":"string","format":"date-time","example":"2026-11-05T09:00:00.000Z","description":"When the event happened (the same on every retry)."},"test":{"type":"boolean","description":"Present and true only on a delivery made by \"Send test\"; its `data` is the catalogue example."},"data":{"$ref":"#/components/schemas/NotifyDeliveryEventDataDto"}},"example":{"workspace_id":"9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b","id":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","type":"delivery.skipped","created_at":"2026-11-05T09:00:00.000Z","data":{"delivery_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a02","notification_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a01","type_key":"invoice.paid","recipient":{"id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a10","external_id":"user_123"},"channel":"push","provider":"apns","device_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a20","state":"skipped","reason":"suppressed_preference","is_fallback":false,"attempts":0}},"x-pc-sdk-model":"DeliverySkippedEvent"},"InboxItemReadEvent":{"type":"object","title":"InboxItemReadEvent","description":"The recipient read an in-app inbox item.","required":["id","type","created_at","data"],"properties":{"workspace_id":{"type":"string","format":"uuid","description":"The workspace the event belongs to, so you can route without opening `data`. Absent on \"Send test\" deliveries."},"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"The event id, also sent as `webhook-id`; the same on every retry and replay."},"type":{"type":"string","enum":["inbox_item.read"],"description":"The event type. Switch on it to narrow the event."},"created_at":{"type":"string","format":"date-time","example":"2026-11-05T09:00:00.000Z","description":"When the event happened (the same on every retry)."},"test":{"type":"boolean","description":"Present and true only on a delivery made by \"Send test\"; its `data` is the catalogue example."},"data":{"$ref":"#/components/schemas/NotifyInboxItemEventDataDto"}},"example":{"workspace_id":"9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b","id":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","type":"inbox_item.read","created_at":"2026-11-05T09:00:00.000Z","data":{"inbox_item_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a30","notification_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a01","type_key":"invoice.paid","recipient":{"id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a10","external_id":"user_123"},"interaction_key":null}},"x-pc-sdk-model":"InboxItemReadEvent"},"InboxItemArchivedEvent":{"type":"object","title":"InboxItemArchivedEvent","description":"The recipient archived an in-app inbox item.","required":["id","type","created_at","data"],"properties":{"workspace_id":{"type":"string","format":"uuid","description":"The workspace the event belongs to, so you can route without opening `data`. Absent on \"Send test\" deliveries."},"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"The event id, also sent as `webhook-id`; the same on every retry and replay."},"type":{"type":"string","enum":["inbox_item.archived"],"description":"The event type. Switch on it to narrow the event."},"created_at":{"type":"string","format":"date-time","example":"2026-11-05T09:00:00.000Z","description":"When the event happened (the same on every retry)."},"test":{"type":"boolean","description":"Present and true only on a delivery made by \"Send test\"; its `data` is the catalogue example."},"data":{"$ref":"#/components/schemas/NotifyInboxItemEventDataDto"}},"example":{"workspace_id":"9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b","id":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","type":"inbox_item.archived","created_at":"2026-11-05T09:00:00.000Z","data":{"inbox_item_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a30","notification_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a01","type_key":"invoice.paid","recipient":{"id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a10","external_id":"user_123"},"interaction_key":null}},"x-pc-sdk-model":"InboxItemArchivedEvent"},"InboxItemInteractedEvent":{"type":"object","title":"InboxItemInteractedEvent","description":"The recipient clicked an inbox item or one of its action buttons (`interaction_key`).","required":["id","type","created_at","data"],"properties":{"workspace_id":{"type":"string","format":"uuid","description":"The workspace the event belongs to, so you can route without opening `data`. Absent on \"Send test\" deliveries."},"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"The event id, also sent as `webhook-id`; the same on every retry and replay."},"type":{"type":"string","enum":["inbox_item.interacted"],"description":"The event type. Switch on it to narrow the event."},"created_at":{"type":"string","format":"date-time","example":"2026-11-05T09:00:00.000Z","description":"When the event happened (the same on every retry)."},"test":{"type":"boolean","description":"Present and true only on a delivery made by \"Send test\"; its `data` is the catalogue example."},"data":{"$ref":"#/components/schemas/NotifyInboxItemEventDataDto"}},"example":{"workspace_id":"9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b","id":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","type":"inbox_item.interacted","created_at":"2026-11-05T09:00:00.000Z","data":{"inbox_item_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a30","notification_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a01","type_key":"invoice.paid","recipient":{"id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a10","external_id":"user_123"},"interaction_key":"approve"}},"x-pc-sdk-model":"InboxItemInteractedEvent"},"DevicePrunedEvent":{"type":"object","title":"DevicePrunedEvent","description":"A device token stopped working (APNs or FCM said it is unregistered, the browser subscription is gone, it was not seen for 270 days, or it was evicted beyond 20 devices) and Notify stopped sending to it.","required":["id","type","created_at","data"],"properties":{"workspace_id":{"type":"string","format":"uuid","description":"The workspace the event belongs to, so you can route without opening `data`. Absent on \"Send test\" deliveries."},"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"The event id, also sent as `webhook-id`; the same on every retry and replay."},"type":{"type":"string","enum":["device.pruned"],"description":"The event type. Switch on it to narrow the event."},"created_at":{"type":"string","format":"date-time","example":"2026-11-05T09:00:00.000Z","description":"When the event happened (the same on every retry)."},"test":{"type":"boolean","description":"Present and true only on a delivery made by \"Send test\"; its `data` is the catalogue example."},"data":{"$ref":"#/components/schemas/NotifyDeviceEventDataDto"}},"example":{"workspace_id":"9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b","id":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","type":"device.pruned","created_at":"2026-11-05T09:00:00.000Z","data":{"device_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a20","channel":"apns","platform":"ios","recipient":{"id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a10","external_id":"user_123"},"reason":"Unregistered","previous_recipient":null,"move_count_day":null}},"x-pc-sdk-model":"DevicePrunedEvent"},"DeviceMovedEvent":{"type":"object","title":"DeviceMovedEvent","description":"A device token was registered for another recipient (someone signed in as another user on the same phone or browser): it now belongs to `recipient` and no longer to `previous_recipient`. A token can move at most 3 times a day.","required":["id","type","created_at","data"],"properties":{"workspace_id":{"type":"string","format":"uuid","description":"The workspace the event belongs to, so you can route without opening `data`. Absent on \"Send test\" deliveries."},"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"The event id, also sent as `webhook-id`; the same on every retry and replay."},"type":{"type":"string","enum":["device.moved"],"description":"The event type. Switch on it to narrow the event."},"created_at":{"type":"string","format":"date-time","example":"2026-11-05T09:00:00.000Z","description":"When the event happened (the same on every retry)."},"test":{"type":"boolean","description":"Present and true only on a delivery made by \"Send test\"; its `data` is the catalogue example."},"data":{"$ref":"#/components/schemas/NotifyDeviceEventDataDto"}},"example":{"workspace_id":"9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b","id":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","type":"device.moved","created_at":"2026-11-05T09:00:00.000Z","data":{"device_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a20","channel":"apns","platform":"ios","recipient":{"id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a11","external_id":"user_456"},"reason":null,"previous_recipient":{"id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a10","external_id":"user_123"},"move_count_day":1}},"x-pc-sdk-model":"DeviceMovedEvent"},"CredentialInvalidEvent":{"type":"object","title":"CredentialInvalidEvent","description":"Apple or Google refused an APNs key or FCM service account. Push through it stops until you upload a working one.","required":["id","type","created_at","data"],"properties":{"workspace_id":{"type":"string","format":"uuid","description":"The workspace the event belongs to, so you can route without opening `data`. Absent on \"Send test\" deliveries."},"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"The event id, also sent as `webhook-id`; the same on every retry and replay."},"type":{"type":"string","enum":["credential.invalid"],"description":"The event type. Switch on it to narrow the event."},"created_at":{"type":"string","format":"date-time","example":"2026-11-05T09:00:00.000Z","description":"When the event happened (the same on every retry)."},"test":{"type":"boolean","description":"Present and true only on a delivery made by \"Send test\"; its `data` is the catalogue example."},"data":{"$ref":"#/components/schemas/NotifyCredentialInvalidDataDto"}},"example":{"workspace_id":"9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b","id":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","type":"credential.invalid","created_at":"2026-11-05T09:00:00.000Z","data":{"credential_id":"0193a5f2-7c1e-7b3a-9f10-2b6c4d8e9a40","provider":"apns","app_id":"com.example.app","environment":"production","error_code":"InvalidProviderToken"}},"x-pc-sdk-model":"CredentialInvalidEvent"},"EmailAutoPausedEvent":{"type":"object","title":"EmailAutoPausedEvent","description":"Notify paused email for the workspace: more than 0.1 % complaints or 5 % hard bounces among recent emails. Fix the cause, then resume it in the settings.","required":["id","type","created_at","data"],"properties":{"workspace_id":{"type":"string","format":"uuid","description":"The workspace the event belongs to, so you can route without opening `data`. Absent on \"Send test\" deliveries."},"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"The event id, also sent as `webhook-id`; the same on every retry and replay."},"type":{"type":"string","enum":["email.auto_paused"],"description":"The event type. Switch on it to narrow the event."},"created_at":{"type":"string","format":"date-time","example":"2026-11-05T09:00:00.000Z","description":"When the event happened (the same on every retry)."},"test":{"type":"boolean","description":"Present and true only on a delivery made by \"Send test\"; its `data` is the catalogue example."},"data":{"$ref":"#/components/schemas/NotifyEmailAutoPausedDataDto"}},"example":{"workspace_id":"9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b","id":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","type":"email.auto_paused","created_at":"2026-11-05T09:00:00.000Z","data":{"reason":"complaints","complaints":2,"bounces":3,"window":500}},"x-pc-sdk-model":"EmailAutoPausedEvent"},"RecipientDeletedEvent":{"type":"object","title":"RecipientDeletedEvent","description":"A recipient was erased with their devices, preferences and inbox. Carries your id for them only.","required":["id","type","created_at","data"],"properties":{"workspace_id":{"type":"string","format":"uuid","description":"The workspace the event belongs to, so you can route without opening `data`. Absent on \"Send test\" deliveries."},"id":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$","example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","description":"The event id, also sent as `webhook-id`; the same on every retry and replay."},"type":{"type":"string","enum":["recipient.deleted"],"description":"The event type. Switch on it to narrow the event."},"created_at":{"type":"string","format":"date-time","example":"2026-11-05T09:00:00.000Z","description":"When the event happened (the same on every retry)."},"test":{"type":"boolean","description":"Present and true only on a delivery made by \"Send test\"; its `data` is the catalogue example."},"data":{"$ref":"#/components/schemas/NotifyRecipientDeletedDataDto"}},"example":{"workspace_id":"9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b","id":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f","type":"recipient.deleted","created_at":"2026-11-05T09:00:00.000Z","data":{"external_id":"user_123","cause":"api"}},"x-pc-sdk-model":"RecipientDeletedEvent"},"NotifyErrorCode":{"description":"Error codes this API returns (open set: a client must accept codes it does not know; new codes are not a breaking change). Each is documented at https://productcraft.co/docs/errors#<code>.","anyOf":[{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","PAYMENT_REQUIRED","FORBIDDEN","NOT_FOUND","METHOD_NOT_ALLOWED","NOT_ACCEPTABLE","REQUEST_TIMEOUT","CONFLICT","GONE","PRECONDITION_FAILED","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","UNPROCESSABLE_ENTITY","PRECONDITION_REQUIRED","RATE_LIMITED","INTERNAL_SERVER_ERROR","NOT_IMPLEMENTED","BAD_GATEWAY","SERVICE_UNAVAILABLE","GATEWAY_TIMEOUT","SERVICE_NOT_ENABLED","INVALID_CREDENTIALS","SESSION_REVOKED","MCP_NOT_PERMITTED","MCP_WORKSPACE_MISMATCH","PAK_NOT_PERMITTED","ACTIONS_NOT_GRANTABLE","IDEMPOTENCY_KEY_REUSE","IDEMPOTENCY_KEY_REQUIRED","IDEMPOTENCY_IN_PROGRESS","WEBHOOK_ENDPOINT_LIMIT","WEBHOOK_ENDPOINT_NOT_FOUND","WEBHOOK_ENDPOINT_NOT_SUBSCRIBED","WEBHOOK_EVENT_NOT_FOUND","WEBHOOK_EVENT_TYPE_UNKNOWN","NOTIFICATION_TYPE_NOT_FOUND","NOTIFICATION_TYPE_ARCHIVED","RECIPIENT_NOT_FOUND","TOO_MANY_RECIPIENTS","DATA_TOO_LARGE","TEMPLATE_MISSING","TEMPLATE_INVALID","CHANNEL_NOT_CONFIGURED","CREDENTIAL_INVALID","CREDENTIAL_NOT_FOUND","CREDENTIAL_MISMATCH","DEVICE_TOKEN_INVALID","WEBPUSH_ENDPOINT_NOT_ALLOWED","RECIPIENT_TOKEN_INVALID","RECIPIENT_TOKEN_EXPIRED","RECIPIENT_TOKEN_SCOPE","ORIGIN_NOT_ALLOWED","STREAM_LIMIT","LIMIT_EXCEEDED","SEND_AT_OUT_OF_RANGE","UNSAFE_URL","AUTH_USER_ALREADY_LINKED","DEVICE_NOT_FOUND","CATEGORY_NOT_FOUND","CATEGORY_KEY_TAKEN","CATEGORY_IN_USE","NOTIFICATION_TYPE_KEY_TAKEN","TEMPLATE_NOT_FOUND","TEMPLATE_VERSION_NOT_FOUND","PREFERENCE_LOCKED","NOTIFICATION_NOT_FOUND","DELIVERY_NOT_FOUND","CONFIRMATION_REQUIRED","INBOX_ITEM_NOT_FOUND","RECIPIENT_ERASED","DEVICE_MOVE_LIMIT"]},{"type":"string","description":"A code added after this document was published."}],"x-pc-open-enum":true,"x-pc-error-docs":"https://productcraft.co/docs/errors#{code}","x-enum-descriptions":{"BAD_REQUEST":"The request is malformed (not valid JSON, a bad header, an unusable parameter).","VALIDATION_ERROR":"One or more request fields are invalid; `errors` lists each field (`param`, `message`, `code`).","UNAUTHORIZED":"No credentials, or the token or API key is invalid, expired or revoked.","PAYMENT_REQUIRED":"The workspace has no credit or plan for this operation.","FORBIDDEN":"The caller is authenticated but not allowed to do this.","NOT_FOUND":"The resource does not exist, or belongs to another workspace or app.","METHOD_NOT_ALLOWED":"The route does not accept this HTTP method.","NOT_ACCEPTABLE":"The route cannot produce the requested content type.","REQUEST_TIMEOUT":"The request took too long to arrive.","CONFLICT":"The request conflicts with the current state of the resource.","GONE":"The resource existed but has been removed for good.","PRECONDITION_FAILED":"A precondition header did not match.","PAYLOAD_TOO_LARGE":"The request body is over the size limit.","UNSUPPORTED_MEDIA_TYPE":"The request body is not in a content type the route accepts.","UNPROCESSABLE_ENTITY":"The request is well-formed but cannot be carried out.","PRECONDITION_REQUIRED":"The route requires a precondition header.","RATE_LIMITED":"Too many requests; retry after the `Retry-After` header.","INTERNAL_SERVER_ERROR":"An unexpected server error. Retrying is safe for idempotent requests; quote `request_id` to support.","NOT_IMPLEMENTED":"The server does not support this operation.","BAD_GATEWAY":"An upstream service returned an invalid response.","SERVICE_UNAVAILABLE":"The service or one of its dependencies is temporarily unavailable; retry.","GATEWAY_TIMEOUT":"An upstream service did not answer in time; retry.","SERVICE_NOT_ENABLED":"The workspace has not enabled this product.","INVALID_CREDENTIALS":"The credentials were not accepted.","SESSION_REVOKED":"The session behind this token was signed out or revoked.","MCP_NOT_PERMITTED":"An MCP (agent) token cannot call this route.","MCP_WORKSPACE_MISMATCH":"The MCP token was issued for a different workspace.","PAK_NOT_PERMITTED":"A workspace API key cannot call this route.","ACTIONS_NOT_GRANTABLE":"The policy grants actions the caller does not hold.","IDEMPOTENCY_KEY_REUSE":"The `Idempotency-Key` was already used with a different body or route.","IDEMPOTENCY_KEY_REQUIRED":"This operation requires an `Idempotency-Key` header.","IDEMPOTENCY_IN_PROGRESS":"A request with the same `Idempotency-Key` is still running. Retry after `Retry-After` seconds with the same key to get its response.","WEBHOOK_ENDPOINT_LIMIT":"The workspace has the maximum number of webhook endpoints.","WEBHOOK_ENDPOINT_NOT_FOUND":"No webhook endpoint with this id in this workspace.","WEBHOOK_ENDPOINT_NOT_SUBSCRIBED":"The endpoint is not subscribed to this event type.","WEBHOOK_EVENT_NOT_FOUND":"No webhook event with this id in this workspace.","WEBHOOK_EVENT_TYPE_UNKNOWN":"The event type is not one this product sends.","NOTIFICATION_TYPE_NOT_FOUND":"No notification type with this id or key in this workspace.","NOTIFICATION_TYPE_ARCHIVED":"The notification type is archived and cannot be sent.","RECIPIENT_NOT_FOUND":"No recipient with this external id in this workspace (on a send: none of the recipients is usable).","TOO_MANY_RECIPIENTS":"The request names more recipients than one call accepts.","DATA_TOO_LARGE":"The `data` object is over the size limit.","TEMPLATE_MISSING":"The notification type has no template for any of the requested channels.","TEMPLATE_INVALID":"The template does not parse, uses an unsupported construct or has an invalid field.","CHANNEL_NOT_CONFIGURED":"The channel has no credentials configured in this workspace.","CREDENTIAL_INVALID":"The uploaded provider credential was rejected (malformed, wrong type or refused by the provider).","CREDENTIAL_NOT_FOUND":"No channel credential with this id in this workspace.","CREDENTIAL_MISMATCH":"The credential belongs to a different app (bundle id or Firebase project id differs).","DEVICE_TOKEN_INVALID":"The device token or web push subscription is malformed for its platform.","WEBPUSH_ENDPOINT_NOT_ALLOWED":"The web push subscription endpoint is not a known push service.","RECIPIENT_TOKEN_INVALID":"The recipient token is malformed, has a bad signature or was revoked.","RECIPIENT_TOKEN_EXPIRED":"The recipient token has expired; mint a new one from your backend.","RECIPIENT_TOKEN_SCOPE":"The recipient token does not allow this operation.","ORIGIN_NOT_ALLOWED":"The request origin is not in the workspace client origins.","STREAM_LIMIT":"The recipient has the maximum number of open real-time streams.","LIMIT_EXCEEDED":"A preview limit of the workspace was reached.","SEND_AT_OUT_OF_RANGE":"`send_at` or `delay_seconds` is in the past or beyond the type TTL.","UNSAFE_URL":"A URL field does not use https (http is accepted only for localhost).","AUTH_USER_ALREADY_LINKED":"The Auth end user is already linked to another recipient in this workspace.","DEVICE_NOT_FOUND":"The recipient has no device with this id.","CATEGORY_NOT_FOUND":"No category with this id or key in this workspace.","CATEGORY_KEY_TAKEN":"A category with this key already exists in the workspace.","CATEGORY_IN_USE":"The category is used by notification types; move them to another category first.","NOTIFICATION_TYPE_KEY_TAKEN":"A notification type with this key already exists in the workspace.","TEMPLATE_NOT_FOUND":"The notification type has no template for this channel and locale.","TEMPLATE_VERSION_NOT_FOUND":"That version of the template is not kept (the last 20 are).","PREFERENCE_LOCKED":"The channel is mandatory for this category and cannot be turned off.","NOTIFICATION_NOT_FOUND":"No notification with this id in this workspace.","DELIVERY_NOT_FOUND":"No delivery with this id in this workspace.","CONFIRMATION_REQUIRED":"The change is destructive and needs an explicit confirmation field (e.g. `confirm: \"rotate\"`).","INBOX_ITEM_NOT_FOUND":"The recipient has no inbox item with this id (or the sender retracted it).","RECIPIENT_ERASED":"The recipient was erased in the last 24 hours (its linked Auth user was deleted) and cannot be recreated yet.","DEVICE_MOVE_LIMIT":"The device was moved between recipients too often; wait before moving it again."}}},"headers":{"X-RateLimit-Limit-short":{"description":"Requests allowed per second from this client IP (20).","schema":{"type":"integer"}},"X-RateLimit-Remaining-short":{"description":"Requests left in the current one-second window.","schema":{"type":"integer"}},"X-RateLimit-Reset-short":{"description":"Seconds until the one-second window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit-long":{"description":"Requests allowed per minute from this client IP (600).","schema":{"type":"integer"}},"X-RateLimit-Remaining-long":{"description":"Requests left in the current one-minute window.","schema":{"type":"integer"}},"X-RateLimit-Reset-long":{"description":"Seconds until the one-minute window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying. Sent on every 429.","schema":{"type":"integer"}},"Retry-After-short":{"description":"Deprecated: read `Retry-After`. Seconds to wait, when the per-second IP limit tripped.","deprecated":true,"schema":{"type":"integer"}},"Retry-After-long":{"description":"Deprecated: read `Retry-After`. Seconds to wait, when the per-minute IP limit tripped.","deprecated":true,"schema":{"type":"integer"}},"RequestId":{"description":"The request's id: the inbound `X-Request-Id` when it is 1 to 128 characters of `A-Z a-z 0-9 . _ : -`, otherwise a fresh `req_...` id. Error bodies repeat it as `request_id`. Quote it when contacting support.","schema":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$","example":"req_6f1c2a9d4b7e4f0a8c3d2e1f0a9b8c7d"}}}},"x-webhooks":{"notification.accepted":{"post":{"summary":"A send was accepted: Notify will plan a delivery per recipient and channel. Carries the recipient counts, not the recipients.","description":"A `notification.accepted` event (Notifications), POSTed as JSON to every enabled endpoint subscribed to it and signed with the Standard Webhooks headers. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h.","tags":["webhooks"],"x-pc-webhook-category":"Notifications","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id: the same on every retry and replay, so dedupe on it. Part of the signed content.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"},"example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds of this attempt. Part of the signed content; reject a delivery more than 5 minutes from your clock.","schema":{"type":"string","pattern":"^\\d+$"},"example":"1793869200"},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"<webhook-id>.<webhook-timestamp>.<raw body>\">`, keyed with the base64-decoded part of the endpoint's `whsec_` secret. During a secret rotation it carries one space-separated `v1,` entry per active secret; accept the delivery when any entry matches.","schema":{"type":"string"},"example":"v1,lkO4DTG3zOoxV5QD2PC5B41Sc8nqZNoh50NPrwnhJ5I="}],"requestBody":{"required":true,"description":"The `notification.accepted` event. Verify the signature over the raw body before parsing it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationAcceptedEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h."}}}},"delivery.sent":{"post":{"summary":"A provider accepted the delivery (APNs, FCM, the browser push service, or Mail). For push this is final: Apple and Google report acceptance only.","description":"A `delivery.sent` event (Deliveries), POSTed as JSON to every enabled endpoint subscribed to it and signed with the Standard Webhooks headers. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h.","tags":["webhooks"],"x-pc-webhook-category":"Deliveries","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id: the same on every retry and replay, so dedupe on it. Part of the signed content.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"},"example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds of this attempt. Part of the signed content; reject a delivery more than 5 minutes from your clock.","schema":{"type":"string","pattern":"^\\d+$"},"example":"1793869200"},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"<webhook-id>.<webhook-timestamp>.<raw body>\">`, keyed with the base64-decoded part of the endpoint's `whsec_` secret. During a secret rotation it carries one space-separated `v1,` entry per active secret; accept the delivery when any entry matches.","schema":{"type":"string"},"example":"v1,lkO4DTG3zOoxV5QD2PC5B41Sc8nqZNoh50NPrwnhJ5I="}],"requestBody":{"required":true,"description":"The `delivery.sent` event. Verify the signature over the raw body before parsing it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliverySentEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h."}}}},"delivery.delivered":{"post":{"summary":"The delivery reached its target: an item in the in-app inbox, or an email the receiving mail server accepted.","description":"A `delivery.delivered` event (Deliveries), POSTed as JSON to every enabled endpoint subscribed to it and signed with the Standard Webhooks headers. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h.","tags":["webhooks"],"x-pc-webhook-category":"Deliveries","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id: the same on every retry and replay, so dedupe on it. Part of the signed content.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"},"example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds of this attempt. Part of the signed content; reject a delivery more than 5 minutes from your clock.","schema":{"type":"string","pattern":"^\\d+$"},"example":"1793869200"},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"<webhook-id>.<webhook-timestamp>.<raw body>\">`, keyed with the base64-decoded part of the endpoint's `whsec_` secret. During a secret rotation it carries one space-separated `v1,` entry per active secret; accept the delivery when any entry matches.","schema":{"type":"string"},"example":"v1,lkO4DTG3zOoxV5QD2PC5B41Sc8nqZNoh50NPrwnhJ5I="}],"requestBody":{"required":true,"description":"The `delivery.delivered` event. Verify the signature over the raw body before parsing it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryDeliveredEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h."}}}},"delivery.failed":{"post":{"summary":"The delivery failed for good (retries spent, expired, refused credential, payload too large), or the device token is dead (`state: pruned`). A fallback step, if the type has one, follows.","description":"A `delivery.failed` event (Deliveries), POSTed as JSON to every enabled endpoint subscribed to it and signed with the Standard Webhooks headers. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h.","tags":["webhooks"],"x-pc-webhook-category":"Deliveries","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id: the same on every retry and replay, so dedupe on it. Part of the signed content.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"},"example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds of this attempt. Part of the signed content; reject a delivery more than 5 minutes from your clock.","schema":{"type":"string","pattern":"^\\d+$"},"example":"1793869200"},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"<webhook-id>.<webhook-timestamp>.<raw body>\">`, keyed with the base64-decoded part of the endpoint's `whsec_` secret. During a secret rotation it carries one space-separated `v1,` entry per active secret; accept the delivery when any entry matches.","schema":{"type":"string"},"example":"v1,lkO4DTG3zOoxV5QD2PC5B41Sc8nqZNoh50NPrwnhJ5I="}],"requestBody":{"required":true,"description":"The `delivery.failed` event. Verify the signature over the raw body before parsing it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryFailedEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h."}}}},"delivery.bounced":{"post":{"summary":"An email bounced or drew a complaint (`reason` says which). Notify stops emailing that address; the recipient's email is marked invalid.","description":"A `delivery.bounced` event (Deliveries), POSTed as JSON to every enabled endpoint subscribed to it and signed with the Standard Webhooks headers. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h.","tags":["webhooks"],"x-pc-webhook-category":"Deliveries","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id: the same on every retry and replay, so dedupe on it. Part of the signed content.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"},"example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds of this attempt. Part of the signed content; reject a delivery more than 5 minutes from your clock.","schema":{"type":"string","pattern":"^\\d+$"},"example":"1793869200"},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"<webhook-id>.<webhook-timestamp>.<raw body>\">`, keyed with the base64-decoded part of the endpoint's `whsec_` secret. During a secret rotation it carries one space-separated `v1,` entry per active secret; accept the delivery when any entry matches.","schema":{"type":"string"},"example":"v1,lkO4DTG3zOoxV5QD2PC5B41Sc8nqZNoh50NPrwnhJ5I="}],"requestBody":{"required":true,"description":"The `delivery.bounced` event. Verify the signature over the raw body before parsing it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryBouncedEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h."}}}},"delivery.skipped":{"post":{"summary":"Nothing was sent for this channel, with the reason: the recipient opted out, has no device or address, the channel is not configured, the send was cancelled, and so on (`NotifyDeliveryReason`).","description":"A `delivery.skipped` event (Deliveries), POSTed as JSON to every enabled endpoint subscribed to it and signed with the Standard Webhooks headers. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h.","tags":["webhooks"],"x-pc-webhook-category":"Deliveries","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id: the same on every retry and replay, so dedupe on it. Part of the signed content.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"},"example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds of this attempt. Part of the signed content; reject a delivery more than 5 minutes from your clock.","schema":{"type":"string","pattern":"^\\d+$"},"example":"1793869200"},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"<webhook-id>.<webhook-timestamp>.<raw body>\">`, keyed with the base64-decoded part of the endpoint's `whsec_` secret. During a secret rotation it carries one space-separated `v1,` entry per active secret; accept the delivery when any entry matches.","schema":{"type":"string"},"example":"v1,lkO4DTG3zOoxV5QD2PC5B41Sc8nqZNoh50NPrwnhJ5I="}],"requestBody":{"required":true,"description":"The `delivery.skipped` event. Verify the signature over the raw body before parsing it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliverySkippedEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h."}}}},"inbox_item.read":{"post":{"summary":"The recipient read an in-app inbox item.","description":"A `inbox_item.read` event (Inbox), POSTed as JSON to every enabled endpoint subscribed to it and signed with the Standard Webhooks headers. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h.","tags":["webhooks"],"x-pc-webhook-category":"Inbox","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id: the same on every retry and replay, so dedupe on it. Part of the signed content.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"},"example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds of this attempt. Part of the signed content; reject a delivery more than 5 minutes from your clock.","schema":{"type":"string","pattern":"^\\d+$"},"example":"1793869200"},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"<webhook-id>.<webhook-timestamp>.<raw body>\">`, keyed with the base64-decoded part of the endpoint's `whsec_` secret. During a secret rotation it carries one space-separated `v1,` entry per active secret; accept the delivery when any entry matches.","schema":{"type":"string"},"example":"v1,lkO4DTG3zOoxV5QD2PC5B41Sc8nqZNoh50NPrwnhJ5I="}],"requestBody":{"required":true,"description":"The `inbox_item.read` event. Verify the signature over the raw body before parsing it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboxItemReadEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h."}}}},"inbox_item.archived":{"post":{"summary":"The recipient archived an in-app inbox item.","description":"A `inbox_item.archived` event (Inbox), POSTed as JSON to every enabled endpoint subscribed to it and signed with the Standard Webhooks headers. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h.","tags":["webhooks"],"x-pc-webhook-category":"Inbox","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id: the same on every retry and replay, so dedupe on it. Part of the signed content.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"},"example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds of this attempt. Part of the signed content; reject a delivery more than 5 minutes from your clock.","schema":{"type":"string","pattern":"^\\d+$"},"example":"1793869200"},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"<webhook-id>.<webhook-timestamp>.<raw body>\">`, keyed with the base64-decoded part of the endpoint's `whsec_` secret. During a secret rotation it carries one space-separated `v1,` entry per active secret; accept the delivery when any entry matches.","schema":{"type":"string"},"example":"v1,lkO4DTG3zOoxV5QD2PC5B41Sc8nqZNoh50NPrwnhJ5I="}],"requestBody":{"required":true,"description":"The `inbox_item.archived` event. Verify the signature over the raw body before parsing it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboxItemArchivedEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h."}}}},"inbox_item.interacted":{"post":{"summary":"The recipient clicked an inbox item or one of its action buttons (`interaction_key`).","description":"A `inbox_item.interacted` event (Inbox), POSTed as JSON to every enabled endpoint subscribed to it and signed with the Standard Webhooks headers. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h.","tags":["webhooks"],"x-pc-webhook-category":"Inbox","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id: the same on every retry and replay, so dedupe on it. Part of the signed content.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"},"example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds of this attempt. Part of the signed content; reject a delivery more than 5 minutes from your clock.","schema":{"type":"string","pattern":"^\\d+$"},"example":"1793869200"},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"<webhook-id>.<webhook-timestamp>.<raw body>\">`, keyed with the base64-decoded part of the endpoint's `whsec_` secret. During a secret rotation it carries one space-separated `v1,` entry per active secret; accept the delivery when any entry matches.","schema":{"type":"string"},"example":"v1,lkO4DTG3zOoxV5QD2PC5B41Sc8nqZNoh50NPrwnhJ5I="}],"requestBody":{"required":true,"description":"The `inbox_item.interacted` event. Verify the signature over the raw body before parsing it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboxItemInteractedEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h."}}}},"device.pruned":{"post":{"summary":"A device token stopped working (APNs or FCM said it is unregistered, the browser subscription is gone, it was not seen for 270 days, or it was evicted beyond 20 devices) and Notify stopped sending to it.","description":"A `device.pruned` event (Devices), POSTed as JSON to every enabled endpoint subscribed to it and signed with the Standard Webhooks headers. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h.","tags":["webhooks"],"x-pc-webhook-category":"Devices","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id: the same on every retry and replay, so dedupe on it. Part of the signed content.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"},"example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds of this attempt. Part of the signed content; reject a delivery more than 5 minutes from your clock.","schema":{"type":"string","pattern":"^\\d+$"},"example":"1793869200"},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"<webhook-id>.<webhook-timestamp>.<raw body>\">`, keyed with the base64-decoded part of the endpoint's `whsec_` secret. During a secret rotation it carries one space-separated `v1,` entry per active secret; accept the delivery when any entry matches.","schema":{"type":"string"},"example":"v1,lkO4DTG3zOoxV5QD2PC5B41Sc8nqZNoh50NPrwnhJ5I="}],"requestBody":{"required":true,"description":"The `device.pruned` event. Verify the signature over the raw body before parsing it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DevicePrunedEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h."}}}},"device.moved":{"post":{"summary":"A device token was registered for another recipient (someone signed in as another user on the same phone or browser): it now belongs to `recipient` and no longer to `previous_recipient`. A token can move at most 3 times a day.","description":"A `device.moved` event (Devices), POSTed as JSON to every enabled endpoint subscribed to it and signed with the Standard Webhooks headers. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h.","tags":["webhooks"],"x-pc-webhook-category":"Devices","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id: the same on every retry and replay, so dedupe on it. Part of the signed content.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"},"example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds of this attempt. Part of the signed content; reject a delivery more than 5 minutes from your clock.","schema":{"type":"string","pattern":"^\\d+$"},"example":"1793869200"},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"<webhook-id>.<webhook-timestamp>.<raw body>\">`, keyed with the base64-decoded part of the endpoint's `whsec_` secret. During a secret rotation it carries one space-separated `v1,` entry per active secret; accept the delivery when any entry matches.","schema":{"type":"string"},"example":"v1,lkO4DTG3zOoxV5QD2PC5B41Sc8nqZNoh50NPrwnhJ5I="}],"requestBody":{"required":true,"description":"The `device.moved` event. Verify the signature over the raw body before parsing it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceMovedEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h."}}}},"credential.invalid":{"post":{"summary":"Apple or Google refused an APNs key or FCM service account. Push through it stops until you upload a working one.","description":"A `credential.invalid` event (Credentials), POSTed as JSON to every enabled endpoint subscribed to it and signed with the Standard Webhooks headers. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h.","tags":["webhooks"],"x-pc-webhook-category":"Credentials","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id: the same on every retry and replay, so dedupe on it. Part of the signed content.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"},"example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds of this attempt. Part of the signed content; reject a delivery more than 5 minutes from your clock.","schema":{"type":"string","pattern":"^\\d+$"},"example":"1793869200"},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"<webhook-id>.<webhook-timestamp>.<raw body>\">`, keyed with the base64-decoded part of the endpoint's `whsec_` secret. During a secret rotation it carries one space-separated `v1,` entry per active secret; accept the delivery when any entry matches.","schema":{"type":"string"},"example":"v1,lkO4DTG3zOoxV5QD2PC5B41Sc8nqZNoh50NPrwnhJ5I="}],"requestBody":{"required":true,"description":"The `credential.invalid` event. Verify the signature over the raw body before parsing it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialInvalidEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h."}}}},"email.auto_paused":{"post":{"summary":"Notify paused email for the workspace: more than 0.1 % complaints or 5 % hard bounces among recent emails. Fix the cause, then resume it in the settings.","description":"A `email.auto_paused` event (Email), POSTed as JSON to every enabled endpoint subscribed to it and signed with the Standard Webhooks headers. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h.","tags":["webhooks"],"x-pc-webhook-category":"Email","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id: the same on every retry and replay, so dedupe on it. Part of the signed content.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"},"example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds of this attempt. Part of the signed content; reject a delivery more than 5 minutes from your clock.","schema":{"type":"string","pattern":"^\\d+$"},"example":"1793869200"},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"<webhook-id>.<webhook-timestamp>.<raw body>\">`, keyed with the base64-decoded part of the endpoint's `whsec_` secret. During a secret rotation it carries one space-separated `v1,` entry per active secret; accept the delivery when any entry matches.","schema":{"type":"string"},"example":"v1,lkO4DTG3zOoxV5QD2PC5B41Sc8nqZNoh50NPrwnhJ5I="}],"requestBody":{"required":true,"description":"The `email.auto_paused` event. Verify the signature over the raw body before parsing it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAutoPausedEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h."}}}},"recipient.deleted":{"post":{"summary":"A recipient was erased with their devices, preferences and inbox. Carries your id for them only.","description":"A `recipient.deleted` event (Recipients), POSTed as JSON to every enabled endpoint subscribed to it and signed with the Standard Webhooks headers. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h.","tags":["webhooks"],"x-pc-webhook-category":"Recipients","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"The event id: the same on every retry and replay, so dedupe on it. Part of the signed content.","schema":{"type":"string","pattern":"^evt_[0-9a-f-]{36}$"},"example":"evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f"},{"name":"webhook-timestamp","in":"header","required":true,"description":"Unix seconds of this attempt. Part of the signed content; reject a delivery more than 5 minutes from your clock.","schema":{"type":"string","pattern":"^\\d+$"},"example":"1793869200"},{"name":"webhook-signature","in":"header","required":true,"description":"`v1,<base64 HMAC-SHA256 of \"<webhook-id>.<webhook-timestamp>.<raw body>\">`, keyed with the base64-decoded part of the endpoint's `whsec_` secret. During a secret rotation it carries one space-separated `v1,` entry per active secret; accept the delivery when any entry matches.","schema":{"type":"string"},"example":"v1,lkO4DTG3zOoxV5QD2PC5B41Sc8nqZNoh50NPrwnhJ5I="}],"requestBody":{"required":true,"description":"The `recipient.deleted` event. Verify the signature over the raw body before parsing it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientDeletedEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event. Answer with any 2xx within 15 s to acknowledge it; anything else, or no answer, is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 24 h."}}}}},"x-pc-webhook-signing":{"scheme":"standard-webhooks","spec_url":"https://www.standardwebhooks.com","algorithm":"HMAC-SHA256","headers":{"id":"webhook-id","timestamp":"webhook-timestamp","signature":"webhook-signature"},"signed_content":"{webhook-id}.{webhook-timestamp}.{body}","signature_format":"v1,<base64>","secret_prefix":"whsec_","secret_key":"base64 decode of the part after `whsec_`; a secret without the prefix (minted before it) is used as its UTF-8 bytes","tolerance_seconds":300,"rotation":"space-separated `v1,` entries, one per active secret, current first"}}