A REST API for everything
Add contacts from your website, send order confirmations, start automations and pull reports — with simple JSON over HTTPS and signed webhooks back to your app.
Your first request in a minute
- Create an API key in Developers → API keys (read-only or full access)
- Send it as a Bearer token; all requests and responses are JSON
- Errors return a clear message and the fields that need fixing
- Lists are paginated with
pageandpageSize - Rate limits depend on your plan; unusual traffic is flagged automatically
curl https://app.mailnesto.com/api/v1/contacts \
-H "Authorization: Bearer $MAILNESTO_KEY" \
-H "Content-Type: application/json" \
-d '{"email":"asha@example.com","firstName":"Asha","tags":["website"]}'What you can do with the API
Base URL https://app.mailnesto.com/api/v1 — the complete reference is in the app under Developers.
- GET/contacts
- POST/contacts
- PATCH/contacts/:id
- POST/contacts/bulk
- GET/lists
- POST/lists
- GET/segments
- GET/fields
- GET/campaigns
- POST/campaigns
- POST/campaigns/:id/send
- GET/campaigns/:id/report
- POST/transactional/send
- POST/transactional/batch
- GET/transactional/messages
- GET/automations
- POST/automations/:id/enroll
- GET/forms/:id/submissions
- POST/orders
- POST/carts
- GET/orders
Real-time events, signed and retried
We POST JSON to your URL the moment something happens, with an HMAC-SHA256 signature in the X-MailNesto-Signature header. Failed deliveries are retried with back-off.
contact.createdcontact.subscribedcontact.updatedcontact.tag_addedcontact.tag_removedcontact.list_leftcontact.unsubscribedform.submittedemail.openedemail.clickedemail.bouncedemail.complainedcampaign.sentorder.createdtransactional.senttransactional.failedAn MCP server for AI assistants
Connect Claude or any assistant that speaks the Model Context Protocol to https://app.mailnesto.com/api/v1/mcp with an API key. It can find contacts, add them to lists, read lists, segments and campaign reports, draft campaigns for a person to review, and send transactional email — with exactly the permissions and limits of the key.
search_contactsadd_contact_to_listlist_listslist_segmentscreate_draft_campaignget_campaign_reportsend_transactional_emailBuilding on the API
The MailNesto REST API lets your own code do what your team does in the app: add and update contacts, record orders and carts, start automations, send campaigns and transactional email, and read reports. Requests and responses are JSON over HTTPS, authenticated with an API key sent as a Bearer token. Webhooks work the other way, posting signed events to your server as things happen.
Below is what matters after the first request: what each key can reach, how repeated contacts and orders behave, the rules for transactional messages, verifying webhooks and reading errors. Every field is in the full reference, in the app under Developers → API reference.
API keys and what they can reach
Owners, admins and members with the developer role create keys under Developers → API keys. A key is either read-only or full access. Read-only keys can fetch data but change nothing. Full-access keys can work with contacts, lists, campaigns, automations, orders and transactional email, but can’t change team members, billing, workspace settings or sending setup, can’t read the audit log, and can’t create keys or manage webhooks. Those stay with people signed in to the app.
The full key is shown once, when you create it; MailNesto keeps only a hash and the first few characters so you can tell keys apart. A key can be limited to IP addresses or ranges (for example 203.0.113.0/24), and a request from anywhere else gets a 403 even with the right key. Revoking a key takes effect on the next request.
- Send the key as Authorization: Bearer followed by the key, on every request
- Give each system — website, ERP, ticketing app — its own key, so one can be revoked without breaking the rest
- Keep keys on your server; never put them in browser JavaScript or a mobile app
Contacts, orders, carts and automations
POST /contacts creates a contact with an email, names, custom field values (attributes), up to 50 tags and the lists to join. If the address already exists you get 409 Conflict with the existing contact’s id, so a sign-up handler can fall back to PATCH /contacts/:id. Creating a contact, adding a tag or joining a list fires the same automation triggers as doing it in the app, so a welcome series starts by itself.
POST /orders records an order from your own checkout, with totals in rupees or another currency. Orders are matched on your order id: sending the same order again updates it rather than counting it twice, and the order.created webhook fires only the first time. If the buyer clicked a campaign in the five days before ordering, the revenue is credited to it. POST /carts records a cart in progress for abandoned-cart automations, and an order with the same cart id marks the cart recovered.
To put people into a journey directly, POST /automations/:id/enroll accepts email addresses, contact ids, a list or a segment. The automation must be active, and the response says how many contacts matched, how many were enrolled and how many were skipped.
Receipts, resets and one-time passwords
POST /transactional/send sends one message. Give it a recipient, a subject and HTML or text, or a templateId with a data object to fill the template’s merge tags. It returns 202 Accepted with the stored message, then sends from a queue through your workspace’s delivery servers with DKIM signing. POST /transactional/batch takes up to 100 messages, validates each separately and returns 207 with a result for each, so one bad address doesn’t sink the rest.
Transactional messages ignore marketing unsubscribes — someone who left your newsletter still needs their receipt — but never go to suppressed or blocklisted addresses; those come back with the status suppressed and a reason. Pass an idempotencyKey, such as your order id, and a retried request returns the original message instead of sending a second copy.
- sendAt schedules a message up to 72 hours ahead
- Up to 10 attachments, 7 MB in total, base64-encoded; executable files are refused
- tag and metadata let you filter the message log and match messages to your own records
- trackOpens and trackClicks can be turned off per message, which suits one-time passwords
- GET /transactional/messages filters by status: queued, sent, failed, suppressed, bounced or complained
Receiving and verifying webhooks
Add an endpoint under Developers → Webhooks and choose its events. MailNesto then POSTs a JSON body with the event name, a timestamp, your workspace id and a data object. Each request carries headers with the event name, a unique delivery id and a signature in the form t=timestamp,v1=hex. The v1 value is an HMAC-SHA256 of the timestamp, a full stop and the raw body, keyed with the endpoint’s secret.
To verify, read the raw body before parsing it as JSON, join the timestamp and body with a full stop, compute the HMAC with your secret and compare it with v1 in constant time. Reject requests whose timestamp is more than a few minutes old to stop replays, and store the delivery id so a retried delivery is processed only once.
Reply with a 2xx status within 10 seconds. Any other status, a redirect or a timeout counts as a failure and is retried up to five more times, roughly 30 seconds, 1, 2, 4 and 8 minutes apart. After 50 failures in a row the endpoint is paused and owners, admins and developers are notified. Every attempt is in the delivery log with a retry button.
- Up to 20 endpoints per workspace, each with its own events and secret
- A campaign can also have its own webhook for that campaign’s opens, clicks, bounces and unsubscribes
- Addresses on private or internal networks are refused; you can send a test ping or rotate the secret at any time
Rate limits, errors and paging
Errors come back as JSON with a plain-English message. Validation errors use 400 and add a fields object naming each field that needs fixing, so you can show the message beside the right input. The status code tells you what to do next.
Requests are limited per minute to keep the service fair for everyone. Spread bulk work out, use POST /contacts/bulk to tag, list, unsubscribe or delete up to 5,000 contacts in one call, and retry 429 and 5xx responses with a growing delay. List endpoints are paginated with page and pageSize (up to 200 a page) and return the total, so you can loop until you have everything.
- 400: the body or query is invalid; fix the fields listed
- 401: the key is missing, wrong or revoked
- 402: a plan limit was reached, such as contacts or emails a month; the message says which
- 403: the key isn’t allowed to do this, can’t be used from this IP address, or the workspace is suspended
- 404: the record doesn’t exist in this workspace
- 409: a conflict, such as a contact that already exists or enrolling into an automation that isn’t active
- 429: too many requests in a short time; wait and retry with back-off
Developer questions
Which events can a webhook receive?
Contact created, joined a list, left a list, details changed, tag added or removed, and unsubscribed; form submitted; email opened, clicked, bounced or marked as spam; campaign finished sending; order received; and transactional email sent or failed.
How do I stop the same receipt being sent twice?
Send an idempotencyKey with each transactional message, such as your order or payment id. If your code retries after a timeout, MailNesto returns the message it already accepted instead of sending a duplicate.
Can I email people who unsubscribed from my newsletter?
With transactional email, yes, for messages they need, such as receipts, OTPs and booking confirmations. Marketing unsubscribes don’t block those. Addresses on your suppression list or the platform blocklist are never emailed.
My server was down for an hour. Did I lose webhooks?
Automatic retries stop after about 15 minutes, so some deliveries will be marked failed. Open the endpoint’s delivery log, filter by failed, and retry them. They are sent with their original payload and delivery id.
How should I send order totals?
Send totals and line prices in rupees as ordinary numbers, for example 1499.50, with a currency code if it isn’t INR. MailNesto stores them precisely and shows revenue in reports in ₹.