---
title: "Verify a custom domain"
method: POST
path: "/api/domains/{id}/verify"
tags: ["Domains"]
---

# Verify a custom domain

`POST /api/domains/{id}/verify`

## Request

- Base URL: `https://www.flyn.to`
- URL: `https://www.flyn.to/api/domains/{id}/verify`
- Auth: HTTP bearer

Checks the `_flyn` TXT record; when it matches the stored token the domain is attached for serving, then routing and hosting ownership are checked and the domain row is updated. Returns 200 whether or not the domain is ready yet: read `verification`. Safe to call repeatedly, but each call rewrites status from the live checks.

**Auth:** API key (`Bearer flyn_sk_live_...`), or the signed-in dashboard session cookie when no API key header is sent. MCP OAuth access tokens are not accepted here and get 401.

**Plan:** Pro, Lifetime or Team (a member of a paying owner's workspace has the effective plan `team`) only; Free gets 403 `UPGRADE_REQUIRED`.

**Rate limit:** 5 requests per 60 s per client IP, fixed window, in memory per server instance, checked before authentication.

## Response `200`

Verification result.

- DomainVerifyResponse
  - `domain` Domain, required: A custom domain row, returned with its database column names.
    - `id` string, uuid, required
    - `user_id` string, uuid, required: Who added it.
    - `team_id` string, uuid, nullable, required: Workspace it belongs to, or null for a personal domain.
    - `domain` string, required: Lower-case host, for example go.brand.com.
    - `slug` string, required: The domain with dots replaced by hyphens. Unique, so a-b.com and a.b.com collide.
    - `verified` boolean, required
    - `status` 'pending' | 'active' | 'invalid' | 'expired', required
    - `txt_record` string, nullable, required: Ownership token to publish as a TXT record, `flyn-verify=<32 hex>`.
    - `cname_target` string, nullable, required: Routing target: an A record value for an apex domain, otherwise a CNAME target.
    - `last_checked_at` string, date-time, nullable
    - `verified_at` string, date-time, nullable
    - `placeholder` string, nullable: Stored placeholder text. Nothing at runtime reads it yet.
    - `not_found_url` string, nullable: Stored not-found URL. Nothing at runtime reads it yet: an unknown slug always gets the standard dead-link response.
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required
    - `pause_warned_at` string, date-time, nullable
    - `pause_scheduled_at` string, date-time, nullable
    - `paused_at` string, date-time, nullable: Set when the domain is paused; links on a paused domain stop redirecting.
  - `verification` object, required
    - `verified` boolean, required: True only when the TXT record matches, routing is configured and hosting ownership is confirmed.
    - `txtVerified` boolean, required
    - `configured` boolean, required: DNS routing points at Flyn.
    - `ownershipVerified` boolean, required
    - `ownershipChallenge` DnsRecord[], required: TXT record to add at your DNS provider when this domain, or the main domain it belongs to, is connected to another account at Flyn's hosting provider. `name` is the full host name (for example `_vercel.example.com`), not relative like `dnsInstructions`. Add it, then call this endpoint again. Once it verifies, this exact host name is served by Flyn straight away. Empty when no such record is outstanding.
      - `type` 'TXT' | 'CNAME' | 'A', required
      - `name` string, required
      - `value` string, required
    - `misconfigured` boolean, required
    - `dnsInstructions` DnsRecord[], required: Always exactly one routing record.
      - `type` 'TXT' | 'CNAME' | 'A', required
      - `name` string, required
      - `value` string, required
    - `message` string, required: Next step, for example "Domain is verified and active!".

## Other responses

- `401`: No valid API key and no dashboard session. Messages: "Invalid or revoked API key", "API key has expired", "Unauthorized. Sign in to the dashboard, or pass a Bearer API key in the Authorization header.", or "Unauthorized" when the session lookup itself fails. An MCP OAuth token lands here too.
- `403`: Free plan (code `UPGRADE_REQUIRED`) or a suspended account.
- `404`: Not found or not in scope.
- `429`: More than 5 requests in the window. Counted per client IP. Retry-After gives the seconds until the window resets.
- `500`: Database update error.

---

[API](https://skmtc.dev/flyn/apis/flyn-api.md) · [All operations](https://skmtc.dev/flyn/apis/flyn-api/llms.txt) · [OpenAPI document](https://skmtc.dev/flyn/apis/flyn-api/revisions/977962a98ccb?raw)
