Postbox docs

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"
      }'
FieldRequiredNotes
domainIdyesThe domain's id, not its name.
localPartyesThe part before the @. Trimmed and lowercased for you.
displayNameyesHuman label, shown as the From name on outbound mail.
quotanoStorage cap in bytes.
groupIdnoAttach 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 paramValuesNotes
folderfolder nameDefaults to every folder.
readtrue / falseFilter by read state.
starredtrue / falseFilter by starred state.
qstringSubstring match on the message.
threadedtrue / falseGroup into threads instead of a flat list.
page, pageSizedigitsPagination.

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.