# Add an email domain

> Add a domain to send email from. domain is validated first: a bare domain (a leading https:// and a trailing path are dropped); anything else, spaces included, is 400 with details.errors[0].path ["domain"]. Adding a domain the project already has returns it with 200 (nothing created), like POST /v2/channels.

`POST https://api.zavu.dev/v2/email-domains`

Add a domain to send email from. `domain` is validated first: a bare domain (a leading `https://` and a trailing path are dropped); anything else, spaces included, is `400` with `details.errors[0].path` `["domain"]`. Adding a domain the project already has returns it with `200` (nothing created), like `POST /v2/channels`.

Returns the DNS records to publish; the DKIM CNAMEs are required. If they could not be generated, the domain is still created (`201`) but comes back `verifiable: false` with `setupError`: remove it and add it again, or contact support. Verify cannot fix it.

Publish the records exactly as returned, verify, then connect an address with `POST /v2/channels {"type":"email"}`.

## Body

- `domain` (string, required)

## Responses

### 200

Already a domain of this project; returned as it is, nothing created.

- `domain` (object, required)
  - `id` (string, required)
  - `domain` (string, required)
  - `status` (string, required): Overall verification status.
  - `dkimStatus` (string, required)
  - `dnsRecords` (object[]): DNS records to publish. Present when fetching a single domain or after adding one.
    - `type` (string, required): DNS record type.
    - `name` (string, required): Record host/name to create.
    - `value` (string, required): Record value.
    - `priority` (integer): Priority (MX records only).
    - `purpose` (string, required): What the record is for. One of: `dkim`, `spf`, `dmarc`, `mail_from`.
    - `required` (boolean, required): Whether the record is required to verify + send (DKIM) or recommended for deliverability.
  - `verifiable` (boolean): Present with `dnsRecords`. False when the DKIM records could not be generated: such a domain cannot verify whatever DNS you publish, and `setupError` says what to do.
  - `setupError` (object): Present when `verifiable` is false. `recommendation` is the same everywhere this domain is refused (`POST .../verify`, `POST /v2/channels`): remove the domain and add it again, which sets it up from scratch; contact support if the new one has no `dkim` record either. It never says to retry verify or to publish the other records.
    - `code` (string) One of: `dkim_records_unavailable`.
    - `message` (string)
    - `recommendation` (string)

### 201

Domain added.

- `domain` (object, required)
  - `id` (string, required)
  - `domain` (string, required)
  - `status` (string, required): Overall verification status.
  - `dkimStatus` (string, required)
  - `dnsRecords` (object[]): DNS records to publish. Present when fetching a single domain or after adding one.
    - `type` (string, required): DNS record type.
    - `name` (string, required): Record host/name to create.
    - `value` (string, required): Record value.
    - `priority` (integer): Priority (MX records only).
    - `purpose` (string, required): What the record is for. One of: `dkim`, `spf`, `dmarc`, `mail_from`.
    - `required` (boolean, required): Whether the record is required to verify + send (DKIM) or recommended for deliverability.
  - `verifiable` (boolean): Present with `dnsRecords`. False when the DKIM records could not be generated: such a domain cannot verify whatever DNS you publish, and `setupError` says what to do.
  - `setupError` (object): Present when `verifiable` is false. `recommendation` is the same everywhere this domain is refused (`POST .../verify`, `POST /v2/channels`): remove the domain and add it again, which sets it up from scratch; contact support if the new one has no `dkim` record either. It never says to retry verify or to publish the other records.
    - `code` (string) One of: `dkim_records_unavailable`.
    - `message` (string)
    - `recommendation` (string)

### 400

Missing or invalid `domain` (`details.errors`, `path: ["domain"]`), or the domain could not be set up (the message says what to do).

- `code` (string, required)
- `message` (string, required)
- `details` (object): Machine-readable context. Keys that mean the same thing everywhere: - `errors`: present on every `400 invalid_request` for an invalid body or query parameter, `[{path, message, ...}]`, `path` naming the field (`["to"]`, `["contacts", 2, "recipient"]`, `["limit"]`). - `recommendation`: one sentence saying what to do next. - `dashboardUrl`: the dashboard screen that fixes it. - `connect`: on `409 no_channel`, the channels this project can connect to reach the recipient, each `{type, request, note, available, dashboardUrl?}`; `request` is the exact call (`POST /v2/channels` with its body, or `POST /v2/numbers`), or null for a channel connected from the dashboard (WhatsApp, Instagram, Messenger). `available: false` says why in `note` (one-way SMS closed until business verification is approved). - `address` / `from`: the address the error is about, under the name the request used for it.
  - `errors` (object[])
    - `path` (object[], required)
    - `message` (string, required)
    - `code` (string)

### 401

Unauthorized.

- `code` (string, required)
- `message` (string, required)
- `details` (object): Machine-readable context. Keys that mean the same thing everywhere: - `errors`: present on every `400 invalid_request` for an invalid body or query parameter, `[{path, message, ...}]`, `path` naming the field (`["to"]`, `["contacts", 2, "recipient"]`, `["limit"]`). - `recommendation`: one sentence saying what to do next. - `dashboardUrl`: the dashboard screen that fixes it. - `connect`: on `409 no_channel`, the channels this project can connect to reach the recipient, each `{type, request, note, available, dashboardUrl?}`; `request` is the exact call (`POST /v2/channels` with its body, or `POST /v2/numbers`), or null for a channel connected from the dashboard (WhatsApp, Instagram, Messenger). `available: false` says why in `note` (one-way SMS closed until business verification is approved). - `address` / `from`: the address the error is about, under the name the request used for it.
  - `errors` (object[])
    - `path` (object[], required)
    - `message` (string, required)
    - `code` (string)

## Example

```bash
curl -X POST "https://api.zavu.dev/v2/email-domains" \
  -H "Authorization: Bearer $ZAVUDEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "domain": "example.com"
}'
```

```typescript
import Zavudev from "@zavudev/sdk";

const zavu = new Zavudev({ apiKey: process.env.ZAVUDEV_API_KEY });

const result = await zavu.v2.emailDomains.create({
  domain: "example.com",
});

console.log(result);
```
