Amba

Email

Sending domains (Amba-managed or self-hosted DNS), verification, and the From address rules for project email.

Project email goes out from Amba's shared mailbox until the project adds a sending domain and it verifies. A domain the project bought through Amba is Amba-managed (dns_managed: true): Amba writes its DNS records, starts verification, and keeps checking, so there is no DNS step. Any other domain is self-hosted (dns_managed: false): you create the returned records at your DNS host, then call verify. The email guide walks through both paths. Templates, suppressions, sends, and deliveries are described in the email guide.

Sender choice happens on the server. The client SDKs need no change: passwordless sign-in email (one-time codes and magic links) uses the project's default sender once it is set.

Endpoints

MethodPathDescription
POST/admin/projects/:projectId/email/domainsAdd a sending domain.
GET/admin/projects/:projectId/email/domainsList sending domains.
GET/admin/projects/:projectId/email/domains/:domainRefresh one sending domain.
POST/admin/projects/:projectId/email/domains/:domain/verifyPublish (Amba-managed) and verify.
DELETE/admin/projects/:projectId/email/domains/:domainRemove a sending domain.
POST/admin/projects/:projectId/email/sendSend email; from.email must be on a verified domain.
PATCH/admin/projects/:projectIdSet the default sender (see Projects).

The sending-domain object

Every domain endpoint returns this shape (inside data).

FieldTypeDescription
domainstringThe sending domain.
status"pending" | "verified" | "failed"The project can send from addresses on the domain once it is verified.
provider_statusstring | nullVerification progress as last checked: not_started, pending, partially_verified (some records verified, the rest still being checked; status stays pending), verified, partially_failed, failed, or temporary_failure.
dns_managedbooleantrue: one of the project's own domains bought through Amba (or a subdomain of one); Amba publishes the records and no DNS action is needed. false: you create the records.
dns_status"published" | "pending" | "conflict" | nullAmba-managed only. published: every record in records is in DNS. pending: Amba writes the rest on the next automatic check. conflict: a record Amba did not create blocks a name. null when self-hosted.
dns_errorstring | nullAmba-managed only: why the records are not all published yet.
recordsRecord[]The DNS records the domain needs (below).
recommended_dmarcobject | nullSelf-hosted domains with no DMARC policy: _dmarc.<domain> TXT v=DMARC1; p=none; to create. Always null for Amba-managed domains, where Amba adds it.
default_sender_fallbackbooleantrue while the project default sender is on this domain and the domain is unverified; email then goes from the shared mailbox.
verified_atstring | nullWhen the domain verified.
next_check_atstring | nullWhen Amba next checks verification automatically; null when no check is scheduled.
checks_untilstring | nullEnd of the 72-hour window of automatic checks that starts when Amba publishes the records or verify is called.
next_stepstringWhat happens next and whether any DNS action is needed. While verification is partway (partially_verified), it names each record still unverified.
last_errorstring | nullThe last verification refresh error.
created_at, updated_atstringTimestamps.

Record

FieldTypeDescription
record"DKIM" | "SPF" | "DMARC"Purpose. DMARC appears only for Amba-managed domains.
type"TXT" | "MX" | "CNAME"DNS record type. The return-path (SPF) records can include a CNAME such as rsend.example.com.
namestringFully qualified record name, e.g. send.example.com.
valuestringRecord content. For MX, the mail host (priority holds the priority). For CNAME, the target host.
prioritynumberMX only.
statusstringVerification status of this record as last checked; verified once it verifies.
publish_state"published" | "pending" | "conflict" | "not_needed" | nullAmba-managed only. not_needed: DMARC, the domain already has a policy, a TXT record or a delegated CNAME (kept as it is). null when self-hosted.
created_by_ambabooleantrue when Amba created the record; removing the domain deletes it.
conflictstring | nullThe existing record that blocks this one, when publish_state is conflict.

POST /admin/projects/:projectId/email/domains

Add a sending domain.

  • Amba-managed (one of this project's domains bought through Amba, or a subdomain of one): Amba writes the DKIM TXT record, the send. MX and SPF TXT records, any return-path CNAME (such as rsend.example.com), and _dmarc TXT v=DMARC1; p=none; when the domain has no DMARC policy, then starts verification once every record is in DNS and schedules automatic checks. Identical existing records are left alone, the domain's root MX and SPF records (email forwarding) are never touched, and an existing DMARC policy is kept.
  • Self-hosted (any other domain): Amba registers the domain and returns the records to create. Nothing is written to DNS.

Request

FieldTypeRequiredDescription
domainstringyese.g. example.com or mail.example.com.

Response 201 (Amba-managed)

{
  "data": {
    "domain": "example.com",
    "status": "pending",
    "dns_managed": true,
    "dns_status": "published",
    "dns_error": null,
    "records": [
      {
        "record": "DKIM",
        "type": "TXT",
        "name": "<selector>._domainkey.example.com",
        "value": "p=…",
        "status": "pending",
        "publish_state": "published",
        "created_by_amba": true,
        "conflict": null
      },
      {
        "record": "SPF",
        "type": "MX",
        "name": "send.example.com",
        "value": "…",
        "priority": 10,
        "status": "pending",
        "publish_state": "published",
        "created_by_amba": true,
        "conflict": null
      },
      {
        "record": "SPF",
        "type": "TXT",
        "name": "send.example.com",
        "value": "v=spf1 …",
        "status": "pending",
        "publish_state": "published",
        "created_by_amba": true,
        "conflict": null
      },
      {
        "record": "SPF",
        "type": "CNAME",
        "name": "rsend.example.com",
        "value": "…",
        "status": "pending",
        "publish_state": "published",
        "created_by_amba": true,
        "conflict": null
      },
      {
        "record": "DMARC",
        "type": "TXT",
        "name": "_dmarc.example.com",
        "value": "v=DMARC1; p=none;",
        "publish_state": "published",
        "created_by_amba": true,
        "conflict": null
      }
    ],
    "recommended_dmarc": null,
    "default_sender_fallback": false,
    "verified_at": null,
    "next_check_at": "2026-10-09T12:01:00.000Z",
    "checks_until": "2026-10-12T12:00:00.000Z",
    "next_step": "No DNS action needed. Amba published the DNS records and the domain is waiting for verification. Amba checks verification automatically until 2026-10-12T12:00:00.000Z."
  }
}

Response 201 (self-hosted)

{
  "data": {
    "domain": "example.org",
    "status": "pending",
    "dns_managed": false,
    "dns_status": null,
    "records": [
      {
        "record": "SPF",
        "type": "TXT",
        "name": "send.example.org",
        "value": "v=spf1 …",
        "status": "not_started",
        "publish_state": null,
        "created_by_amba": false,
        "conflict": null
      }
    ],
    "recommended_dmarc": {
      "record": "DMARC",
      "type": "TXT",
      "name": "_dmarc.example.org",
      "value": "v=DMARC1; p=none;"
    },
    "next_check_at": null,
    "next_step": "Create the records listed in records and the recommended_dmarc record at your DNS host, then call verify. …"
  }
}

Errors

  • 400 INVALID_JSON / 400 INVALID_DOMAIN — the body or domain is malformed.
  • 409 DOMAIN_CLAIMED — the domain belongs to another project: it was added there first, or another project bought it through Amba.
  • 409 DNS_RECORD_CONFLICT — Amba-managed: a record Amba did not create holds a name Amba needs (another SPF policy on send., another DKIM key at the selector, a CNAME at a TXT or MX name, or any record at the name of a return-path CNAME other than that same CNAME). The message names each record; Amba leaves it unchanged. The domain is added anyway and returned in error.details.domain. If a site or function domain on this project uses that name, remove that domain; otherwise contact Amba support to have the record removed. Then call verify.
  • 502 EMAIL_PROVIDER_REJECTED / 503 EMAIL_PROVIDER_UNAVAILABLE — the domain could not be registered; retry.

Try it:

POST/admin/projects/%7B%7BprojectId%7D%7D/email/domains
developer auth
curl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/email/domains' \
  -H 'Content-Type: application/json' \
  -d '{
  "domain": "mail.example.com"
}'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X POST "${BASE_URL}/admin/projects/${PROJECT_ID}/email/domains" \
  -H "Authorization: Bearer ${DEV_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"domain":"mail.example.com"}'

GET /admin/projects/:projectId/email/domains

List the project's sending domains, refreshing each one's verification status.

Response 200

{
  "data": [
    {
      "domain": "example.com",
      "status": "verified",
      "dns_managed": true,
      "next_step": "Verified. …"
    }
  ]
}

Try it:

GET/admin/projects/%7B%7BprojectId%7D%7D/email/domains
developer auth
curl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/email/domains'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X GET "${BASE_URL}/admin/projects/${PROJECT_ID}/email/domains" \
  -H "Authorization: Bearer ${DEV_TOKEN}"

GET /admin/projects/:projectId/email/domains/:domain

Refresh one sending domain. Reads only: it never writes DNS.

Response 200

The sending-domain object.

Errors

  • 404 DOMAIN_NOT_FOUND.

Try it:

GET/admin/projects/%7B%7BprojectId%7D%7D/email/domains/%7B%7Bdomain%7D%7D
developer auth
curl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/email/domains/%7B%7Bdomain%7D%7D'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X GET "${BASE_URL}/admin/projects/${PROJECT_ID}/email/domains/{domain}" \
  -H "Authorization: Bearer ${DEV_TOKEN}"

POST /admin/projects/:projectId/email/domains/:domain/verify

Start verification and restart the 72-hour window of automatic checks.

  • Amba-managed: first re-publishes any record that is not in DNS yet (and adopts a domain that was added before its purchase completed), then starts verification once every record is published.
  • Self-hosted: call this after creating the records. Amba keeps checking automatically until the domain verifies or the window ends.

When the window ends unverified, the domain stays pending, next_check_at becomes null, and next_step asks you to call verify again. On the transition to verified, Amba emits the email.domain.verified control event ({ "domain": "example.com", "dns_managed": true }) to subscribed control webhooks.

Response 200

The sending-domain object.

Errors

  • 404 DOMAIN_NOT_FOUND.
  • 409 DNS_RECORD_CONFLICT — as for add.
  • 502 EMAIL_PROVIDER_REJECTED / 503 EMAIL_PROVIDER_UNAVAILABLE — the verification request failed; retry.

Try it:

POST/admin/projects/%7B%7BprojectId%7D%7D/email/domains/%7B%7Bdomain%7D%7D/verify
developer auth
curl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/email/domains/%7B%7Bdomain%7D%7D/verify'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X POST "${BASE_URL}/admin/projects/${PROJECT_ID}/email/domains/{domain}/verify" \
  -H "Authorization: Bearer ${DEV_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{}'

DELETE /admin/projects/:projectId/email/domains/:domain

Remove a sending domain. For an Amba-managed domain, Amba deletes the DNS records it created; every other record (your own records, an existing DMARC policy, email forwarding) stays as it is. If the project default sender used this domain, email falls back to the shared mailbox.

Response 200

{ "data": { "deleted": true } }

Errors

  • 404 DOMAIN_NOT_FOUND.
  • 502 DNS_CLEANUP_FAILED — the records Amba created could not be deleted. Nothing was removed; retry.

Try it:

DELETE/admin/projects/%7B%7BprojectId%7D%7D/email/domains/%7B%7Bdomain%7D%7D
developer auth
curl -X DELETE 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/email/domains/%7B%7Bdomain%7D%7D'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X DELETE "${BASE_URL}/admin/projects/${PROJECT_ID}/email/domains/{domain}" \
  -H "Authorization: Bearer ${DEV_TOKEN}"

POST /admin/projects/:projectId/email/send

Send a templated or ad-hoc email. The full body is described in the email guide. Sender fields:

FieldTypeDescription
from{ email, name? }Optional. from.email must be on a verified sending domain of this project. Defaults to the project default sender, then the shared mailbox.
reply_tostring | string[]Optional. Defaults to the project's default_reply_to.

Errors

  • 400 INVALID_SENDER — from.email is not on a verified sending domain of this project. Nothing is sent.

Try it:

POST/admin/projects/%7B%7BprojectId%7D%7D/email/send
developer auth
curl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/email/send' \
  -H 'Content-Type: application/json' \
  -d '{
  "to": {
    "email": "user@example.com"
  },
  "subject": "Welcome",
  "text": "Thanks for signing up."
}'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X POST "${BASE_URL}/admin/projects/${PROJECT_ID}/email/send" \
  -H "Authorization: Bearer ${DEV_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"to":{"email":"user@example.com"},"subject":"Welcome","text":"Thanks for signing up."}'