# Nexmail: the whole site in plain text > Free team email on your own domain. Nexmail gives your whole team addresses at your domain, receives through Resend’s inbound, sends through your own Resend or Brevo account on their free plans, and puts every message in one shared inbox routed to Slack. All you need is your domain. Nexmail gives your whole team email addresses at your own domain, for free. Mail for your domain arrives through Resend’s inbound (or your own Postfix or Mailu server) and replies leave through your own Resend or Brevo account; both providers have free plans that cover a small team. Nexmail itself is free while in early access. Every message lands in one shared inbox and is announced in the Slack channel that owns its address. Replies fail over between providers, and a message with no provider available waits in the queue instead of failing. ## What happens to every email Source: https://nexmail.nexprove.com/features ### 1. Receive Mail for your domains arrives through Resend or your own mail server. - Inbound through Resend, or a self-hosted Postfix or Mailu relay that posts signed messages. - A webhook that cannot save a message answers with an error, so the sender retries instead of the mail vanishing. - Duplicate deliveries are recognised and stored once. - Attachments are kept on local disk or any S3-compatible bucket. ### 2. Route Each address belongs to a Slack channel, set by rules you control. - Rules for an exact address, a whole domain, a domain fallback and a workspace fallback, checked in that order. - Mail that matches nothing is stored and listed under Unrouted. It is never discarded. - Replies stay in one Slack thread, and moving a route moves the next message with it. ### 3. Answer A shared inbox where the team assigns, labels, notes and replies. - Assign a conversation, add labels, and leave notes only your team can see. - Snooze a thread until later, or set a follow-up that brings it back if nobody replies. - Drafts save as you type. Reply from the inbox, the compose page or straight from Slack. - Keyboard first: j and k to move, x to select, Enter to open, c to compose, ? for the full list. - An assistant suggests tidy-ups, like threads nobody owns, and changes nothing until you click. ### 4. Check Every send is read for problems the recipient would see before it leaves. - Stops a link whose text names one site and opens another, an executable attachment, a spoofed display name, or an address that unsubscribed. - Warns, and never blocks, on SPF, DKIM, DMARC and provider health. - An override is allowed and recorded in the audit log under the name of the person who made it. ### 5. Send Replies go out through whichever provider is healthy, and wait when none is. - Resend, Amazon SES, Brevo, SMTP2GO or any SMTP server, in an order you choose. - Three failures in a row open a provider’s breaker and traffic moves to the next one. - With no provider available, mail waits in the queue. It is never marked failed for that. - Bounces and complaints come back from each provider and update the suppression list. ### 6. Limit Daily ceilings and an emergency stop keep a mistake from becoming a flood. - Daily limits for the whole workspace, for each provider and for each sending domain. - Reaching a limit schedules mail for the next window rather than failing it. - One switch in Settings holds every outgoing message. Incoming mail keeps arriving. ## Also included - **Outreach campaigns.** Import recipients from a spreadsheet, a paste or the CRM, review the list, get it approved and send within your limits. Opens, clicks and one-click unsubscribe are built in. - **Separate workspaces.** Each organisation has its own domains, inbox, routes, providers and team. Nothing crosses from one to another. - **Guided domain setup.** Nexmail lists the DNS records to publish, checks each one through public resolvers and finishes with a test send. - **Team and roles.** Invite people by email, choose who is an admin, and let anyone reset a forgotten password on their own. - **Your logo on outgoing mail.** Upload a logo once and it appears in the messages your team sends. - **A record of every message.** Every email in and out is stored with its delivery history, so “did it arrive?” takes seconds to answer. ## Keyboard shortcuts in the inbox - `j / ↓`: Next conversation - `k / ↑`: Previous conversation - `x`: Select - `Enter`: Open - `c`: Compose - `/`: Search - `Esc`: Clear selection - `?`: Show every shortcut ## Integrations Source: https://nexmail.nexprove.com/integrations ### Sending Connect one or several. Nexmail fails over between them. - Resend (live): Sends through the Resend API, with delivery and bounce events. - Amazon SES (live): Sends through SES, with bounces and complaints delivered over SNS. - Brevo (live): SMTP relay for campaigns and bulk mail, with delivery feedback. - SMTP2GO (live): SMTP relay for campaigns and bulk mail, with delivery feedback. - Any SMTP server (live): Your own server or any relay that speaks authenticated SMTP. - Postmark (planned): A native Postmark transport. - Mailgun (planned): A native Mailgun transport. ### Receiving How mail for your domains reaches Nexmail. - Resend inbound (live): Resend receives your mail and hands every message to Nexmail. - Postfix or Mailu (live): Your own mail server posts each message, signed with a shared secret. - Amazon SES receiving (exploring): Receive through SES instead of Resend. ### Team chat Where your team hears about new mail. - Slack (live): New mail posts to the right channel, threads stay together, and replies can be sent from Slack. - Microsoft Teams (exploring): The same routing, into Teams channels. ### CRM and API For tools that send and read mail without a person. - REST API (live): Machine credentials, created by an admin, for launching approved campaigns and reading results. - CRM sync (live): Contacts, unsubscribes and engagement shared with the Nextlabs CRM. - Outgoing webhooks (planned): Tell your own systems when mail arrives, is answered or bounces. ### Domains and storage The pieces around the mail itself. - Any DNS host (live): Nexmail lists the records and checks them through public resolvers, wherever your DNS lives. - S3-compatible storage (live): Keep attachments in Amazon S3, Cloudflare R2 or any S3-compatible bucket. - One-click DNS with Cloudflare (planned): Publish the records for you instead of listing them. ## Roadmap Source: https://nexmail.nexprove.com/roadmap Planned means decided and designed, not built. Nothing has a date until it ships. ### Recently shipped Live in the app today. - **Contacts and newsletters.** Import, sort and segment contacts, run newsletters with public sign-up pages, and start campaigns from a list. - **Campaign scheduling and follow-ups.** Pace a campaign over days and follow up automatically until someone replies. - **General mailboxes.** support@, info@ and others, each shared with everyone or only the people you choose. - **One-click DNS.** Publish your domain records through Cloudflare, Vercel or DigitalOcean instead of copying them. - **Daily sending limits.** Mail past a limit is scheduled for the next window, never lost. - **Deliverability check while you write.** Problems show up in the composer, not after the send. - **Recipient import.** From the CRM, a spreadsheet or a paste, reviewed before it is used. - **Folded quotes and keyboard help.** Long threads open on the newest message, and ? lists every shortcut. ### Planned Decided and designed. Not built yet. - **AI reply drafts.** A first draft written from the thread and your past replies. A person reads and approves it before anything is sent. - **AI triage.** Suggested labels, owners and routes for incoming mail, added to the rules-based assistant you already have. - **Shared templates.** Reusable replies with variables, shared across the team. - **Send later.** Pick when a reply goes out. - **More providers.** Native Postmark and Mailgun transports. - **Outgoing webhooks.** Events for your own systems when mail arrives, is answered or bounces. - **Connect AI tools over MCP.** Let AI assistants read, triage and draft mail for you, within the same access rules your team has. ### Exploring Ideas we are testing. They may change or not happen. - **Team analytics.** Response times, volume by channel and deliverability trends. - **Microsoft Teams.** Routing into Teams channels as well as Slack. - **Receiving through Amazon SES.** An inbound path that does not depend on Resend. ## Pricing Source: https://nexmail.nexprove.com/pricing ### Is Nexmail really free? Nexmail itself costs nothing while it is in early access. Mail arrives through Resend’s inbound and leaves through your own Resend or Brevo account, and both have free plans, so a small team can run on its own domain without paying anyone. Higher volumes move you onto their paid plans, billed by them. ### What does early access include? Everything Nexmail does today, for as many people as your team has. There is no feature held back for a paid plan yet. ### What do I pay my email providers? Whatever they charge you. Nexmail sends through your own Resend, Amazon SES, Brevo, SMTP2GO or SMTP account, so the bill comes from them at their rates. Most offer a free tier that covers a small team. ### Can Brevo receive my mail too? No. In Nexmail, Brevo sends. Mail for your domain reaches Nexmail through Resend’s inbound or through your own mail server, such as Postfix or Mailu. ### Do I need my own domain? Yes, and it is all you need. Nexmail receives and sends mail for domains you own, so it can route each address and authenticate everything it sends. ### What happens when early access ends? Prices go up on this page first, and every early-access workspace is told by email before anything changes. ## Docs Last updated 2026-10-14. ### Connect your domain to Nexmail Source: https://nexmail.nexprove.com/docs/connect-your-domain To send and receive as you@yourdomain, Nexmail needs a few DNS records at your domain. This page explains what each one does, how to find where to add them, and the mistakes that trip most people up. #### How it works 1. In Nexmail, open **Get started** and add your domain. Nexmail asks Resend for the exact records your domain needs. 2. Add those records where your domain’s DNS is managed (your DNS host). 3. Click **Check DNS** in Nexmail. When every record is found, your domain is verified and mail can leave from it. > **Your DNS host might add them for you.** If your DNS is on Cloudflare, Vercel or DigitalOcean, Nexmail can add every record with one API token. Anywhere else, Nexmail can move your DNS to Cloudflare for free and do the same. See [Cloudflare](https://nexmail.nexprove.com/docs/connect-your-domain/cloudflare). #### What each record does Copy the exact values from Nexmail. The ones below show the shape for example.com; the region in them (us-east-1) and the DKIM key differ for every account. | Job | Type | Host | Example value | What it does | | --- | --- | --- | --- | --- | | Sending | MX | `send` | `feedback-smtp.us-east-1.amazonses.com (priority 10)` | Lets Resend collect bounces for the mail you send. It only covers the send subdomain, so your own mail is untouched. | | Sending | TXT | `send` | `v=spf1 include:amazonses.com ~all` | SPF. Tells other mail servers that Resend is allowed to send for you. | | Signing | TXT | `resend._domainkey` | `p=MIGfMA0GCSqGSIb3… (a long key)` | DKIM. A public key that proves each message really came from your domain and was not changed on the way. | | DMARC | TXT | `_dmarc` | `v=DMARC1; p=none; rua=mailto:dmarc@example.com` | Tells receivers what to do with mail that fails the checks. p=none means report only, block nothing, which is the safe start. | | Receiving (optional) | MX | `@` | `inbound-smtp.us-east-1.amazonaws.com (priority 10)` | Sends mail for your domain to Nexmail through Resend. Add it only if Nexmail should receive your mail: it replaces wherever your mail goes today. | Sending through Brevo instead of Resend? Nexmail then asks for an SPF record at `@` (`v=spf1 include:spf.brevo.com ~all`), a DKIM record at `mail._domainkey` copied from Brevo → Senders, Domains & Dedicated IPs → Domains, and the same DMARC record. #### Find your DNS host Your DNS host is whoever runs your domain’s nameservers. It is often where you bought the domain, but not always. Nexmail looks this up for you and names your host on the records step. To check yourself, look up the nameservers with any “NS lookup” tool, or run `dig NS example.com +short` in a terminal. Then match them: | Nameservers contain | Your DNS host | | --- | --- | | `registrar-servers.com` | [Namecheap](#namecheap) | | `ns.cloudflare.com` | [Cloudflare](#cloudflare) | | `domaincontrol.com` | [GoDaddy](#godaddy) | | `awsdns` | [Amazon Route 53](#route53) | | `googledomains.com` or `squarespacedns.com` | [Squarespace (formerly Google Domains)](#squarespace) | | `vercel-dns.com` | [Vercel](#vercel) | | `porkbun.com` | [Porkbun](#porkbun) | | `dns-parking.com` or `hostinger` | [Hostinger](#hostinger) | | `digitalocean.com` | [DigitalOcean](#digitalocean) | | `ui-dns` | [IONOS](#ionos) | #### Common mistakes ##### Typing the full name in the host field Most DNS hosts add your domain to the end of the host for you. Type `send.example.com` and you get `send.example.com.example.com`, which never verifies. Type only the part Nexmail shows in the Host column: `send`, `resend._domainkey`, `_dmarc`, or `@` for the domain itself. ##### Leaving the Cloudflare proxy on On Cloudflare, CNAME and A records have a Proxy status. Any record that is part of your email setup must be **DNS only**, shown as a grey cloud. An orange cloud (Proxied) hides the real value and mail checks fail. Click the cloud to turn it grey, then save. *Illustration: Cloudflare’s Proxy status switch. Mail records need the grey cloud: DNS only.* ##### Putting Namecheap MX records under Host Records Namecheap keeps mail records apart. TXT and CNAME records go under Advanced DNS → Host Records, but MX records go in the **Mail Settings** section further down: set its dropdown to **Custom MX** first. See [the Namecheap guide](https://nexmail.nexprove.com/docs/connect-your-domain/namecheap). ##### Not waiting for the change to spread New records usually show up within minutes. Sometimes it takes an hour, and old cached answers can last up to 48 hours. Nexmail keeps checking on its own. Do not delete and re-add a record that looks right: that only restarts the wait. ##### Two SPF records on one host A host can have only one record starting with `v=spf1`. If one is already there, merge them into one, for example `v=spf1 include:amazonses.com include:_spf.google.com ~all`. Two separate SPF records make both fail. #### Steps for every DNS host Pick your host. “Your records table in Nexmail” means the records on the **Publish the DNS records and verify** step of Nexmail’s setup. ##### Namecheap There is a full guide with pictures: [Namecheap step by step](https://nexmail.nexprove.com/docs/connect-your-domain/namecheap). 1. Sign in to Namecheap and open Domain List. 2. Click Manage next to your domain, then the Advanced DNS tab. 3. Under Host Records, click Add New Record and pick the Type (TXT Record, CNAME Record). 4. Paste the Host and the Value from your records table in Nexmail. Leave TTL on Automatic. 5. Click the green tick to save. Repeat for each record. **Mail (MX) records:** Mail records go in the Mail Settings section further down, not under Host Records: change its dropdown to Custom MX, then add each MX record with its Host, Value and Priority. Switching to Custom MX turns off Namecheap email forwarding for this domain. ##### Cloudflare There is a full guide with pictures: [Cloudflare step by step](https://nexmail.nexprove.com/docs/connect-your-domain/cloudflare). 1. Sign in to Cloudflare and pick your domain. 2. Open DNS → Records. 3. Click Add record, pick the Type, and paste the Name and Content from your records table in Nexmail. 4. For CNAME records, set Proxy status to DNS only (grey cloud). Mail records must never be proxied. 5. Save, and repeat for each record. Or use Import and Export → Import with the zone file Nexmail offers. **Mail (MX) records:** MX records take a Priority; enter the number shown in the table. ##### GoDaddy 1. Sign in to GoDaddy and open My Products → Domains. 2. Pick your domain and choose DNS → Manage DNS. 3. Click Add New Record, pick the Type, and paste the Name and Value from your records table in Nexmail. 4. Leave TTL at the default and save. Repeat for each record. **Mail (MX) records:** For MX records also enter the Priority shown in the table. ##### Amazon Route 53 1. Open Route 53 → Hosted zones and pick your domain. 2. Click Create record, type the Record name (leave it empty for @), pick the Record type and paste the Value. 3. TXT values must be wrapped in double quotes in Route 53. **Mail (MX) records:** MX values are written as "priority value", for example 10 inbound-smtp.us-east-1.amazonaws.com. ##### Squarespace (formerly Google Domains) 1. Sign in to Squarespace Domains and pick your domain. 2. Open DNS → DNS Settings, then Custom records. 3. Click Add record, pick the Type, paste the Host and Data from your records table in Nexmail, and save. **Mail (MX) records:** MX records take the Priority shown in the table. ##### Vercel 1. Open the Vercel dashboard → Domains and pick your domain. 2. Under DNS Records, enter the Name, pick the Type, paste the Value and click Add. **Mail (MX) records:** MX records take the Priority shown in the table. ##### Porkbun 1. Open Domain Management and click DNS next to your domain. 2. Pick the Type, enter the Host and Answer from your records table in Nexmail, and click Add. ##### Hostinger 1. Open hPanel → Domains, pick your domain and open DNS / Nameservers. 2. Under Manage DNS records, pick the Type, enter the Name and value, and click Add Record. ##### DigitalOcean 1. Open Networking → Domains and pick your domain. 2. Pick the record type tab, enter the Hostname and value, and click Create Record. ##### IONOS 1. Open Domains & SSL, click the gear next to your domain and choose DNS. 2. Click Add record, pick the Type, enter the Host name and value, and save. ##### Any other host 1. Sign in where your domain is registered (or wherever its DNS is managed) and find its DNS records. 2. Add each record from your records table in Nexmail with the Type, Host and Value shown. 3. If the host field shows your domain after it, enter only the part shown in Host. Use @ for the domain itself. ### Connect your domain on Cloudflare Source: https://nexmail.nexprove.com/docs/connect-your-domain/cloudflare Two paths. If your domain’s DNS is already on Cloudflare, add Nexmail’s records with a token or by hand. If it is somewhere else, Nexmail can move it to Cloudflare for free and set everything up in the same step. #### Which path is yours? - Your nameservers end in `ns.cloudflare.com`: your DNS is already on Cloudflare. Follow [path A](#already-on-cloudflare). - Anything else: your DNS is elsewhere. Add the records there (see [every DNS host](https://nexmail.nexprove.com/docs/connect-your-domain#other-hosts)), or follow [path B](#move-your-dns) to move it to Cloudflare. #### A. Your DNS is already on Cloudflare ##### Let Nexmail add the records (fastest) On the records step, Nexmail shows **Add them all for me** with a box for a Cloudflare API token. Make the token from Cloudflare’s ready-made “Edit zone DNS” template: 1. Open [Cloudflare → My Profile → API Tokens](https://dash.cloudflare.com/profile/api-tokens) and click Create Token. 2. Next to the "Edit zone DNS" template, click Use template. 3. Check the Permissions row reads Zone, DNS, Edit. The template fills it in for you. 4. Under Zone Resources, choose Include, then Specific zone, then your domain. 5. Click Continue to summary, then Create Token. 6. Copy the token. Cloudflare shows it only once. *Illustration: The template’s permission row: Zone, then DNS, then Edit. You do not need to change it.* Paste the token into Nexmail and click **Add records**. Nexmail adds each record as DNS only, checks them, and forgets the token. It is never stored. ##### Or add them by hand 1. Sign in to Cloudflare and pick your domain. 2. Open DNS → Records. 3. Click Add record, pick the Type, and paste the Name and Content from your records table in Nexmail. 4. For CNAME records, set Proxy status to DNS only (grey cloud). Mail records must never be proxied. 5. Save, and repeat for each record. Or use Import and Export → Import with the zone file Nexmail offers. **Mail (MX) records:** MX records take a Priority; enter the number shown in the table. *Illustration: Proxy status must be the grey cloud, DNS only, for every mail record.* #### B. Move your DNS to Cloudflare Cloudflare DNS is free. Nexmail copies your current records to Cloudflare, adds its own, and then you change two nameservers where you bought the domain. Nothing changes until you do that last step, and going back is putting the old nameservers back. ##### 1. Make a custom token > **Read this before you start.** Each permission row in Cloudflare’s form has **three dropdowns**. The first one is easy to miss: it must say **Zone** (or **Account** for the last row). If it says anything else, the second dropdown will not offer DNS. 1. Open [Cloudflare → My Profile → API Tokens](https://dash.cloudflare.com/profile/api-tokens) and click Create Token. 2. At the bottom, next to Create Custom Token, click Get started. Name the token "Nexmail". 3. Under Permissions, the first row has three dropdowns. Set the first dropdown to Zone, the second to Zone, the third to Edit. 4. Click + Add more. In the new row, set the first dropdown to Zone, the second to DNS, the third to Edit. 5. Click + Add more again. In the third row, set the first dropdown to Account (not Zone), the second to Account Settings, the third to Read. 6. Under Account Resources, choose Include, then your account. 7. Under Zone Resources, choose Include, then All zones from an account, then your account. 8. Click Continue to summary, then Create Token, and copy the token. Cloudflare shows it only once. *Illustration: The three permission rows, three dropdowns each. Set the first dropdown of each row, highlighted, before the other two.* ##### 2. Paste it into Nexmail On the records step, open **Move to Cloudflare**, paste the token and click **Copy my DNS to Cloudflare**. Nexmail lists what it copied and anything it could not, and shows your two Cloudflare nameservers. ##### 3. Switch the nameservers This is the one step no registrar lets an outside app do. Sign in where you bought the domain and replace its nameservers with the two Cloudflare gave you: ##### At Namecheap 1. Open Domain List and click Manage next to your domain. 2. In the Nameservers row (Domain tab), change Namecheap BasicDNS to Custom DNS. 3. Paste the two nameservers Cloudflare gave you, one per line, and click the green tick. ##### At GoDaddy 1. Open My Products → Domains and pick your domain. 2. Choose DNS → Nameservers → Change Nameservers → I'll use my own nameservers. 3. Enter the two nameservers Cloudflare gave you and save. ##### At Squarespace (formerly Google Domains) 1. Open your domain → DNS → Domain Nameservers → Use custom nameservers. 2. Enter the two nameservers Cloudflare gave you and save. ##### At Porkbun 1. Open Domain Management, click Details under your domain, then Authoritative Nameservers → edit. 2. Replace them with the two nameservers Cloudflare gave you and submit. ##### At Hostinger 1. Open hPanel → Domains → your domain → DNS / Nameservers → Change nameservers. 2. Choose custom nameservers, enter the two nameservers Cloudflare gave you and save. ##### At IONOS 1. Open Domains & SSL, click the gear next to your domain → Nameservers. 2. Choose custom nameservers, enter the two nameservers Cloudflare gave you and save. ##### Anywhere else 1. Sign in where you bought the domain (the registrar) and find its nameserver settings. 2. Replace the current nameservers with the two nameservers Cloudflare gave you and save. ##### 4. Wait for the switch Nexmail checks every minute and tells you when the switch is seen. It usually takes minutes, occasionally a few hours. From then on, any DNS change Nexmail needs is one click. #### If something goes wrong - **“The token is missing a permission.”** Open the token in Cloudflare and check every row’s first dropdown. It must be Zone (Account for the Account Settings row). - **Nexmail cannot see your domain.** Check Zone Resources includes your account (path B) or your domain (path A). - **Lost the token.** Cloudflare shows it once. Make a new one; Nexmail never kept the old one. - **A record shows an orange cloud.** Click it to turn it grey (DNS only) and save. ### Connect your domain on Namecheap Source: https://nexmail.nexprove.com/docs/connect-your-domain/namecheap Namecheap splits DNS records into two places. TXT and CNAME records go under Host Records; MX records go under Mail Settings, set to Custom MX. Here is each step. #### 1. Open Advanced DNS 1. Sign in to Namecheap and open Domain List. 2. Click Manage next to your domain, then the Advanced DNS tab. *Illustration: Advanced DNS: Host Records at the top for TXT and CNAME, Mail Settings below with the Custom MX dropdown.* #### 2. Add the TXT and CNAME records under Host Records 1. Under Host Records, click Add New Record and pick the Type (TXT Record, CNAME Record). 2. Paste the Host and the Value from your records table in Nexmail. Leave TTL on Automatic. 3. Click the green tick to save. Repeat for each record. In the Host box, type only the part Nexmail shows, such as `send`, `resend._domainkey` or `_dmarc`. Namecheap adds your domain on its own. Use `@` for the domain itself. #### 3. Add the MX records under Mail Settings 1. Scroll down to the **Mail Settings** section of the Advanced DNS tab. 2. Change its dropdown to **Custom MX**. 3. Click **Add New Record** and enter the Host, Value and Priority from your records table in Nexmail. Repeat for each MX record. 4. Click the green tick to save. > **This turns off Namecheap email forwarding.** Mail records go in the Mail Settings section further down, not under Host Records: change its dropdown to Custom MX, then add each MX record with its Host, Value and Priority. Switching to Custom MX turns off Namecheap email forwarding for this domain. If you rely on Namecheap forwarding today, leave out the receiving MX at `@` until you are ready for Nexmail to receive your mail. The `send` MX can be added either way. #### 4. Check in Nexmail Back in Nexmail, click **Check DNS**. Most records appear within minutes. If one stays missing after an hour, compare its Host box with the table: a host typed as `send.example.com` is the usual cause. #### Rather not do it by hand? Nexmail can move your DNS to Cloudflare for free and add every record for you. Then you change the nameservers at Namecheap once. See [moving to Cloudflare](https://nexmail.nexprove.com/docs/connect-your-domain/cloudflare#move-your-dns). 1. Open Domain List and click Manage next to your domain. 2. In the Nameservers row (Domain tab), change Namecheap BasicDNS to Custom DNS. 3. Paste the two nameservers Cloudflare gave you, one per line, and click the green tick. ### Using Nexmail from AI agents and scripts Source: https://nexmail.nexprove.com/docs/ai-agents A REST API for campaigns, built so an AI agent can do the work and a person still decides what goes out. Bearer tokens, JSON in and out, and refusals you can branch on. #### Rules for agents Follow these exactly. They are how the API is built, not suggestions. 1. Use only the endpoints on this page. Do not guess other paths. 2. Never launch a campaign a person has not approved. The launch call must name that person, and it must echo the approval digest they saw. 3. Branch on the HTTP status and on `code` when present. Never on the wording of `error`; it changes. 4. A `202` from a launch means queued, not sent. Read the campaign afterwards to see what happened. 5. Never retry a `422` with a freshly read digest unless a person approved again. That defeats the approval. 6. Treat a held or deferred recipient as waiting, not failed. Nexmail never drops mail; it waits. 7. Never print, log or store the token anywhere but a secret store. #### Get a credential 1. An administrator signs in to Nexmail and opens **Settings → Credentials**. 2. They create a credential, name it after the agent (for example `crm-agent`), and tick only the scopes it needs. 3. Nexmail shows the token **once**. Store it in a secret manager. A lost token cannot be recovered; mint a new one and revoke the old. The token looks like `mgw_<24 hex characters>_`. Send it on every request: ```http Authorization: Bearer mgw_0123456789abcdef01234567_REPLACE_WITH_SECRET Content-Type: application/json ``` Base URL: `https://nexmail.nexprove.com`. Every path below is relative to it. ##### Scopes | Scope | Allows | | --- | --- | | `campaigns:read` | List and read campaigns, read the approval digest, preview, read recipients and send limits. **Required for any launch flow**: without it you cannot read the digest the launch must echo. | | `campaigns:write` | Create, edit and delete draft campaigns; add recipients; mark reply outcomes. | | `campaigns:send` | Launch a campaign, with a person’s approval attached. | | `campaigns:prerendered` | Create campaigns whose recipients each carry finished subject, HTML and text. Not implied by `campaigns:write`. Only for systems that render and approve mail themselves. | A credential belongs to one organization and acts only inside it. It is not a user: it can never reach settings, raise a send limit, lift the emergency stop, or take a launch override. #### Errors Every error is JSON with an `error` string. Some carry more fields: ```json { "error": "This credential does not carry campaigns:send" } ``` | Status | Means | What to do | | --- | --- | --- | | 400 | The body is invalid, or the action is not allowed in this state (for example editing a campaign that is not a draft). | Fix the request. Do not retry unchanged. | | 401 | No token, or an unknown, malformed or revoked one. Body: `{"error":"Authentication required"}`. | Stop. Ask a person for a valid credential. | | 403 | The credential lacks the scope, or the action is for people only. | Stop. Do not look for a way around it. | | 404 | Not found in this organization. | Check the id. | | 409 | The campaign changed while you were acting on it. | Read it again, then decide. | | 422 | Refused on purpose: missing or stale approval, or content that would harm the recipient. Carries `blocked: true` and often `code`. | Read `code`. Most need a person. | | 429 | Rate limited. Has a `retry-after` header in seconds. | Wait that long. | | 500 | Server error. `{"error":"Internal server error"}`. | Retry later with backoff. Nothing you sent is half-applied in a way that loses mail. | #### Endpoints | Method | Path | Scope | Does | | --- | --- | --- | --- | | GET | `/api/campaigns` | `campaigns:read` | The 100 newest campaigns, each with `stats`. | | POST | `/api/campaigns` | `campaigns:write` | Create a draft. | | GET | `/api/campaigns/:id` | `campaigns:read` | One campaign, its stats, approval and a sample of 50 recipients. Add `?approvalDigest=1` for the digest. | | PATCH | `/api/campaigns/:id` | `campaigns:write` | Edit a draft. | | DELETE | `/api/campaigns/:id` | `campaigns:write` | Delete a draft. Sent campaigns cannot be deleted. | | GET | `/api/campaigns/:id/recipients` | `campaigns:read` | Search the list: `?q=` and `?take=` (default 50). | | POST | `/api/campaigns/:id/recipients` | `campaigns:write` | Add up to 5,000 recipients per call. Safe to repeat. | | PATCH | `/api/campaigns/:id/recipients/:recipientId` | `campaigns:write` | Record how a reply went. | | GET | `/api/campaigns/:id/preview` | `campaigns:read` | Dry run: what a launch would do, with warnings. | | POST | `/api/campaigns/:id/preview` | `campaigns:read` | Render the mail for one recipient or sample fields. | | POST | `/api/campaigns/:id/test-send` | people only | Sends a test to the signed-in person. A credential gets 403. | | POST | `/api/campaigns/:id/send` | `campaigns:send` | Launch, with the approval attached. Answers 202. | | GET | `/api/campaigns/send-limits` | `campaigns:read` | Today’s sending ceilings and how much is left. | ##### List campaigns ```bash curl -s https://nexmail.nexprove.com/api/campaigns \ -H "Authorization: Bearer $NEXMAIL_TOKEN" ``` 200: ```json { "campaigns": [ { "id": "CAMPAIGN_ID", "name": "October update", "status": "draft", "contentMode": "template", "fromAddress": "news@example.com", "stats": { "total": 0, "pending": 0 } } ] } ``` `status` is one of `draft`, `sending`, `sent`, `failed`. ##### Create a draft Fields: `name` (required), `subject` (required), `bodyHtml` and/or `bodyText` (at least one), `from` (required: an address on a verified domain of this organization), `fromName`, `subjectB` (a second subject to A/B test). Merge fields are written `{{firstName}}` and filled from each recipient’s `fields`. ```bash curl -s -X POST https://nexmail.nexprove.com/api/campaigns \ -H "Authorization: Bearer $NEXMAIL_TOKEN" -H "Content-Type: application/json" \ -d '{"name":"October update","subject":"What changed in October", "bodyHtml":"

Hi {{firstName}},

Here is what changed.

", "bodyText":"Hi {{firstName}},\n\nHere is what changed.", "from":"news@example.com","fromName":"Example Co"}' ``` 201: ```json { "campaign": { "id": "CAMPAIGN_ID", "name": "October update", "status": "draft" } } ``` ##### Edit a draft Send only the fields to change: `name`, `subject`, `bodyHtml`, `bodyText`, `from`, `fromName`, `subjectB` (null turns the test off). Anything but a draft answers 400. PATCH /api/campaigns/:id: ```json { "subject": "What changed this month" } ``` ##### Add recipients Up to 5,000 per call; send a long list in several calls. Repeating a call adds nobody twice. Addresses on the organization’s suppression list (unsubscribed, bounced, complained) come back in `suppressed` and are never mailed. POST /api/campaigns/:id/recipients: ```json { "recipients": [ { "email": "ada@example.org", "fields": { "firstName": "Ada" } }, { "email": "lin@example.org", "fields": { "firstName": "Lin" } } ] } ``` 200: ```json { "added": 2, "skipped": 0, "suppressed": [] } ``` Adding people **after** a campaign was approved needs a fresh approval for the addition: send the same `authorisation` block as a launch, without `launchedBy`, carrying the digest read just before the person approved. Without it the answer is 422 `addition_requires_approval`. ##### Dry run and preview `GET /api/campaigns/:id/preview` reports what a launch would do: counts, recipients missing merge fields, content that would be refused, which transports would carry it, and plain-language `warnings`. Show every warning to the person approving. 200 (shortened): ```json { "campaignId": "CAMPAIGN_ID", "status": "draft", "subjectTest": false, "stats": { "total": 2, "pending": 2 }, "warnings": [ "Launching this campaign over a machine credential requires an authorisation block naming the person who approved it. …" ] } ``` `POST /api/campaigns/:id/preview` renders the actual mail. Body (all optional): `recipientId`, or `fields` for sample data, and `variant` (`"A"` or `"B"`). The answer has `subject`, `html`, `text`, `missing` (merge fields it could not fill) and `refusals`. POST /api/campaigns/:id/preview: ```json { "fields": { "firstName": "Ada" } } ``` ##### Test send A test goes to the signed-in person’s own inbox, so it needs a person in the Nexmail app: **a credential gets 403**. Each person can send 10 tests an hour; the 11th answers 429 with `retry-after`. Ask the approver to send themselves a test from the campaign page. ##### Read the approval digest Right before a person approves, read the campaign with `?approvalDigest=1`. The digest fingerprints the content (subject, bodies, sender, click tracking, test subject) and the full recipient list. Keep the object exactly as returned. ```bash curl -s "https://nexmail.nexprove.com/api/campaigns/CAMPAIGN_ID?approvalDigest=1" \ -H "Authorization: Bearer $NEXMAIL_TOKEN" ``` 200 (shortened): ```json { "campaign": { "id": "CAMPAIGN_ID", "status": "draft" }, "approvalDigest": { "content": "7285e7f5…64 hex", "recipients": "6b5e4a64…64 hex" } } ``` ##### Launch The `authorisation` block is required from a credential. `approvedBy` is the person who approved; `launchedBy` is who pressed launch (send the same person twice if one person did both). `approvedAt` is when they approved: not in the future, not older than 30 days. Unknown keys are rejected. POST /api/campaigns/:id/send: ```json { "authorisation": { "approvedBy": { "ref": "crm-user-9f3", "name": "Dana Okafor" }, "approvedAt": "2026-10-14T09:58:02.000Z", "launchedBy": { "ref": "crm-user-9f3", "name": "Dana Okafor" }, "digest": { "content": "7285e7f5…", "recipients": "6b5e4a64…" } }, "allowMissingFields": false } ``` 202: ```json { "queued": true, "campaignId": "CAMPAIGN_ID", "pending": 2, "approvedBy": { "source": "crm_assertion", "ref": "crm-user-9f3", "name": "Dana Okafor", "asserted": true }, "launchedBy": { "source": "crm_assertion", "ref": "crm-user-9f3", "name": "Dana Okafor", "asserted": true }, "approverLaunchedIt": true } ``` `allowMissingFields: true` sends even where a merge field is empty. Leave it false unless a person chose otherwise. `overrideUnauthenticatedTransport` and `overrideUnroutedReplyTo` exist for signed-in administrators only; a credential that sets either gets 403. Launch refusals that need a person (422, `blocked: true`): | `code` | Means | Do | | --- | --- | --- | | `missing_authorisation` | No `authorisation` block. | Your request is incomplete. Fix it; no person needed. | | `stale_approval` | The campaign or its list changed after the approval. `mismatched` says which (`content`, `recipients`). | Show the person the current campaign (the 422 carries the new `approvalDigest`), get a new approval, launch again. | | `approval_expired` | `approvedAt` is more than 30 days old. | Ask the person again. | | `approval_in_future` | `approvedAt` is more than 5 minutes ahead of Nexmail’s clock. | Fix the clock or the field. | A 422 **without** `code` but with `refusals` means the content itself is blocked (for example a link whose text names a different site than it opens). Edit the draft; it cannot be overridden. ##### After launch Poll `GET /api/campaigns/:id` (once a minute is plenty). Read each recipient’s `disposition.state`, not `status`: it is one of `pending`, `queued`, `held` (waiting for a later window, with `until`), `sending`, `retrying`, `dispatched` (handed to a provider), `failed`, `suppressed` or `unknown`. Only `failed` is a failure. `held` alongside `stats` says how many are waiting and when the first goes. If every provider refuses at launch, the campaign stays `draft` with everyone `pending`; launching again later is safe and adds nobody twice. ##### Record a reply outcome PATCH /api/campaigns/:id/recipients/:recipientId: ```json { "disposition": "meeting_booked" } ``` `disposition` is `interested`, `meeting_booked`, `not_interested`, or `null` to clear it. ##### Send limits ```bash curl -s "https://nexmail.nexprove.com/api/campaigns/send-limits?from=news@example.com" \ -H "Authorization: Bearer $NEXMAIL_TOKEN" ``` 200 (shortened): ```json { "window": { "day": "2026-10-14", "timezone": "UTC", "nextWindowStart": "2026-10-15T00:00:00.000Z" }, "axes": [ { "scope": "global", "label": "the gateway-wide daily limit", "limit": 500, "used": 120, "remaining": 380, "exhausted": false } ], "effective": { "scope": "global", "remaining": 380 }, "unresolved": ["transport"], "exhausted": false, "warmup": null, "deferredCount": 0 } ``` `effective: null` with an empty `unresolved` means no limit is set. `provider` is optional (`resend`, `smtp`, `brevo`, `smtp2go`, `outreach`, `ses`); an unknown one answers 400. #### The safety model - **Approval digest.** A launch carries the digest a person saw. Nexmail recomputes it from its own data and refuses if anything moved. It proves what was approved; the approver’s name is your word, recorded as `crm_assertion`. - **Never lose mail.** Every message is saved before it is queued. When no provider is available, mail waits in the queue; it is not marked failed. - **Suppression.** Unsubscribed, bounced and complained addresses are never mailed, even if you add them. If the list cannot be read, bulk mail is held rather than sent blind. - **Daily ceilings.** Per organization, per provider and per domain, plus a warm-up ramp for new domains. Mail over a ceiling waits for the next window; it does not fail and does not use up retries. - **Emergency stop.** An administrator can stop outgoing mail in the app. It acts as a ceiling of zero: `send-limits` shows an axis with `scope: "stop"`, `limit: 0`, `exhausted: true`, and mail waits until it is lifted. A credential cannot lift it. - **Content checks.** Executable attachments, links that lie about where they go and spoofed sender names are refused before sending. - **Rate limits.** Test sends: 10 per person per hour (429 with `retry-after`). - **Audit log.** Every create, edit, addition, launch and refusal is recorded against the credential. #### Recommended workflow 1. **Check limits.** `GET /api/campaigns/send-limits?from=…`. If `exhausted` is true, tell the person mail will wait. 2. **Create the draft.** `POST /api/campaigns`. Save the `id`. 3. **Add recipients.** `POST /api/campaigns/:id/recipients` in pages of up to 5,000. Report `suppressed` to the person. 4. **Preview.** `GET /api/campaigns/:id/preview` and `POST /api/campaigns/:id/preview` for one or two recipients. Fix every missing field and refusal by editing the draft. 5. **Test send.** Ask the person to send themselves a test from the campaign page in Nexmail and read it in their inbox. 6. **Human approval.** Read `GET /api/campaigns/:id?approvalDigest=1`. Show the person the subject, body preview, recipient count and every warning. Record who approved, and when. 7. **Send.** `POST /api/campaigns/:id/send` with the `authorisation` block and the digest from step 6. Expect 202. 8. **Watch.** Poll `GET /api/campaigns/:id` and report progress. On a 422, follow the table above; do not loop. #### Not yet available These are not open to credentials today. Do not call them or invent paths for them: - Reading the inbox, conversations or messages. - Replying to or composing individual emails. - Contacts and contact lists. - Settings, domains, providers, routes and teammates. - An MCP server. It is planned, not built. #### For language models - This page as Markdown: [/docs/ai-agents.md](https://nexmail.nexprove.com/docs/ai-agents.md). - A summary of Nexmail and links to every page: [/llms.txt](https://nexmail.nexprove.com/llms.txt). - Everything in one file, including this page: [/llms-full.txt](https://nexmail.nexprove.com/llms-full.txt).