Authenticate
Create a key in the dashboard on Pro or Business. Send it as Authorization: Bearer. The full key is shown once. Every response includes request_id. The API sends X-RateLimit-Limit: 120.
curl https://api.mailbelo.com/v1/mailboxes \
-H "Authorization: Bearer $MAILBELO_KEY"
{
"mailboxes": [],
"next_cursor": null,
"request_id": "…"
}Create a mailbox
POST /v1/mailboxes with local_part. Add domain when the mailbox should live on a domain you have verified. Send Idempotency-Key so a retry returns the first response. Reusing that key with a different body returns 409 idempotency_conflict. The password is in this response only.
curl -X POST https://api.mailbelo.com/v1/mailboxes \
-H "Authorization: Bearer $MAILBELO_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"local_part":"ada","display_name":"Ada"}'{
"mailbox": {
"id": "…",
"address": "[email protected]",
"display_name": "Ada",
"disabled": false,
"quota_bytes": 1073741824,
"password": "shown-once",
"created_at": "2026-10-07T00:00:00+00:00"
},
"request_id": "…"
}List mailboxes
GET /v1/mailboxes?limit=20. limit is between 1 and 100. Pass the returned next_cursor back as cursor to read the next page. A null cursor means you are done.
curl "https://api.mailbelo.com/v1/mailboxes?limit=20&cursor=$CURSOR" \
-H "Authorization: Bearer $MAILBELO_KEY"
{
"mailboxes": [{ "id": "…", "address": "[email protected]", "display_name": "Ada", "disabled": false }],
"next_cursor": null,
"request_id": "…"
}Send a message
POST /v1/mailboxes/{address}/messages with to, subject, and text. html is optional. The address must belong to the key’s organization.
curl -X POST https://api.mailbelo.com/v1/mailboxes/[email protected]/messages \
-H "Authorization: Bearer $MAILBELO_KEY" \
-H "Content-Type: application/json" \
-d '{"to":["[email protected]"],"subject":"Hello","text":"From MailBelo."}'{ "ok": true, "request_id": "…" }Read messages
GET /v1/mailboxes/{address}/messages?limit=20 lists the inbox. GET /v1/mailboxes/{address}/messages/{id} returns one message, including text when the mail server has it.
curl "https://api.mailbelo.com/v1/mailboxes/[email protected]/messages?limit=20" \
-H "Authorization: Bearer $MAILBELO_KEY"
{
"messages": [{
"id": "…",
"subject": "Hello",
"from": "[email protected]",
"preview": "From MailBelo.",
"received_at": "2026-10-07T12:00:00Z",
"unread": true
}],
"request_id": "…"
}Delete a mailbox
DELETE /v1/mailboxes/{address}. Reserved company locals such as support, info, security, abuse, and postmaster return 404. They are not mailboxes you can create or read through this API.
curl -X DELETE https://api.mailbelo.com/v1/mailboxes/[email protected] \
-H "Authorization: Bearer $MAILBELO_KEY"
{ "ok": true, "request_id": "…" }Domains
POST /v1/domains with name. The response includes verification_txt. Publish that TXT record, then POST /v1/domains/{name}/verify. GET /v1/domains lists domains that are not the shared mailbelo.com domain.
curl -X POST https://api.mailbelo.com/v1/domains \
-H "Authorization: Bearer $MAILBELO_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"example.com"}'{
"domain": { "name": "example.com", "verification_txt": "mailbelo-verification=…" },
"request_id": "…"
}Errors
Failures use a JSON object with code, message, and request_id. A missing or reserved mailbox is 404 not_found. Reusing an Idempotency-Key with a different body is 409 idempotency_conflict. A missing or revoked key is unauthorized.
{ "error": { "code": "not_found", "message": "Mailbox not found", "request_id": "…" } }{ "error": { "code": "idempotency_conflict", "message": "This key was used with a different request", "request_id": "…" } }The same error object is on every failure. GET /v1/errors lists the codes. GET /v1/openapi.txt lists the paths. Send can take the same Idempotency-Key header as create. The clients retry 429 and 5xx responses.
Webhook deliveries send MailBelo-Signature, the hex HMAC-SHA256 of the raw JSON body, signed with the secret returned when the endpoint is created.
Python and TypeScript
The clients in packages/sdk-python and packages/sdk-ts call the same endpoints.
from mailbelo import MailBelo
client = MailBelo("mb_live_…")
created = client.create_mailbox("ada", idempotency_key="once")
client.send_message(created["mailbox"]["address"], ["[email protected]"], "Hello", "From MailBelo.")
print(client.messages("[email protected]"))
client.delete_mailbox("[email protected]")
domain = client.create_domain("example.com")
client.verify_domain("example.com")import { MailBelo } from "@mailbelo/sdk";
const client = new MailBelo("mb_live_…");
const created = await client.createMailbox("ada");
await client.sendMessage(created.mailbox.address, ["[email protected]"], "Hello", "From MailBelo.");
await client.messages("[email protected]");
await client.deleteMailbox("[email protected]");
await client.createDomain("example.com");
await client.verifyDomain("example.com");