# Your own address

> Serve production at a domain of yours, such as app.your-company.com — the DNS records to publish, who may add one, and what changes once it is active.

Production can answer at an address of yours, such as `app.example.com`, instead of the one Apigami
gives it.

## Your own address: a custom hostname

Production can answer at a custom domain of yours, such as `app.example.com`, beside
`{slug}.apigami.tech`. Attach it with the MCP tool `app_hostnames_write` with `action: "add"`: it registers the name and answers
the two DNS records to publish at your DNS provider. `app_hostnames` with the same name observes its current DNS
and certificate without changing anything, with a 30-second cache. It shows the projected `state` beside `stored_state`; without `hostname` it lists the stored names.
Use `app_hostnames_write` with `action: "check"` to apply the next step, including retrying a failed registration.
Over HTTP, `PUT /v1/apps/{app_id}/hostnames/{hostname}` registers one or advances an existing name; `GET` on the same
path observes it without applying changes;
`GET /v1/apps/{app_id}/hostnames` lists them, and `DELETE` removes one.

The two records, both in `dns_records`:

| Type | Name | Value |
|---|---|---|
| `CNAME` | `app.example.com` | `domains.apigami.tech` |
| `TXT` | `_apigami.app.example.com` | `apigami-app={app_id}`, which proves the name is yours: keep it as long as the name serves |

| `state` | Where the name stands |
|---|---|
| `pending_dns` | Registered, waiting for its two records. Apigami checks every 5 minutes for an hour, then hourly; `last_error` names each record it finds missing or pointing elsewhere, and `last_checked_at` when. A name still unverified after 7 days is dropped, on its `expires_at`. |
| `pending_certificate` | Verified. Its certificate is being issued, usually within minutes, then its address goes on line within a few minutes and `GET /healthz` must answer `200` over HTTPS on the version in production; `last_error` says which step it waits for, and who acts. |
| `pending_review` | Verified and certified, on an `unlisted` or `public` application: it serves nothing until an Apigami administrator approves the name, then goes on line at once. |
| `active` | Verified, certified, and answering `/healthz`. Checked again every day. |
| `removing` | Off line; its removal was left unfinished and Apigami finishes it within 5 minutes, or remove it again. |
| `failed` | `last_error` says why. Fix it and call `app_hostnames_write` with `action: "check"`: the name is checked anew from its DNS. Failed unverified names are only dropped at expiry by the background checks; they need this explicit retry. |

Use a subdomain, such as `app.example.com` or `www.example.com`: a bare domain such as `example.com` is
refused, since a CNAME cannot sit at the root of a domain, beside its other records. A name that receives mail (MX)
or holds other records cannot take it either: use another subdomain. If your DNS provider can
proxy traffic, keep both records "DNS only", never proxied. If the domain has CAA records, they must allow all
three of `letsencrypt.org`, `pki.goog` and `ssl.com`: the certificate may come from any of them.

**Who adds one, and how many.** Only an owner of your group adds or removes a domain, whatever the application's
visibility. Any member may apply a check. A `private` application's domain serves as soon as it is ready. An `unlisted` or `public` one's is verified
and certified the same way, then waits in `pending_review` until an Apigami administrator approves the name: they are
told at once, and you have nothing to write. Raising a private application takes its unapproved domains off production
within five minutes, until they are approved. An application has at most 2 domains, a group at most 3 across its
applications, and a group adds at most 10 a day, removed ones included.

**Once active, it is the primary production address.** Production's pages open there, the side panel,
your home and settings pages and your connect link alike, and the first of your domains to become active is
the one they use. `{slug}.apigami.tech` keeps answering, and `--staging` stays on Apigami's domain. A
publication or a rollback takes the domain along to the new production version. When the daily check finds
the records no longer pointing at Apigami, or `/healthz` no longer answering, the pages go back to
`{slug}.apigami.tech` until it passes again.

- **Register its origin for OAuth before it goes active**: `https://app.example.com` as a JavaScript origin,
  and its redirect URIs, such as `https://app.example.com/oauth/callback`, wherever your application signs
  people in ([Sign in to a third-party service from your app](/docs/app-sign-in/#sign-in-to-a-third-party-service-from-your-app-oauth-pkce-callback)).
  From the moment it is active, the pages and their sign-ins open there.
- **Browser storage does not follow.** What your pages kept at `{slug}.apigami.tech`, local storage,
  cookies, a signed-in session, stays at that origin: a person finds none of it on your domain. Apigami's
  session starts again there, without a click for someone connected to your application. Your
  database is the application's and follows.
- **It is a site of its own**: the [risks shared with your neighbours](/docs/app-page-sessions/#who-is-behind-a-request-x-apigami-user-ref)
  under `apigami.tech` do not apply there.
- **Prefer a dedicated subdomain.** Once your domain is verified, no other application, your group's
  included, can act on that name or on any name under it, since your pages hold their token there. The parent,
  `example.com` for `app.example.com`, and the names beside it, such as `www.example.com`, stay reachable.

Removing a domain takes that address off line at once: every page, link and redirect URI registered on it
stops answering. `app_hostnames_write` with `action: "remove"` asks you to confirm with `confirm_removal: true`.