CustomDomain Webhooks API
Registering receivers and inspecting delivery attempts.
Registering receivers and inspecting delivery attempts.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/customdomain-ai:customdomain-ai-webhooks-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Customdomain Ai Webhooks API
version: '1.0'
license:
name: Apache-2.0
identifier: Apache-2.0
description: 'Operations tagged Webhooks across 2 of this provider''s published API definitions: customdomain-ai-openapi-original.json, customdomain-ai-openapi.yml. Each path carries the servers of the definition it was published in.'
servers:
- url: https://api.customdomain.ai/v1
description: Hosted control-plane
- url: http://localhost:8080/v1
description: Local control-plane
security:
- apiKey: []
tags:
- name: Webhooks
description: Registering receivers and inspecting delivery attempts.
paths:
/webhooks:
post:
operationId: createWebhook
summary: Register a webhook endpoint (secret returned once)
tags:
- Webhooks
description: Registers an HTTPS receiver under the calling application. The `secret` (`whsec_…`) is returned ONCE. Store it to verify signatures. An empty `events` array subscribes to all events; `"*"` also matches all.
security:
- widgetJWT: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- url
properties:
url:
type: string
format: uri
events:
type: array
description: 'Event types to subscribe to; empty subscribes to all events, and `"*"` also matches all. Every value listed here has a producer: the enum is asserted against the emit sites in CI, so you can never subscribe to an event that cannot arrive. `connection.records.updated` / `connection.reapply.failed` / `connection.disconnected` cover the managed (async one-click) re-apply and disconnect lifecycle; `connection.disconnected` carries a `removal` object saying what happened to the DNS records. `certificate.issued` / `certificate.renewed` / `certificate.failed` follow the edge''s certificate reports, and `certificate.expiring` fires 14, 7 and 3 days before an unrenewed certificate expires and when it expired; each carries a `certificate` object. `domain.record_missing` / `domain.record_restored` carry a `drift` object with the record, what public DNS shows and what was done about it. `connection.records_outdated` fires once per domain a routing change moves (saving a connect target, or `POST /v1/connections/{id}:retarget`), carrying `records.previous` and `records.current`; the domain needs the new records. `certificate.failed` fires once per failure episode, when the edge first reports that issuing or renewing the certificate failed. `secure_status` is sent only where the deployment has Secure on (`SECURE_ENABLED`), once each time a certificate''s status changes. Every connection, certificate and routing event also appears on the domain''s timeline (`GET /v1/connections/{id}/events`) as a row of the same kind. The `nameservers.*` events follow a nameserver migration (`POST /v1/connections/{id}/nameservers:migrate`), each with a `nameservers` object (`NameserverEventData`): `started`, `dnssec_cleared` (the registry stopped publishing the DS record), `changed` (the registrar took the new nameservers), `active` (the registry lists every one of them), `failed` (with `failure_code`), `rolled_back`, and `drifted` (an active migration''s nameservers were changed away; `observed_nameservers` says to what). They go to the migration''s own application only. The `credential.created`, `credential.expiring`, `credential.failed` and `credential.removed` events follow the lasting API key the cloud browser''s agent made at the domain''s DNS provider (made and kept, renewed by a reconnect, 30 and 7 days before its end, refused by the provider or ended, removed); each carries a `credential` object with the key''s record, never the key, its `rights` (`transfer`, `billing` or `account`: what it can do beyond DNS), and `reconnect: true` when the end user should sign in again.'
items:
type: string
enum:
- '*'
- domain.record_missing
- domain.record_restored
- domain.purchased
- purchase.error
- purchase.confirmation.expired
- secure_status
- power_status
- connection.created
- connection.applied
- connection.live
- connection.failed
- connection.records.updated
- connection.reapply.failed
- connection.records_outdated
- connection.disconnected
- certificate.issued
- certificate.renewed
- certificate.failed
- certificate.expiring
- credential.created
- credential.expiring
- credential.failed
- credential.removed
- nameservers.started
- nameservers.dnssec_cleared
- nameservers.changed
- nameservers.active
- nameservers.failed
- nameservers.rolled_back
- nameservers.drifted
responses:
'201':
description: Created (secret returned once)
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookEndpoint'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
get:
operationId: listWebhooks
summary: List webhook endpoints
tags:
- Webhooks
description: 'With an API key: endpoints across all the tenant''s live apps (a test mode application''s endpoints are left out unless `?app_id=` names it). With a test mode API key: that application''s endpoints. With a widget JWT: the calling app''s endpoints. Secrets are omitted.'
security:
- apiKey: []
- widgetJWT: []
parameters:
- name: app_id
in: query
required: false
schema:
type: string
description: Narrow to one application, which is how a live key reaches a test mode application's endpoints.
responses:
'200':
description: Endpoints
content:
application/json:
schema:
type: object
required:
- webhooks
properties:
webhooks:
type: array
items:
$ref: '#/components/schemas/WebhookEndpoint'
'401':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
servers:
- url: https://api.customdomain.ai/v1
description: Hosted control-plane
- url: http://localhost:8080/v1
description: Local control-plane
/webhooks/{id}:
delete:
operationId: deleteWebhook
summary: Delete a webhook endpoint
tags:
- Webhooks
security:
- widgetJWT: []
parameters:
- $ref: '#/components/parameters/Id'
responses:
'204':
description: Deleted
'401':
$ref: '#/components/responses/Error'
'404':
$ref: '#/components/responses/Error'
servers:
- url: https://api.customdomain.ai/v1
description: Hosted control-plane
- url: http://localhost:8080/v1
description: Local control-plane
/webhook-deliveries:
get:
operationId: listWebhookDeliveries
summary: Recent webhook delivery attempts for the caller's tenant
tags:
- Webhooks
description: 'API key: all the tenant''s apps. Widget JWT: the calling app. `?app_id=` narrows to one application; `?type=` filters by event type.'
security:
- apiKey: []
- widgetJWT: []
parameters:
- name: app_id
in: query
required: false
schema:
type: string
- name: type
in: query
required: false
schema:
type: string
responses:
'200':
description: Deliveries
content:
application/json:
schema:
type: object
required:
- deliveries
- count
properties:
deliveries:
type: array
items:
$ref: '#/components/schemas/WebhookDelivery'
count:
type: integer
'401':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
servers:
- url: https://api.customdomain.ai/v1
description: Hosted control-plane
- url: http://localhost:8080/v1
description: Local control-plane
components:
parameters:
Id:
name: id
in: path
required: true
schema:
type: string
schemas:
WebhookEndpoint:
type: object
required:
- id
- url
properties:
id:
type: string
application_id:
type: string
url:
type: string
format: uri
events:
type: array
items:
type: string
secret:
type:
- string
- 'null'
description: 'Present ONLY in the creation response (whsec_…). Deliveries are signed with it: each POST carries X-JE-Timestamp (unix seconds) and X-JE-Signature (sha256=hex of HMAC-SHA256 over "<timestamp>.<rawBody>"). Reject deliveries whose timestamp is outside a ~5-minute window.'
created_at:
type: string
format: date-time
WebhookDelivery:
type: object
required:
- id
- event_type
properties:
id:
type: string
endpoint_id:
type: string
application_id:
type: string
url:
type: string
format: uri
event_id:
type: string
event_type:
type: string
domain:
type: string
attempts:
type: integer
status_code:
type: integer
description: HTTP status of the final attempt (0 = no response)
delivered:
type: boolean
error:
type: string
created_at:
type: string
format: date-time
Error:
type: object
required:
- code
- title
properties:
code:
type: string
description: stable machine code, e.g. invalid_request, unauthorized, not_found, service_unavailable, internal_error
title:
type: string
description: human-readable summary
details:
type: string
description: optional extra context
responses:
Error:
description: 'Error envelope: {code, title, details}.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
securitySchemes:
managementKey:
type: http
scheme: bearer
description: Operator management key. Gates provisioning + reads across tenants.
provisionSecret:
type: apiKey
in: header
name: X-Provision-Secret
description: Narrow secret held by the customer console's server, for signup provisioning and its other server to server calls (also accepted as a Bearer token, except on POST /audit, where a Bearer is always a tenant credential).
apiKey:
type: http
scheme: bearer
description: Long-lived tenant API key, cd_live_… (production) or cd_test_… (staging, development), stored hashed. Keys created before the switch to cd_ start with sk_live_… / sk_test_… and are still accepted. Scoped to the owning application's tenant.
widgetJWT:
type: http
scheme: bearer
bearerFormat: JWT
description: Short-lived widget JWT minted server-side via POST /v1/tokens. Scoped to one application (and optionally one domain).
browserSession:
type: http
scheme: bearer
description: The session token of one cloud browser sign in, returned by `POST /browser-sessions:redeem`. It binds one session, one connection and one workspace, lives at most as long as the session, and stops working the moment the session ends. It lives in the popup's memory only.
agentToken:
type: oauth2
description: 'A delegated agent access token: an ES256 JWT that lives 15 minutes, sent as a bearer token. A person approves an agent in the console and the agent exchanges its authorization code (with PKCE) at the token endpoint. The token carries the workspace, the one application the grant is bound to, the grant, the approved scopes and its audience, and is verified against `/.well-known/jwks.json`. A route that accepts it lists the scope it needs; a token without the scope is refused with `403`, and a connection or order of another application answers `404`.'
flows:
authorizationCode:
authorizationUrl: https://api.customdomain.ai/oauth/agent/authorize
tokenUrl: https://api.customdomain.ai/oauth/agent/token
refreshUrl: https://api.customdomain.ai/oauth/agent/token
scopes:
domains:read: Look at availability, connections, records, certificates, diagnoses, orders and the billing standing.
domains:connect: Connect a domain, recheck it, reapply its records and start a one click setup.
domains:disconnect: Remove a connected domain. The call must name the domain.
domains:purchase: Buy a domain, up to the grant's monthly spend cap.
x-refined-from:
- customdomain-ai-openapi-original.json
- customdomain-ai-openapi.yml