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:
| Header | Description |
|---|---|
X-Rate-Limit-Limit | Max requests per day |
X-Rate-Limit-Remaining | Requests remaining today |
X-Rate-Limit-Reset | Seconds until limit resets |
POST /v1/domains
Provision a new custom domain.
Request
| Field | Type | Required | Description |
|---|---|---|---|
domain | string | yes | Custom domain to provision (e.g. "mysite.com") |
origin_url | string | yes | Origin URL to proxy to |
end_user_email | string | yes | End user's email for billing |
idempotency_key | string | no | Prevent 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
| Param | Default | Description |
|---|---|---|
status | all | Filter by status enum |
page | 1 | Page number |
limit | 50 | Results per page (max 100) |
Response (200)
{
"domains": [ ... ],
"total": 42,
"page": 1,
"per_page": 50,
"has_more": false
}
Domain Status Enum
| Status | Meaning |
|---|---|
pending_payment | Waiting for end user to pay via claim page |
pending_dns | Paid, waiting for DNS records to propagate |
pending_ssl | DNS verified, SSL certificate provisioning |
active | Domain is live with SSL |
failed | Provisioning failed or expired |
released | Domain was released/deleted |