REST API Reference

All endpoints are under /v1/. Authenticate with X-Platform-API-Key header.

Authentication

Every request must include your platform API key:

X-Platform-API-Key: th_live_your_key_here

Error Format

All errors return a structured JSON body:

{
  "error": {
    "code": "domain_taken",
    "message": "The domain mysite.com is already provisioned",
    "next_action": "Choose a different domain or check if you own this domain",
    "retryable": false
  }
}

Rate Limits

Rate limits are per-platform and vary by plan tier. Headers included on every response:

HeaderDescription
X-Rate-Limit-LimitMax requests per day
X-Rate-Limit-RemainingRequests remaining today
X-Rate-Limit-ResetSeconds until limit resets

POST /v1/domains

Provision a new custom domain.

Request

FieldTypeRequiredDescription
domainstringyesCustom domain to provision (e.g. "mysite.com")
origin_urlstringyesOrigin URL to proxy to
end_user_emailstringyesEnd user's email for billing
idempotency_keystringnoPrevent duplicate provisions on retry

Response (201)

{
  "id": "uuid",
  "domain": "mysite.com",
  "status": "pending_payment",
  "dns_instructions": {
    "type": "CNAME",
    "name": "mysite.com",
    "value": "cname.thin.host",
    "human_readable": "Set a CNAME record for mysite.com pointing to cname.thin.host"
  },
  "claim_url": "https://thin.host/claim/abc123...",
  "next_action": "Direct the user to the claim_url to complete payment and DNS setup",
  "expected_completion_seconds": 300
}

GET /v1/domains/:id

Get full domain status.

Response (200)

{
  "id": "uuid",
  "hostname": "mysite.com",
  "origin_url": "https://myapp.vercel.app",
  "status": "active",
  "dns_status": "resolved",
  "ssl_status": "active",
  "payment_status": "paid",
  "ready": true,
  "created_at": "2026-04-25T00:00:00Z",
  "updated_at": "2026-04-25T01:00:00Z"
}

PATCH /v1/domains/:id

Update the origin URL for an existing domain.

Request

{ "origin_url": "https://new-deploy.vercel.app" }

Response (200)

{
  "id": "uuid",
  "domain": "mysite.com",
  "origin_url": "https://new-deploy.vercel.app",
  "status": "active",
  "updated_at": "2026-04-25T02:00:00Z"
}

DELETE /v1/domains/:id

Release a domain. Removes proxy config and marks as released. Not reversible.

Response (200)

{
  "id": "uuid",
  "domain": "mysite.com",
  "status": "released",
  "released_at": "2026-04-25T03:00:00Z"
}

GET /v1/domains

List domains with pagination and filtering.

Query Parameters

ParamDefaultDescription
statusallFilter by status enum
page1Page number
limit50Results per page (max 100)

Response (200)

{
  "domains": [ ... ],
  "total": 42,
  "page": 1,
  "per_page": 50,
  "has_more": false
}

Domain Status Enum

StatusMeaning
pending_paymentWaiting for end user to pay via claim page
pending_dnsPaid, waiting for DNS records to propagate
pending_sslDNS verified, SSL certificate provisioning
activeDomain is live with SSL
failedProvisioning failed or expired
releasedDomain was released/deleted