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:
| Purpose | Type | Host | Value | Needed to send |
|---|---|---|---|---|
| DKIM | CNAME | <token1>._domainkey.hello.yourdomain.com | <token1>.dkim.amazonses.com | Yes |
| DKIM | CNAME | <token2>._domainkey.hello.yourdomain.com | <token2>.dkim.amazonses.com | Yes |
| DKIM | CNAME | <token3>._domainkey.hello.yourdomain.com | <token3>.dkim.amazonses.com | Yes |
| SPF | TXT | hello.yourdomain.com | v=spf1 include:amazonses.com ~all | Recommended; if the host already has an SPF record, add include:amazonses.com to it |
| DMARC | TXT | _dmarc.hello.yourdomain.com | v=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.
2. SPF Record (recommended)
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
3. DMARC Record (recommended)
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
pendingdomain every 10 minutes - On demand:
POST /v1/domains/{id}/verifychecks 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.
| Status | Meaning |
|---|---|
pending | Amazon SES hasn't found all three DKIM records yet, or a temporary SES issue prevented the check. Wait, or check again later. |
verified | The DKIM records were found. Live emails can be sent from this domain. |
failed | Amazon 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 itsfromdomain is verified in your workspace. Otherwise the API returns403withFORBIDDEN. - Exact match: the check compares the full domain after
@. Verifyingyourdomain.comdoes not coverhello.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
verifiedand your workspace has no default sender, Tratto sets it tonoreply@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:
- Compare each DKIM host and value with
recordsfromGET /v1/domains/{id} - Check that your provider didn't append your domain to the host a second time
- Use MXToolbox or
dig CNAME <token1>._domainkey.hello.yourdomain.comto check that the records are live - 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
| Record | Purpose | Checked by Tratto |
|---|---|---|
| DKIM | Signs every email with a key Amazon SES manages for your domain | Yes, required to send |
| SPF | Authorizes Amazon SES to send for the hostname | No, recommended |
| DMARC | Instructs mail servers how to handle failed SPF/DKIM | No, 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
- Send your first email? Go to Send Email
- Test domain setup? Use Quickstart
- Need help with DNS? See Deliverability for best practices
Edit this page on GitHub
Last updated on