Settings

Everything an organization has to be configured for, and the single call that answers “can I send, and if not why”.

Can I send? — the one call

GET /api/v1/me answers it. Several separate guards can refuse a send, and until now they refused without saying which one: this block names them.
bash
curl -H "x-api-key: $AGENTMAIL_API_KEY" "https://www.agentsmail.io/api/v1/me"
json
{
  "success": true,
  "data": {
    "organization": {"id": "…", "name": "Atelier Nord", "slug": "atelier-nord"},
    "organizationRole": "admin",
    "keyId": "…",
    "sending": {
      "ready": false,
      "blockers": ["domain_not_verified"],
      "setup": {
        "status": "dns_pending",
        "provisioning": "provisioned"
      }
    }
  }
}

Blocker codes

They are stable and never translated — this is a machine interface. Build your remediation on them, not on a message.
CodeWhat it meansHow to clear it
sending_disabledSending is switched off application-wideNothing you can do from the API
organization_not_allowedThe organization is not on the sending allowlistAsk the operator to allow it
sending_pausedThe email provider suspended sending for this organizationNothing from the API — see below
domain_not_verifiedA domain is declared but its DKIM is not SUCCESSPublish the DNS records, then verify
domain_ownership_unprovenDKIM is SUCCESS, but this organization has not proved it owns the domainPublish the _agentmail-challenge TXT, then verify
sender_not_on_verified_domainA domain is verified, but the default sender address is not on it (nor on one of its subdomains)Promote or set an address on the verified domain
no_sender_identityThe sender address book has no default — reported only alongside a real blockerAdd an address (POST /api/v1/settings/identities)
sending_paused is not something you can retry. It appears when the email provider suspends the organization — reputation, usually a bounce or complaint rate. Every send to your contacts is refused while it is there: campaigns, sequences, opt-in confirmations. The campaign test send stays available, so you can still check how a campaign renders while you sort the suspension out — it goes out under the application address, not yours. The blocker clears on its own when the provider re-enables sending, and never from the API. Fix what caused it — clean your lists, stop importing cold contacts — and talk to the operator.
no_sender_identity alone is not a blocker. With no sending identity set, emails still go out under the application address. It appears only when something else is already blocking, to tell you the account is bare.
sender_not_on_verified_domain is a real blocker, and it is yours to fix. A verified domain only lets you send from that domain: with codemail.io verified, hello@codemail.io and news@news.codemail.io go out, hello@example.org does not. Without this check the provider accepts the batch and then fails every message with "Email address is not verified" — nothing in the product would tell you why. It only ever appears once at least one domain is verified: before that, set the sender address freely.

Setup status

sending.setup is the same state the account owner reads on screen, as one code instead of a list. It is purely informational: it never enters blockers and never changes ready. Gate your sends on ready, and use setup.status to say where the account stands in one word. status is derived, first match wins, in this order:
statusWhenWhat to do
suspendedThe email provider suspended sending for this organizationNothing from the API — see the callout
sending_disabledSending is switched off application-wideWait — nothing you can do from the API
not_allowedThe organization is not on the sending allowlistAsk the operator to allow it
verification_failedSending is blocked on domains, and one of them failed DKIM verificationRe-check the DNS records, then verify
dns_pendingSending is blocked on domains for any other reason, including none declaredDeclare a domain, publish DNS, verify
ownership_pendingDKIM succeeded, but the domain ownership is not proved yetPublish the challenge TXT, then verify
sender_mismatchA domain is verified, but the default sender address is not on itPATCH /api/v1/settings/sending
incompleteNothing blocks, but the sending isolation is not fully in placeNothing — the operator retries it
activeNothing blocks, and the isolation is in placeSend
provisioning is the raw isolation state — none, provisioned or incomplete. none is not a fault: it simply means nothing has been attempted yet, and it does not stop a send.
An organization that is not allowed to send never reads active. That is the whole point of the ordering: a green light for a send the service would refuse is worse than no light at all. A verified domain does not make an account allowed, and setup.status says so.
Two domains, one verified and one failed? blockers is empty, ready is true, and setup.status is active — the account sends fine. verification_failed only ever shows up when the failure is actually blocking.

Reading everything at once

GET /api/v1/settings returns the whole state in one call.
json
{
  "success": true,
  "data": {
    "sending": {
      "senderEmail": "bonjour@atelier-nord.fr",
      "senderName": "Atelier Nord",
      "postalAddress": "12 rue des Lilas\n59000 Lille",
      "effectiveSender": "Atelier Nord <bonjour@atelier-nord.fr>"
    },
    "optin": {
      "required": true,
      "subject": "Confirme ton inscription",
      "content": "<div>…</div>",
      "customized": false
    },
    "readiness": {
      "ready": true,
      "blockers": [],
      "setup": {"status": "active", "provisioning": "provisioned"}
    }
  }
}
readiness here is the very same object GET /api/v1/me returns under sending, setup included — one question, one answer, two places to read it. effectiveSender is what will actually go out, fallback included — that is what you want to check, not the column.

Sending identity

An organization keeps a sender address book: one address per verified domain, and exactly one of them marked default. The default signs everything that does not name its own address: sequences, sign-up confirmations, and every campaign that left sendingIdentityId out. A campaign can name one of the other addresses — see POST /api/v1/campaigns — and then that address signs it, whatever the default becomes.
senderEmail and senderName in GET /api/v1/settings describe the default address. The contract has not changed: read them exactly as before. The full book lives at GET /api/v1/settings/identities.

Reading and writing the default — unchanged

PATCH /api/v1/settings/sendingadmin role required.
bash
curl -X PATCH -H "x-api-key: $AGENTMAIL_API_KEY" -H "content-type: application/json" \
  -d '{"senderName":"Atelier Nord"}' \
  "https://www.agentsmail.io/api/v1/settings/sending"
A field left out stays as it is. A field set to null is cleared and falls back to the application address. Without that distinction, setting only the display name would wipe the sender address. This endpoint acts on the default address, and on it alone:
BodyEffect
senderEmail (non-null)Sets the default address — the identity is created if the book is empty
senderEmail: nullRemoves the default from the book; sending falls back to the application address
senderName aloneRenames the default. Refused with 400 when there is no default — a display name has nowhere to live without an address
postalAddressStays on the organization: a legal mention, not a sender attribute
senderEmail: null is refused when the book holds more than one address. Removing the default while alternatives remain would leave the account with a full book and nothing signing. Promote another address first, with PATCH /api/v1/settings/identities/{identityId}.

The address book

EndpointRoleWhat it does
GET /api/v1/settings/identitiesmemberThe whole book, default first
POST /api/v1/settings/identitiesadminAdds an address — {senderEmail, senderName?, isDefault?}
PATCH /api/v1/settings/identities/{identityId}adminChanges the address or display name, or promotes it with {"isDefault": true}
DELETE /api/v1/settings/identities/{identityId}adminRemoves an address from the book
bash
curl -X POST -H "x-api-key: $AGENTMAIL_API_KEY" -H "content-type: application/json" \
  -d '{"senderEmail":"news@atelier-nord.fr","senderName":"Atelier Nord"}' \
  "https://www.agentsmail.io/api/v1/settings/identities"
json
{
  "success": true,
  "data": {
    "id": "…",
    "senderEmail": "news@atelier-nord.fr",
    "senderName": "Atelier Nord",
    "isDefault": false,
    "createdAt": "2026-08-17T09:00:00.000Z"
  }
}
The first address you add becomes the default on its own. An account never holds several addresses and nothing signing — that would clear no blocker and change no send.
The default cannot be deleted while other addresses remain, and {"isDefault": false} is refused. Both answer 400 naming the address. Silently promoting a survivor would change the sender of every scheduled campaign, every running sequence and every opt-in confirmation — without anyone asking for it. Promote another address first, then delete.Deleting the last address is allowed: sending then falls back to the application address, and no_sender_identity says so.
An address named by a campaign cannot be deleted either — whatever that campaign's status, draft and already-sent included. The refusal is a 400 that names the campaigns. The alternative would be a scheduled mass send silently changing sender, which is precisely what this product refuses to do. Point those campaigns at another address, or back at the default with {"sendingIdentityId": null}, then delete.
Sequences and opt-in confirmations always take the default. There is no per-sequence sender: the choice exists on campaigns only, through sendingIdentityId.

The sender address must be on a verified domain

Every address in the book — not just the default — must live on a domain of yours that is verified and proved. Once the organization has at least one verified domain, senderEmail must be on it, or on one of its subdomains. Anything else is refused with 400, naming both facts:
bash
curl -X PATCH -H "x-api-key: $AGENTMAIL_API_KEY" -H "content-type: application/json" \
  -d '{"senderEmail":"hello@example.org"}' \
  "https://www.agentsmail.io/api/v1/settings/sending"
json
{
  "error": "Erreur de validation : Votre domaine codemail.io est vérifié — mais hello@example.org n’est pas dessus. Utilisez une adresse sur ce domaine ou sur l’un de ses sous-domaines."
}
With no verified domain yet, any address is accepted: the sender is often set before DNS is published. domain_not_verified still blocks the sends until a domain is verified, and sender_not_on_verified_domain takes over from there.

Sign-up settings

PATCH /api/v1/settings/optinadmin role required.
bash
curl -X PATCH -H "x-api-key: $AGENTMAIL_API_KEY" -H "content-type: application/json" \
  -d '{"required":true,"subject":"Confirm your sign-up","content":"<p><a href=\"{{confirmationUrl}}\">Confirm</a></p>"}' \
  "https://www.agentsmail.io/api/v1/settings/optin"
FieldEffect
requiredWhen true, the organization forces double opt-in: doubleOptIn: false sent to the contact API no longer has any effect
subjectSubject of the confirmation email
contentIts HTML
The content must contain {{confirmationUrl}}. Without it the recipient has no way to confirm, and double opt-in breaks silently. A content without it is refused with 400, naming the missing variable.
Available variables: {{confirmationUrl}} (required), {{firstName}}, {{lastName}}, {{email}}, {{postalAddress}}, {{currentYear}}. There is no unsubscribe variable — the recipient is not subscribed yet. Sending the default content back is not a customization. A content identical to the default is stored as empty, so the organization keeps inheriting future improvements to it. customized tells you which case you are in.

Testing the confirmation email

POST /api/v1/settings/optin/test-sendadmin role required.
bash
curl -X POST -H "x-api-key: $AGENTMAIL_API_KEY" -H "content-type: application/json" \
  -d '{"email":"you@example.com","content":"<p><a href=\"{{confirmationUrl}}\">Confirm</a></p>"}' \
  "https://www.agentsmail.io/api/v1/settings/optin/test-send"
The content and subject of the body win over what is stored: try before you publish. The confirmation link in a test is fake — testing three times must not put three real, valid tokens into circulation. A test is a real send: it passes the same guards as any other. Answers {"status":"blocked"} (sending switched off application-wide), {"status":"not_allowed"} (organization not allowed, or domain not verified) or {"status":"send_refused"} (the provider refused the send) rather than failing, so you can tell a refusal from an error — and tell the refusals apart.