This guide focuses on DNS mechanics and troubleshooting. For the full request/response bodies of every domain endpoint, see Custom email domains in the White Labeling guide.
Why set up a custom domain
Sender reputation. Email sent from Firma’s shared sending domain carries Firma’s reputation, not yours. A verified custom domain sends under your own SPF/DKIM/DMARC records, so your sending history and reputation build up independently and aren’t affected by other Firma customers. Branding. Recipients see signing invitations fromnoreply@sign.yourcompany.com instead of a firma.dev address, reinforcing that the request came from you.
Custom domains can be set at the company level (default for all workspaces) or per workspace (for multi-tenant apps that need a distinct domain per customer). See Account-level vs. workspace-level domains for the resolution order.
DNS records you’ll need
Setting up a domain requires three DNS records once it’s finalized, plus one TXT record earlier to prove ownership:1
Add the domain
verification_token and a _firma-verification.<domain> TXT record to add. The record’s value is the raw token — no prefix or formatting.2
Verify ownership
Once the TXT record is live, call
verify-ownership. Firma looks up the record over DNS and compares it to the stored token.3
Finalize to get sending records
4
Add the DNS records and verify
Add all three records, then trigger a check:DNS propagation typically takes minutes but can take up to 48 hours. It’s normal to call
verify-dns more than once while records propagate.Already using Resend for your own email?
The single most common setup failure: you already have this domain registered in your own Resend account for your own transactional email (password resets, notifications, etc.). Firma sends through its own Resend account under the hood. Resend does not allow the same domain to be registered under two different accounts at once. When Firma’s background auto-finalize job detects a conflict, it setsstatus_message: "resend_conflict" on the domain row. This surfaces in the dashboard as a Domain Conflict badge on the domain.
This is also the right pattern even if you’re not using Resend directly yourself — it isolates Firma’s DNS records from whatever your main domain is already doing for email, and it’s what most of our customers do regardless of conflicts.
Other provider conflict patterns
Beyond the direct Resend conflict above, two more general DNS constraints trip people up when a domain already sends mail through another provider (Google Workspace, Microsoft 365, SendGrid, Mailgun, Postmark, etc.): SPF: only one record is allowed per domain. Ifacme.com already has an SPF TXT record for another provider (e.g. v=spf1 include:_spf.google.com ~all), do not add a second TXT record at @ for Firma’s include:amazonses.com. Two SPF records at the same name causes a permanent SPF failure (permerror) for every sender on the domain, not just Firma. Merge the mechanism into your existing record instead:
_dmarc.acme.com already exists with a policy like p=quarantine or p=reject, don’t add a second _dmarc record with Firma’s default p=none. Multiple _dmarc TXT records make DMARC processing undefined for mail servers that check it. Keep your existing, stricter policy — it still applies to Firma’s mail as long as SPF and DKIM pass.
DKIM selectors from other providers generally don’t collide. Each provider uses its own CNAME selector name (Google uses google._domainkey, Microsoft uses selector1._domainkey/selector2._domainkey, SendGrid uses its own custom selector, and so on), so DKIM itself is rarely the issue outside the Resend-specific conflict above. If you do hit an unexpected DKIM collision with a provider other than Resend, using a subdomain resolves it the same way.
Verification states
A domain moves through two independent status fields as it progresses. The dashboard badge reflects both:Stuck in “Configuring”
If a domain sits atverification_status = 1 for more than a few minutes without progressing, Firma’s background job automatically retries the finalize call. If it’s still stuck after that, the underlying cause is almost always the Resend conflict above — check the domain’s status_message for resend_conflict via GET /company/domains before contacting support.
Verified domain later shows “Failed”
Unlikeverification_status, domain_status can move backward from 1 (Verified) to 2 (Failed). Firma periodically re-checks DNS in the background, and if a previously-verified record is later removed or changed — for example, you migrate DNS providers and the CNAME doesn’t carry over — the domain flips to Failed. Re-add the missing record(s) and call verify-dns again to restore it.
Known display quirk: “pending” after a domain is already verified
Becauseverify-dns re-checks live against the email provider on every call, calling it again on a domain that’s already fully verified can occasionally report a transient hiccup even though nothing is actually wrong:
- In the API, the top-level
verifiedfield andmessagestring in averify-dnsresponse reflect that specific live check, not the stored record. If the provider has a momentary blip, you can get"verified": falsewith a “not yet verified” message in the same response body wheredomain.domain_statusstill correctly reads1(Verified). Trustdomain.domain_status, not the top-levelverified/message, when re-checking an already-verified domain. - In the dashboard, the status badge at the top of the domain row is authoritative — it’s read from the stored record. The “Domain Records” detail dialog shows individual record checks (TXT/CNAME/DMARC) that can occasionally lag behind and show an individual record as still pending even while the overall badge already says Verified. If the two disagree, trust the top-level badge.
Related guides
- White Labeling: Custom email domains — full API walkthrough, request/response bodies, and workspace-level domain setup
- Webhooks — subscribe to
domain.verifiedanddomain.verification.failedevents to track ownership verification (Step 2); there is no webhook for the DNS verification step — pollverify-dnsfor that - Email Domains API Reference