# Quickstart

> Let a customer of your SaaS use their own hostname in about fifteen minutes on the Free plan, from sign-up to HTTPS, with the API and the Python SDK.

Source: https://customdomainapi.com/docs/quickstart/

This guide takes one customer hostname from nothing to live over HTTPS, on
the Free plan. You need a backend reachable on the internet over HTTPS (your
**origin**), and a way to run a few lines of code.

## 1. Start Free

Sign in at [app.customdomainapi.com](https://app.customdomainapi.com) with
your email, open **Free** and start. No card is needed. Your application is
set up in a few seconds.

On Free, your customers point their hostnames at:

```
edge.customdomainapi.com
```

and your backend calls the API at:

```
https://edge.customdomainapi.com/v1
```

## 2. Add and verify your origin

On the **Origin** tab, add your backend's hostname, for example
`app.yourproduct.com`. The page shows a token and a URL. Make your origin
answer that URL with exactly the token as a plain-text body:

```
GET https://app.yourproduct.com/.well-known/custom-domain-origin-verification
→ 200, body: <the token>
```

Then press **Verify and use**. Traffic for your customers' hostnames goes to
this origin from now on.

## 3. Get your assertion key

On the same tab, press **Get your assertion key**. You see, once:

- the **key id** (it starts with `app_`),
- the **secret**,
- your **application id**.

Store the secret with your origin's secrets. Every request the edge forwards
carries an `X-Custom-Domain-Assertion` header signed with this key; your
origin verifies it and selects the workspace from it (step 6).

## 4. Create your API key

On **API keys**, create your key. It is shown once. Free includes one key,
which may make 60 requests a minute. Store it in your backend's secrets, for
example as `CUSTOM_DOMAIN_API_KEY`.

## 5. Register a customer's hostname

When a customer enters their hostname in your product, register it for their
workspace:

```bash
curl -X POST https://edge.customdomainapi.com/v1/domains \
  -H "Authorization: Bearer $CUSTOM_DOMAIN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: signup-ws_8f3a1c" \
  -d '{"hostname": "forms.acme.com", "reference": "ws_8f3a1c"}'
```

Or with the Python SDK (`pip install custom-domain-sdk`):

```python
from custom_domain import Client

client = Client("https://edge.customdomainapi.com", credential=CUSTOM_DOMAIN_API_KEY)
domain = client.create_domain(
    "forms.acme.com",            # the customer's exact subdomain
    reference="ws_8f3a1c",       # your workspace id; it comes back on every request
    idempotency_key="signup-ws_8f3a1c",
)
print(domain.render_dns_instructions())
```

The response contains `dns_records`: one **TXT** record that proves the
customer owns the name and one **CNAME** that sends its traffic to the edge.
Show both to your customer exactly as returned (see
[Your customers' DNS](/docs/customer-dns/)).

## 6. Verify requests at your origin

Your origin must select the workspace from the verified assertion, never from
the `Host` header. With the Python SDK in FastAPI or Starlette:

```python
import os
from custom_domain import CustomDomainMiddleware

app.add_middleware(
    CustomDomainMiddleware,
    keys={"app_…": os.environ["CUSTOM_DOMAIN_ASSERTION_SECRET"]},  # your key id and secret
    application_id="…",                                           # your application id
    workspace_lookup=load_workspace,  # returns the workspace for a reference, or None
)

@app.get("/")
def home(request):
    workspace = load_workspace(request.state.custom_domain.reference)
```

The middleware also answers `GET /.well-known/custom-domain-workspace`, which
the service calls through the edge to prove that the hostname reaches the
right workspace before it goes live. Other languages: see
[Verifying requests](/docs/verify-requests/).

## 7. Wait for ready

Once your customer has published the records, the service checks ownership,
routing, the certificate and your origin. When all four pass, the domain's
`status` becomes `ready` and the hostname is served over HTTPS. Poll
`GET /v1/domains/{id}`, or subscribe to the `domain.ready`
[webhook](/docs/webhooks/). If your customer fixes a record, ask for an early
re-check with `POST /v1/domains/{id}/checks`.

That's it. Repeat step 5 for every customer hostname, up to 25 on Free.
