> ## Documentation Index
> Fetch the complete documentation index at: https://docs.drin.run/llms.txt
> Use this file to discover all available pages before exploring further.

# Verify a domain

> Add a domain, publish its DKIM, SPF, and DMARC records, and send from your own brand — fully white-labeled. Or send from the shared onboarding domain with zero DNS in test mode.

To send from your own brand, prove you control the domain by publishing three DNS records: DKIM signs your mail, a custom MAIL FROM (with SPF) aligns the envelope, and DMARC sets the policy. Drin generates the exact values — you paste them into your DNS, and we confirm them automatically.

You don't need a verified domain to start. New accounts can send immediately from a **shared onboarding domain** in test mode. Verifying your own domain is what lets you email anyone, from your own brand, fully white-labeled.

<CardGroup cols={3}>
  <Card title="DKIM" icon="key">
    A public key in your DNS that lets receivers verify each message was signed by you and not altered in transit. Required to send.
  </Card>

  <Card title="SPF" icon="shield">
    Published on a custom `MAIL FROM` subdomain so the envelope sender aligns with your domain — and no third-party sending service shows in your headers.
  </Card>

  <Card title="DMARC" icon="lock">
    A policy record telling receivers what to do with mail that fails DKIM or SPF. Drin seeds a safe `p=none` to start.
  </Card>
</CardGroup>

## Add the domain

Add a domain in the dashboard on the [Domains](https://app.drin.run/domains) page, or over the API. The response is the domain plus the exact DNS records to publish — every record references only your own domain, never the underlying mail provider.

**`POST /v1/domains`**

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.drin.run/v1/domains \
    -H "Authorization: Bearer $DRIN_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "domain": "mail.acme.com" }'
  ```

  ```typescript Node.js theme={null}
  import { DrinClient } from "@drin00/sdk";

  const drin = new DrinClient({ apiKey: process.env.DRIN_API_KEY });

  // Returns the domain plus the DNS records to publish.
  const domain = await drin.domains.create({ domain: "mail.acme.com" });

  for (const r of domain.records) {
    console.log(r.type, r.name, "→", r.value, r.priority ?? "");
  }
  ```
</CodeGroup>

The new domain comes back with `status: "pending"` and a `records` array. Each record carries a `purpose` (`dkim`, `mail_from`, or `dmarc`) and a `verified` flag that starts `false`:

```json 201 Created theme={null}
{
  "id": "dom_8a1c…",
  "domain": "mail.acme.com",
  "status": "pending",
  "records": [
    {
      "type": "TXT",
      "name": "drin1._domainkey.mail.acme.com",
      "value": "v=DKIM1; k=rsa; p=MIGfMA0GCS…",
      "purpose": "dkim",
      "verified": false
    },
    {
      "type": "MX",
      "name": "send.mail.acme.com",
      "value": "feedback-smtp.us-east-1.example",
      "priority": 10,
      "purpose": "mail_from",
      "verified": false
    },
    {
      "type": "TXT",
      "name": "send.mail.acme.com",
      "value": "v=spf1 include:spf.example ~all",
      "purpose": "mail_from",
      "verified": false
    },
    {
      "type": "TXT",
      "name": "_dmarc.mail.acme.com",
      "value": "v=DMARC1; p=none;",
      "purpose": "dmarc",
      "verified": false
    }
  ]
}
```

<Tip>
  **Use a subdomain.** We recommend a dedicated sending subdomain like `mail.acme.com` or `send.acme.com` rather than your root `acme.com`. It isolates your transactional reputation from the rest of your domain and keeps your DNS tidy.
</Tip>

## Publish the DNS records

Copy each record from the response (or the dashboard) into your DNS provider exactly as given — the `type`, `name` (host), `value`, and, for the MX record, the `priority`. The values are unique to your domain, so always take them from your own Add-domain response, never from this page.

* **DKIM** — a `TXT` (or CNAME) record at a `._domainkey` host. This is what actually authorizes sending.
* **MAIL FROM** — an `MX` and a `TXT` (SPF) record on a `send.` subdomain. These align the return-path so receivers — and Gmail's "mailed-by" — see your domain, not ours.
* **DMARC** — a `TXT` record at `_dmarc.` Drin seeds `p=none` so you can monitor before you enforce.

<Note>
  **Where each value goes.** The host/name fields differ slightly between DNS providers — some want the full name, some want it relative to your zone. See the [DNS provider guides](/domains/dns) for Cloudflare, Namecheap, GoDaddy, Route 53, and Vercel.
</Note>

## Verify

DNS changes can take anywhere from a minute to a few hours to propagate. Drin checks for your records automatically on a background schedule. To check immediately after pasting them, hit **Verify now** in the dashboard, or call the verify endpoint — it re-checks your records right now and returns the fresh status.

**`POST /v1/domains/{id}/verify`**

```bash cURL theme={null}
curl -X POST https://api.drin.run/v1/domains/dom_8a1c…/verify \
  -H "Authorization: Bearer $DRIN_API_KEY"
```

<Steps>
  <Step title="pending">
    Records are saved but not yet confirmed in DNS. We keep re-checking in the background; you can keep refreshing.
  </Step>

  <Step title="verified">
    DKIM is confirmed and the domain can send. MAIL FROM and DMARC often confirm a beat later than DKIM — that's normal, and each record's `verified` flag reflects the truth independently.
  </Step>

  <Step title="failed">
    We couldn't find the records after repeated checks. Re-check the host and value against your Add-domain response — a trailing dot or a wrapped value is the usual culprit — then verify again.
  </Step>
</Steps>

## Send from it

Once `status` is `verified`, use any address at that domain as your `from`. The cleanest pattern is to look up your verified domains in code so you never hardcode an address that might not be ready yet:

```typescript Node.js theme={null}
// Pick a verified domain to send from, in code.
const [d] = await drin.domains.listVerified();

await drin.emails.send({
  from: { email: `hello@${d.domain}` },
  to: [{ email: "customer@example.com" }],
  subject: "Welcome to Acme",
  html: "<p>Your account is ready.</p>",
});
```

## Test mode vs. your own domain

The shared onboarding domain exists so you can integrate and ship a working send *before* touching DNS. It runs in test mode and can only deliver to your own account's address — perfect for development, useless for production. Verifying your own domain lifts that restriction and white-labels every message.

|                       | Shared onboarding domain | Your verified domain |
| --------------------- | ------------------------ | -------------------- |
| DNS setup             | None                     | DKIM + SPF + DMARC   |
| Recipients            | Your own address only    | Anyone               |
| `from` address        | Shared, not your brand   | Your brand           |
| Branding in the inbox | Generic                  | Fully white-labeled  |
| Good for              | Trying it out, local dev | Production           |

<Warning>
  **Don't ship on test mode.** A send from the shared domain to a stranger is rejected with a `permission_error` (sandbox restriction). If a teammate sees "recipient not allowed" in staging, it means you haven't switched the `from` to a verified domain yet.
</Warning>

## White-label

Drin uses BYODKIM, so the key lives in *your* DNS and every customer-facing record points at your domain. Combined with the custom MAIL FROM, recipients see your domain in the `mailed-by` and `signed-by` lines — there is no mention of the sending infrastructure anywhere a recipient looks. Your customers' mail looks like it came straight from you, because as far as the receiving mail server is concerned, it did.

<CardGroup cols={2}>
  <Card title="DNS provider guides" icon="globe" href="/domains/dns">
    Step-by-step on where to paste DKIM, SPF, and DMARC for Cloudflare, Namecheap, GoDaddy, Route 53, and Vercel.
  </Card>

  <Card title="Deliverability" icon="chart-line" href="/deliverability">
    Alignment, warm-up, and staying under bounce and complaint limits so your reputation stays clean.
  </Card>

  <Card title="Receive on this domain" icon="inbox" href="/receiving">
    Once verified, flip on receiving to give the domain an inbox — add one MX record and read inbound threads.
  </Card>

  <Card title="Domains API" icon="code" href="/api-reference/domains/create">
    Full reference for adding, listing, verifying, and deleting domains.
  </Card>
</CardGroup>
