Domains: Add, DNS Setup & Verification

Set up sending domains with proper DNS authentication

A domain is your verified sending identity. Before you send live emails, you must add a domain and publish the DNS records Tratto returns for it.

Why Domains Matter

  • Sender Authentication: Prove you control the domain
  • Deliverability: SPF, DKIM, DMARC prevent spoofing and improve inbox placement
  • Trust: Recipients see your branded domain, not a third-party service

Add a Domain

Register your domain with Tratto.

cURL

curl -X POST https://api.tratto.email/v1/domains \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{"domain":"hello.yourdomain.com"}'

Node.js

const response = await fetch('https://api.tratto.email/v1/domains', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer tratto_live_...',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ domain: 'hello.yourdomain.com' }),
});
const { data } = await response.json();
console.log('Domain ID:', data.id);

Python

import requests

response = requests.post(
  'https://api.tratto.email/v1/domains',
  headers={
    'Authorization': 'Bearer tratto_live_...',
    'Content-Type': 'application/json',
  },
  json={'domain': 'hello.yourdomain.com'},
)
data = response.json()['data']
print(f"Domain ID: {data['id']}")

Response (201; values are examples, always use the ones from your response):

{
  "data": {
    "id": "dom_abc123",
    "domain": "hello.yourdomain.com",
    "status": "pending",
    "records": [
      { "type": "CNAME", "host": "token1._domainkey.hello.yourdomain.com", "value": "token1.dkim.amazonses.com", "verified": false, "purpose": "dkim" },
      { "type": "CNAME", "host": "token2._domainkey.hello.yourdomain.com", "value": "token2.dkim.amazonses.com", "verified": false, "purpose": "dkim" },
      { "type": "CNAME", "host": "token3._domainkey.hello.yourdomain.com", "value": "token3.dkim.amazonses.com", "verified": false, "purpose": "dkim" },
      { "type": "TXT", "host": "hello.yourdomain.com", "value": "v=spf1 include:amazonses.com ~all", "verified": false, "purpose": "spf" },
      { "type": "TXT", "host": "_dmarc.hello.yourdomain.com", "value": "v=DMARC1; p=quarantine; rua=mailto:[email protected]", "verified": false, "purpose": "dmarc" }
    ],
    "createdAt": "2026-06-30T12:00:00.000Z",
    "updatedAt": "2026-06-30T12:00:00.000Z",
    "verifiedAt": null
  }
}

Domain status is pending: Tratto is waiting for Amazon SES, which delivers your email, to find the DKIM records.

DNS Records Setup

Add the records from records at your DNS provider (GoDaddy, Namecheap, Cloudflare, Route 53, etc.). Every domain gets five:

PurposeTypeHostValueNeeded to send
DKIMCNAME<token1>._domainkey.hello.yourdomain.com<token1>.dkim.amazonses.comYes
DKIMCNAME<token2>._domainkey.hello.yourdomain.com<token2>.dkim.amazonses.comYes
DKIMCNAME<token3>._domainkey.hello.yourdomain.com<token3>.dkim.amazonses.comYes
SPFTXThello.yourdomain.comv=spf1 include:amazonses.com ~allRecommended; if the host already has an SPF record, add include:amazonses.com to it
DMARCTXT_dmarc.hello.yourdomain.comv=DMARC1; p=quarantine; rua=mailto:[email protected]Recommended

1. DKIM Records (required)

Type: CNAME, three records
Host: <token>._domainkey.hello.yourdomain.com
Value: <token>.dkim.amazonses.com

The three tokens are unique to your domain: copy each host and value from your response. Amazon SES generates and manages the signing key, and the CNAME records point to its public keys. These are the only records Tratto checks: all three must resolve before the domain becomes verified.

host is the full name. If your DNS provider appends your domain automatically, enter only the part before it (for example <token1>._domainkey.hello in the yourdomain.com zone), or the record ends up as <token1>._domainkey.hello.yourdomain.com.yourdomain.com.

Type: TXT
Host: hello.yourdomain.com
Value: v=spf1 include:amazonses.com ~all

This authorizes Amazon SES to send for the hostname.

A hostname must have exactly one v=spf1 record (RFC 7208, section 3.2). With two, the SPF check fails with a permanent error (permerror) for every sender on that hostname, not only Amazon SES. If the host already has an SPF record, for example from Cloudflare Email Routing, Google Workspace or Microsoft 365, add include:amazonses.com to it instead of creating a second one:

v=spf1 include:amazonses.com include:_spf.mx.cloudflare.net ~all

Type: TXT
Host: _dmarc.hello.yourdomain.com
Value: v=DMARC1; p=quarantine; rua=mailto:[email protected]

This asks receiving servers to quarantine mail that fails authentication. The policy covers all mail sent as this domain, not only mail sent through Tratto, so make sure your other senders authenticate before you publish it. Keep a single DMARC record per host. Tratto doesn't check this record, so you can adapt the policy and the rua address to your domain.

Verification

Tratto verifies DKIM only, by asking Amazon SES whether it has found the three CNAME records. SPF and DMARC are never checked: their verified flag is always false, even on a verified domain. The three DKIM records share one flag, which turns true when the domain is verified.

Checks happen in two ways:

  • Automatically: a background job re-checks every pending domain every 10 minutes
  • On demand: POST /v1/domains/{id}/verify checks right away

Amazon SES notes that DNS changes can take up to 72 hours to propagate, and it keeps looking for the DKIM records for up to 72 hours.

Verify Now

cURL

curl -X POST https://api.tratto.email/v1/domains/dom_abc123/verify \
  -H "Authorization: Bearer tratto_live_..."

Node.js

const response = await fetch('https://api.tratto.email/v1/domains/dom_abc123/verify', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer tratto_live_...' },
});
const { data } = await response.json();
console.log('Status:', data.status);

Python

import requests

response = requests.post(
  'https://api.tratto.email/v1/domains/dom_abc123/verify',
  headers={'Authorization': 'Bearer tratto_live_...'},
)
data = response.json()['data']
print(f"Status: {data['status']}")

The response is the full domain, records included, with 200 whatever the outcome: read status, not the HTTP code.

StatusMeaning
pendingAmazon SES hasn't found all three DKIM records yet, or a temporary SES issue prevented the check. Wait, or check again later.
verifiedThe DKIM records were found. Live emails can be sent from this domain.
failedAmazon SES stopped looking, typically because it couldn't find the DKIM records. The automatic check skips failed domains: see DNS Troubleshooting.

What verified Unlocks

  • Live sends: an email sent with a live key (tratto_live_...) is accepted only if its from domain is verified in your workspace. Otherwise the API returns 403 with FORBIDDEN.
  • Exact match: the check compares the full domain after @. Verifying yourdomain.com does not cover hello.yourdomain.com: add and verify each domain you send from.
  • Test sends: test keys (tratto_test_...) skip this check. See Test mode.
  • Default sender: when a domain first becomes verified and your workspace has no default sender, Tratto sets it to noreply@ on that domain.

Check Domain Status

Get a domain with its DNS records.

cURL

curl https://api.tratto.email/v1/domains/dom_abc123 \
  -H "Authorization: Bearer tratto_live_..."

Response:

{
  "data": {
    "id": "dom_abc123",
    "domain": "hello.yourdomain.com",
    "status": "verified",
    "records": [ ... ],
    "createdAt": "2026-06-30T12:00:00.000Z",
    "updatedAt": "2026-06-30T12:10:00.000Z",
    "verifiedAt": "2026-06-30T12:10:00.000Z"
  }
}

List Domains

Get all domains for your tenant. Each item has id, domain, status and timestamps, but no records: fetch a single domain to see them. Paginate with limit (1 to 100, default 50) and after.

cURL

curl https://api.tratto.email/v1/domains \
  -H "Authorization: Bearer tratto_live_..."

Remove a Domain

Delete a domain. Its identity in Amazon SES is deleted too.

cURL

curl -X DELETE https://api.tratto.email/v1/domains/dom_abc123 \
  -H "Authorization: Bearer tratto_live_..."

Removing a domain cannot be undone. Live emails from that domain are rejected until you add it again and verify it with the records from the new response.

DNS Troubleshooting

Still pending after adding records

Cause: DNS hasn't propagated yet, or a DKIM record doesn't match.

Fix:

  1. Compare each DKIM host and value with records from GET /v1/domains/{id}
  2. Check that your provider didn't append your domain to the host a second time
  3. Use MXToolbox or dig CNAME <token1>._domainkey.hello.yourdomain.com to check that the records are live
  4. Wait for the next automatic check (every 10 minutes), or call POST /v1/domains/{id}/verify

SPF and DMARC don't affect the status: a missing SPF or DMARC record never keeps a domain pending.

Domain is failed

Cause: Amazon SES stopped looking, typically because it couldn't find the DKIM records.

Fix: correct the DKIM records, then call POST /v1/domains/{id}/verify, which reads the current status from Amazon SES. If the domain stays failed, remove it, add it again and publish the records from the new response.

SPF vs DKIM vs DMARC

RecordPurposeChecked by Tratto
DKIMSigns every email with a key Amazon SES manages for your domainYes, required to send
SPFAuthorizes Amazon SES to send for the hostnameNo, recommended
DMARCInstructs mail servers how to handle failed SPF/DKIMNo, recommended

Best practice: Publish all five records for maximum deliverability.

Subdomain Sending

You can use subdomains for sending (e.g., hello.yourdomain.com) while your main domain stays clean. Tratto matches the from domain exactly, so add and verify the subdomain itself.

Advantages:

  • Isolate email reputation from main domain
  • Test new domains without affecting production
  • Separate sending and receiving infrastructure

Next Steps


Edit this page on GitHub

Last updated on