API Reference
Quickstart
Authenticate, load your audience, and preview your first send in about five minutes. Every request below is real: run them in order against your own team and you end with a drafted newsletter and a previewed — not sent — delivery.
https://api.mailneo.co/api/v21. Get your API key
Create a key under Settings → API keys. The API needs the Business plan; the secret is shown once, at creation, and stored hashed — if you lose it, rotate the key. Live keys start mk_live_, test keys mk_test_ — the two environments are strictly separate, and test keys can never send mail.
A key holds only the scopes you grant it. Start read-only (lists:read, subscribers:read, newsletters:read) and widen the grant when a request answers insufficient_scope — the error names the exact scope it needs. To run every step on this page you will eventually want lists:write, subscribers:write, newsletters:write and newsletters:send. The full model is on the authentication page.
2. Make your first call
GET /me needs no scope at all, which makes it the right first call: it tells you which team the key belongs to, which scopes it actually has, and which API version it is pinned to.
curl "https://api.mailneo.co/api/v2/me" \
-H "x-api-key: $MAILNEO_API_KEY"{
"data": {
"object": "api_key",
"id": "...",
"key_id": "mk_live_a1b2c3d4e5",
"environment": "LIVE",
"team": { "id": "..." },
"role": "ADMIN",
"role_at_issue": "ADMIN",
"api_version": "2026-08-15",
"scopes": ["lists:read", "lists:write", "subscribers:read", "subscribers:write"],
"granted_scopes": ["lists:read", "lists:write", "subscribers:read", "subscribers:write"]
},
"meta": { "request_id": "req_..." }
}Every success has this envelope: the payload under data, everything about the response under meta. meta.request_id is what support asks for.
3. Create a list and add subscribers
Newsletters go to the subscribers of a list, so build that first: create the list, create a subscriber, attach one to the other. A newly created subscriber is on nolist until you attach them — and the attach route lets the list's own opt-in setting decide how the subscription starts.
# A list to hold the audience (lists:write)
curl -X POST "https://api.mailneo.co/api/v2/lists" \
-H "x-api-key: $MAILNEO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Product updates", "optin": "SINGLE" }'
# A subscriber (subscribers:write). The Idempotency-Key makes the
# write safe to retry on a timeout.
curl -X POST "https://api.mailneo.co/api/v2/subscribers" \
-H "x-api-key: $MAILNEO_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: quickstart-ada-2026-08-04" \
-d '{ "email": "ada@example.com", "first_name": "Ada" }'
# Attach the subscriber to the list (lists:write)
curl -X POST "https://api.mailneo.co/api/v2/lists/$LIST_ID/subscribers" \
-H "x-api-key: $MAILNEO_API_KEY" \
-H "Content-Type: application/json" \
-d "{ \"subscriber_ids\": [\"$SUBSCRIBER_ID\"] }"The Idempotency-Key header makes the create safe to retry: the same key replays the original response instead of writing twice. Details on the idempotency page. A duplicate email answers 409 resource_already_exists — the remedy is to PATCH the existing subscriber, not re-POST it.
4. Draft a newsletter
POST /newsletters creates a one-off broadcast, always in the DRAFT state.
curl -X POST "https://api.mailneo.co/api/v2/newsletters" \
-H "x-api-key: $MAILNEO_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: quickstart-first-issue" \
-d '{
"name": "First issue",
"subject": "Hello from the Mailneo API",
"from_email": "news@yourdomain.com",
"body": "<h1>It works</h1><p>Drafted over the API.</p>"
}'status, send_at and send_days are refused by name with a 400, never silently ignored — scheduling or starting a send belongs to newsletters:send and its own preview/confirm flow, which is the next step. The sending page explains why.One thing still happens in the app: open the draft and attach your list as its audience. List targeting through the API is planned as its own sub-resource; until it ships, a draft created here has no recipients.
5. Preview the send
POST /newsletters/{newsletterId}/send-preview resolves exactly what a send would do — recipients remaining after opt-in and suppression filtering, credits required, daily-cap headroom, the complaint breaker — runs every send gate read-only, and returns the impact plus a single-use confirmation token bound to that exact plan. Nothing is scheduled or sent by this call.
# No body. The Idempotency-Key header is REQUIRED on this endpoint.
curl -X POST "https://api.mailneo.co/api/v2/newsletters/$NEWSLETTER_ID/send-preview" \
-H "x-api-key: $MAILNEO_API_KEY" \
-H "Idempotency-Key: quickstart-preview-1"{
"data": {
"object": "newsletter_send_preview",
"newsletter_id": "...",
"recipients": { "total": 1, "not_opted_in": 0, "suppressed": 0, "eligible": 1 },
"credits_required": 1,
"send_cap": { "limit": 2000, "used_today": 0, "remaining": 2000 },
"from_address": "news@yourdomain.com",
"sending_account_address": "news@yourdomain.com",
"complaint_rate": { "rate": 0, "threshold": 0.002, "window_days": 30 },
"confirmation": { "token": "...", "expires_at": "2026-08-04T12:15:00Z" }
},
"meta": { "request_id": "req_..." }
}POST /newsletters/{newsletterId}/send with the confirmation_token — made once a human has read the numbers above. The token expires after 15 minutes and is invalidated by any change to the plan, so the send that happens is provably the send that was shown. The full flow, including every gate that can refuse it, is on the sending page.6. Confirm it worked
Read your work back. The draft is in the newsletter list with "status": "DRAFT"; once you confirm a send and it completes, the same endpoint carries its counts, and GET /analytics/overview (scope analytics:read) reports delivery and engagement event totals.
curl "https://api.mailneo.co/api/v2/newsletters?limit=5&sort=updated_at&order=desc" \
-H "x-api-key: $MAILNEO_API_KEY"Next steps
The endpoint reference
Every endpoint, parameter and response schema, generated from the API's OpenAPI specification — it describes exactly what is deployed, not a copy that can fall behind.