Mailboxes
Provision real addresses, list and read their mail, and organise it with folders.
A mailbox is a real, addressable inbox: it sends, receives, threads, stores, and has folders. Creating one is a single call.
Create
import { Postbox } from "@postbox/sdk";
const pb = new Postbox({
apiKey: process.env.PB_KEY!,
projectId: process.env.PROJECT_ID!,
});
const mailbox = await pb.mailboxes.create({
domainId: "dom_...",
localPart: "support",
displayName: "Acme Support",
});import os
from postbox import Postbox
pb = Postbox(api_key=os.environ["PB_KEY"], project_id=os.environ["PROJECT_ID"])
mailbox = pb.mailboxes.create(
{
"domainId": "dom_...",
"localPart": "support",
"displayName": "Acme Support",
}
)using Postbox;
var pb = new PostboxClient(
new PostboxOptions
{
ApiKey = Environment.GetEnvironmentVariable("PB_KEY")!,
ProjectId = Environment.GetEnvironmentVariable("PROJECT_ID")!,
}
);
var mailbox = await pb.Mailboxes.CreateAsync(
new MailboxesCreateBody
{
DomainId = "dom_...",
LocalPart = "support",
DisplayName = "Acme Support",
}
);import cloud.postboxapp.*;
import cloud.postboxapp.gen.*;
import java.util.*;
Postbox pb = new Postbox(PostboxOptions.builder()
.apiKey(System.getenv("PB_KEY"))
.projectId(System.getenv("PROJECT_ID"))
.build());
var mailbox = pb.mailboxes.create(MailboxesCreateBody.builder()
.domainId("dom_...")
.localPart("support")
.displayName("Acme Support")
.build());import postbox "github.com/postboxapp/postbox-go"
pb, err := postbox.New(postbox.Options{
APIKey: os.Getenv("PB_KEY"),
ProjectID: os.Getenv("PROJECT_ID"),
})
ctx := context.Background()
mailbox, err := pb.Mailboxes.Create(ctx, &postbox.MailboxesCreateBody{
DomainId: "dom_...",
LocalPart: "support",
DisplayName: "Acme Support",
})curl -X POST https://api.postboxapp.cloud/v1/mailboxes \
-H "Authorization: Bearer $PB_KEY" \
-H "X-Project-Id: $PROJECT_ID" \
-H "Content-Type: application/json" \
-d '{
"domainId": "dom_...",
"localPart": "support",
"displayName": "Acme Support"
}'| Field | Required | Notes |
|---|---|---|
domainId | yes | The domain's id, not its name. |
localPart | yes | The part before the @. Trimmed and lowercased for you. |
displayName | yes | Human label, shown as the From name on outbound mail. |
quota | no | Storage cap in bytes. |
groupId | no | Attach to a mailbox group, which is where send rate limits live. |
The domain must be active first. Creating support on mail.acme.com gives you
[email protected], live immediately.
A duplicate localPart on the same domain returns 409.
List and fetch
const mailboxes = await pb.mailboxes.list();
const mailbox = await pb.mailboxes.get(mailboxId);mailboxes = pb.mailboxes.list()
mailbox = pb.mailboxes.get(mailbox_id)var mailboxes = await pb.Mailboxes.ListAsync();
var mailbox = await pb.Mailboxes.GetAsync(mailboxId);var mailboxes = pb.mailboxes.list();
var mailbox = pb.mailboxes.get(mailboxId);mailboxes, err := pb.Mailboxes.List(ctx)
mailbox, err := pb.Mailboxes.Get(ctx, mailboxId)curl https://api.postboxapp.cloud/v1/mailboxes \
-H "Authorization: Bearer $PB_KEY" -H "X-Project-Id: $PROJECT_ID"
curl https://api.postboxapp.cloud/v1/mailboxes/$MAILBOX_ID \
-H "Authorization: Bearer $PB_KEY" -H "X-Project-Id: $PROJECT_ID"{
"success": true,
"data": {
"id": "mbx_...",
"email": "[email protected]",
"displayName": "Acme Support",
"status": "active",
"createdAt": "2026-07-16T09:12:00.000Z"
}
}Read its mail
Listing messages accepts a pb_tok_ mailbox token as well as a project key:
const messages = await pb.mailboxes.listMessages(mailboxId, {
folder: "inbox",
read: "false",
page: 1,
pageSize: 25,
});messages = pb.mailboxes.list_messages(
mailbox_id,
query={
"folder": "inbox",
"read": "false",
"page": "1",
"pageSize": "25",
},
)var messages = await pb.Mailboxes.ListMessagesAsync(
mailboxId,
new MailboxesListMessagesQuery
{
Folder = "inbox",
Read = "false",
Page = "1",
PageSize = "25",
}
);var messages = pb.mailboxes.listMessages(mailboxId, MailboxesListMessagesQuery.builder()
.folder("inbox")
.read("false")
.page("1")
.pageSize("25")
.build());messages, err := pb.Mailboxes.ListMessages(ctx, mailboxId, &postbox.MailboxesListMessagesQuery{
Folder: "inbox",
Read: "false",
Page: "1",
PageSize: "25",
})curl "https://api.postboxapp.cloud/v1/mailboxes/$MAILBOX_ID/messages?folder=inbox&read=false&page=1&pageSize=25" \
-H "Authorization: Bearer $PB_KEY"| Query param | Values | Notes |
|---|---|---|
folder | folder name | Defaults to every folder. |
read | true / false | Filter by read state. |
starred | true / false | Filter by starred state. |
q | string | Substring match on the message. |
threaded | true / false | Group into threads instead of a flat list. |
page, pageSize | digits | Pagination. |
Filtering by text
q is a substring filter over the message, meant for narrowing a list rather
than ranked relevance search. Combine it with folder, read, and the other
filters to scope what you fetch.
Fetch or delete a single message:
const message = await pb.messages.get(messageId);
await pb.messages.delete(messageId);message = pb.messages.get(message_id)
pb.messages.delete(message_id)var message = await pb.Messages.GetAsync(messageId);
await pb.Messages.DeleteAsync(messageId);var message = pb.messages.get(messageId);
pb.messages.delete(messageId);message, err := pb.Messages.Get(ctx, messageId)
_, err = pb.Messages.Delete(ctx, messageId)curl https://api.postboxapp.cloud/v1/messages/$MESSAGE_ID \
-H "Authorization: Bearer $PB_KEY"
curl -X DELETE https://api.postboxapp.cloud/v1/messages/$MESSAGE_ID \
-H "Authorization: Bearer $PB_KEY"Reading needs receive. Deleting needs manage.
Update and delete
await pb.mailboxes.update(mailboxId, {
displayName: "Acme Help",
groupId: null,
});pb.mailboxes.update(
mailbox_id,
{
"displayName": "Acme Help",
"groupId": None,
},
)await pb.Mailboxes.UpdateAsync(
mailboxId,
new MailboxesUpdateBody { DisplayName = "Acme Help", GroupId = null }
);pb.mailboxes.update(mailboxId, MailboxesUpdateBody.builder()
.displayName("Acme Help")
.groupId(null)
.build());_, err := pb.Mailboxes.Update(ctx, mailboxId, &postbox.MailboxesUpdateBody{
DisplayName: "Acme Help",
GroupId: nil,
})curl -X PATCH https://api.postboxapp.cloud/v1/mailboxes/$MAILBOX_ID \
-H "Authorization: Bearer $PB_KEY" -H "X-Project-Id: $PROJECT_ID" \
-H "Content-Type: application/json" \
-d '{
"displayName": "Acme Help",
"groupId": null
}'displayName, quota, status, and groupId are updatable. Setting groupId
to null detaches the mailbox from its group, which also detaches its send rate
limits.
Folders
Every mailbox has folders, and inbound mail is filed into one by your inbound policy.
const folders = await pb.mailboxes.listFolders(mailboxId);
const folder = await pb.mailboxes.createFolder(mailboxId, { name: "Receipts" });folders = pb.mailboxes.list_folders(mailbox_id)
folder = pb.mailboxes.create_folder(mailbox_id, {"name": "Receipts"})var folders = await pb.Mailboxes.ListFoldersAsync(mailboxId);
var folder = await pb.Mailboxes.CreateFolderAsync(
mailboxId,
new MailboxesCreateFolderBody { Name = "Receipts" }
);var folders = pb.mailboxes.listFolders(mailboxId);
var folder = pb.mailboxes.createFolder(mailboxId, MailboxesCreateFolderBody.builder()
.name("Receipts")
.build());folders, err := pb.Mailboxes.ListFolders(ctx, mailboxId)
folder, err := pb.Mailboxes.CreateFolder(ctx, mailboxId, &postbox.MailboxesCreateFolderBody{
Name: "Receipts",
})curl https://api.postboxapp.cloud/v1/mailboxes/$MAILBOX_ID/folders \
-H "Authorization: Bearer $PB_KEY" -H "X-Project-Id: $PROJECT_ID"
curl -X POST https://api.postboxapp.cloud/v1/mailboxes/$MAILBOX_ID/folders \
-H "Authorization: Bearer $PB_KEY" -H "X-Project-Id: $PROJECT_ID" \
-H "Content-Type: application/json" \
-d '{ "name": "Receipts" }'Rename or delete through the folder's own id, which is not nested under the mailbox:
await pb.folders.update(folderId, { name: "Invoices" });
await pb.folders.delete(folderId);pb.folders.update(folder_id, {"name": "Invoices"})
pb.folders.delete(folder_id)await pb.Folders.UpdateAsync(folderId, new FoldersUpdateBody { Name = "Invoices" });
await pb.Folders.DeleteAsync(folderId);pb.folders.update(folderId, FoldersUpdateBody.builder()
.name("Invoices")
.build());
pb.folders.delete(folderId);_, err := pb.Folders.Update(ctx, folderId, &postbox.FoldersUpdateBody{
Name: "Invoices",
})
_, err = pb.Folders.Delete(ctx, folderId)curl -X PATCH https://api.postboxapp.cloud/v1/folders/$FOLDER_ID ...
curl -X DELETE https://api.postboxapp.cloud/v1/folders/$FOLDER_ID ...A duplicate folder name in one mailbox returns 409.
Groups and send limits
A mailbox can belong to a mailbox group, and the group is where hourly, daily, and burst send limits are configured. Use groups to stop a single high-volume mailbox from consuming a domain's whole sending reputation.
A mailbox with no group has no per-mailbox send cap, though your org's plan allowance still applies. See Groups & send limits for the full group API and how limits resolve, and Sending for the send path.