REST API · v1

Small API.
Custom-domain infrastructure.

Model HTTPS destinations, customer domains, embedded sessions and webhook deliveries with explicit server-side resources.

Bearer authenticationScoped write keysIdempotent mutations
First integration

Two writes start a live workflow.

Keep the bearer key on your server. Create the HTTPS destination once, then create each customer domain with its own stable idempotency key.

  1. 01
    Server-side bearer keyUse destinations:write and domains:write.
  2. 02
    Public HTTPS originDeclare port 443 or 8443 as an HTTPS destination.
  3. 03
    Stable business keysReuse each idempotency key only for the same operation.
01
Create once

HTTPS destination

POST /destinations
curl -X POST https://api.subdomain.to/v1/destinations \
  -H "Authorization: Bearer $SUBDOMAIN_TO_API_KEY" \
  -H "Idempotency-Key: main-destination" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://app.yoursaas.com"}'

HTTP/1.1 201 Created
{"data":{"id":"01J...","port":443}}
02
Create per customer

Customer domain

POST /domains
curl -X POST https://api.subdomain.to/v1/domains \
  -H "Authorization: Bearer $SUBDOMAIN_TO_API_KEY" \
  -H "Idempotency-Key: customer-42-domain" \
  -H "Content-Type: application/json" \
  -d '{"hostname":"portal.acme.eu",
       "destination_id":"01J..."}'

HTTP/1.1 201 Created
{"data":{"status":"pending_dns",
 "dns_records":[{"type":"CNAME",
 "name":"portal.acme.eu",
 "value":"in.subdomain.to"}]}}

Predictable under retries and failure.

The contract is intentionally small. These behaviours are enforced by the API rather than left to each integration.

Writes 1–128 chars

Idempotency is required.

Reuse a stable business key on network retry. A replay returns the original response with Idempotent-Replayed: true.

Errors Request ID

Branch on code and status.

Errors contain a stable code, a useful message and the incoming X-Request-Id when present.

Collections 1–100

Bounded list responses.

Choose a limit from 1 to 100. The current API returns one page with explicit has_more and next_cursor metadata.

Resources and scopes

Grant only the writes a service needs.

Reads are organization-scoped by the authenticated principal. Mutations check a dedicated scope before touching destinations, domains, webhooks or widget sessions.

Destinations
destinations:write
Domains
domains:write
Webhooks
webhooks:write
Widget sessions
widget:write
Webhook deliveryHMAC-SHA256
{
  "id": "01J...",
  "event": "hostname.created",
  "created_at": "2026-07-15T10:30:00+00:00",
  "data": {
    "hostname": "portal.acme.eu",
    "status": "pending_dns"
  }
}

SubdomainTo-Signature: t=...,v1=...
Webhooks you can verify

Raw body in. Trusted event out.

Deliveries target HTTPS endpoints, disable redirects and sign timestamp + "." + raw_body. Failed responses are retried with backoff and remain visible with attempt count, response code and next attempt.

The creation response returns the endpoint secret once. Store it immediately and use the SDK verifier or a constant-time HMAC comparison.

Contract details

No hidden client behaviour.

Does the API retry my writes?

No. Official clients do not perform implicit retries. Your service decides when to retry and preserves the idempotency key.

Which webhook events exist today?

hostname.created and hostname.deleting, or * to subscribe to all supported events.

Can I replay a failed webhook?

Yes. POST /webhook-deliveries/{id}/retry resets the delivery and schedules a new attempt.

Can I expose the API key in the browser?

No. Use your server or create a widget session with a short-lived token and optional browser-origin allowlist.

Choose your next layer

Call the contract directly or use a typed client.

The API guide documents every resource. The SDK guides preserve the same explicit idempotency, error and webhook behaviour.