Quickstart
Connect your first custom domain end to end in about five minutes.
Connect your first custom domain end to end in about five minutes. This uses the API directly; to embed the UI instead, see The widget SDK.
0. Run the stack (local)
cd app
make dev # boots Postgres + control-plane + edge + demo, prints URLsThe control-plane listens on http://localhost:8080. A demo application and API
key (sk_test_demo) are seeded. For real deployments see
Self-hosting.
1. Create an application
An application is your product surface. It carries the record template new connections inherit and the allowed embed origins.
curl -X POST http://localhost:8080/v1/applications \
-H "Authorization: Bearer sk_test_demo" \
-H "Content-Type: application/json" \
-d '{"name":"Acme Sites","allowed_origins":["https://app.acme.example"]}'Creating applications and keys requires a key with the apps:admin scope — see
Authentication.
2. Create an API key
curl -X POST http://localhost:8080/v1/applications/<APP_ID>/keys \
-H "Authorization: Bearer sk_test_demo" \
-H "Content-Type: application/json" \
-d '{"name":"server-prod","scopes":["connections:read","connections:write"]}'The secret is returned once — store it now; it's kept only as a hash. A key
can never grant scopes the calling key doesn't itself hold.
3. Start a connection
curl -X POST http://localhost:8080/v1/connections \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json" \
-d '{"application_id":"<APP_ID>","hostname":"app.customer.com"}'The response includes the connection id (also the jobId), the desired DNS
records, and an ownership_challenge — a TXT record the customer must publish
to prove control:
{
"id": "…",
"state": "pending_ownership",
"ownership_challenge": { "name": "_customdomain-challenge.app.customer.com", "type": "TXT", "value": "cd-verify-…" }
}If the application has a stored delegated DNS credential, the
records (and no manual step) are written automatically and the connection jumps
straight to propagating — you can skip to step 6.
4. Publish DNS
The customer adds, at their DNS provider:
- The ownership TXT from the challenge.
- The desired records (returned on the connection) pointing the hostname at the edge.
5. Verify ownership
curl -X POST http://localhost:8080/v1/connections/<ID>/verify \
-H "Authorization: Bearer <YOUR_KEY>"On success the connection moves to verified, which opens the edge ask gate.
(The background worker also does this automatically once the TXT is visible.)
6. Watch propagation → live
curl -X POST http://localhost:8080/v1/connections/<ID>/records:check \
-H "Authorization: Bearer <YOUR_KEY>"When every desired record resolves to its intended value, the connection becomes
live, the certificate is issued on the next TLS handshake, and the edge starts
proxying https://app.customer.com to your origin. You'll receive
ssl.issued and domain.flow.completed webhooks.
Next steps
- Embed the widget so customers do steps 3–6 themselves.
- Register webhooks to react to go-live and drift.
- Store a delegated credential to skip the manual DNS step.