Quickstart
The Mailbox.bot API sends postal mail programmatically. Upload a document (PDF, DOCX, and other supported formats), specify a carrier, and receive tracking numbers, proof-of-fulfillment photos, and delivery confirmations via webhook. Mail is printed and dispatched from licensed facilities.
sk_agent_test_) work on production endpoints โ real document validation, real cost previews, signed webhooks. Zero code changes when you go live.The core workflow is one multipart request: upload the document, include the recipient address, set a cost cap, and optionally use dry_run=true first for a no-charge quote.
Have a member key? Use sk_live_ for setup, then create an agent credential with mail.send. Use the returned sk_agent_ or sk_agent_test_ key for this request.
Inbound thread fields are optional advanced context. Leave them out for normal outbound sends.
curl -X POST https://mailbox.bot/api/v1/mail \ -H "Authorization: Bearer sk_agent_test_..." \ -H "X-Mailbox-MD-Version: 3" \ -H "X-Max-Cost-Cents: 1500" \ -F "document=@notice.pdf" \ -F "recipient_name=State of Delaware" \ -F "recipient_line1=401 Federal Street, Suite 4" \ -F "recipient_city=Dover" \ -F "recipient_state=DE" \ -F "recipient_zip=19901" \ -F "mail_class=certified_return_receipt" \ -F "dry_run=true"
Postcards and mailers โ marketing campaigns, appointment reminders, thank-you cards. Design your document, we print and mail it.
Batch mail โ upload a CSV of recipients and a template document. Send hundreds or thousands of identical pieces in one job with volume-discounted pricing (5% off at 500+, 10% at 1,000+, 15% at 5,000+).
Overnight and express โ FedEx Overnight, FedEx 2Day, UPS Next Day, UPS Ground. Same API, same workflow โ just change the
mail_class field.mail.submitted2 Printed and prepared โ the facility prints your document, photographs the printed pages and sealed envelope as proof of fulfillment, and marks it ready for dispatch. Webhook:
mail.ready with fulfillment_photos3 Mailed โ dispatched via your chosen carrier. You receive a carrier-format tracking number, dispatch method (post office drop-off, carrier pickup), and an optional postage receipt photo. Webhook:
mail.mailed with tracking_number + carrier4 Delivered โ final confirmation. If delivery proof is captured, it is included in
fulfillment_photos. Webhook: mail.deliveredEvery webhook is HMAC-SHA256 signed. Your dashboard shows a visual progress tracker with all fulfillment photos, tracking info, and timestamps at each stage.
$1.00. Extra pages still add normal per-page printing and any additional postage from weight.Per-page printing โ charged per page (B&W or color). No flat fees, no minimums.
Actual carrier postage โ calculated from page count, weight, destination ZIP, and mail class. You pay what USPS/FedEx/UPS charges.
Immediate Stripe charge โ charged at submission via PaymentIntent. No surprise invoices. Every response includes
cost_cents, cost_display, and a full cost_breakdown so you know exactly what was charged and why.Dry-run cost preview โ send
dry_run=true with your real document to get an exact cost breakdown without creating a record or charging. Use this to confirm pricing before committing.Sandbox cost preview โ test keys return
estimated_live_cost_cents + cost_breakdown with no actual charge, so you can verify pricing before going live.sk_agent_test_) works on the same endpoints with the same request format โ the only differences are no Stripe charge and no postal mail. Everything else is real:Document validation โ your document is parsed, page-counted, and checked for dimensions just like production.
Accurate cost calculation โ real per-page printing + real carrier postage rates returned as
estimated_live_cost_cents with a full breakdown.HMAC-signed webhooks โ your webhook endpoint receives signed payloads at every lifecycle step so you can validate your signature verification.
Always-present test_mode field โ every response includes
test_mode: true or test_mode: false โ never omitted. Plus an X-Test-Mode response header for middleware detection without parsing the body.Simulated facility fulfillment โ advance your test record through the lifecycle and receive fulfillment photos, carrier-format tracking numbers (USPS, FedEx, UPS), dispatch confirmations, and delivery status โ exactly matching what production webhooks deliver.
Dashboard verification โ test records appear in your dashboard Webhooks tab with delivery status, payload inspection, and attempt-by-attempt debugging. The Mail tab shows the full progress tracker with photos, tracking, and timestamps.
Go live โ swap
sk_agent_test_ for sk_agent_. Zero code changes.Real mailing and package address reservation reservations open โ mailbox.bot-issued street address + mailbox number, package handling, scan/photo intake, and agent notifications are separate services that require identity verification and postal authorization where applicable. Approved address issuance begins August 2026.
Standing instructions โ set rules so your agent handles mail automatically (scan all envelopes, forward packages over 2 lbs, shred junk).
Multi-protocol support โ REST, MCP (Model Context Protocol), A2A (Google Agent-to-Agent), OpenClaw. Same capabilities across all protocols.
Approval workflows โ require human approval in the dashboard before mail is printed, if your use case needs a human in the loop.
Authentication
All requests require a Bearer token. Get your API key from the dashboard after onboarding.
Authorization: Bearer sk_agent_xxxxxxxxxxxxx
sk_live_) โ full account access, all scopes, queries span all agentsAgent keys (
sk_agent_) โ scoped to a single agent. This is the key type you give to your AI agent.Test keys (
sk_agent_test_) โ same as agent keys but activates sandbox mode. No charges, full validation. Swap to a live key when ready.Facility keys (
sk_facility_) โ scoped to a facility for external scanner appsErrors & Rate Limits
{
"error": {
"type": "invalid_request",
"message": "Agent name already in use",
"code": "agent_name_taken"
}
}Rate limits: 100 req/min per API key (standard), custom limits for enterprise.
Webhook Security
Every webhook includes an X-Mailbox-Signature header containing an HMAC-SHA256 signature. Create signing keys via the API and rotate them without downtime.
whsk_prefix:t=<timestamp>,v1=<hmac-hex>. Compute the digest over <timestamp>.<raw_json_body> using the raw 64-char secret. The whsk_ prefix identifies the key; do not include it in the HMAC input.import crypto from "node:crypto";
export function verifyMailboxWebhook(rawBody, signature, secret) {
const match = signature.match(/^([^:]+):t=(\d+),v1=([a-f0-9]+)$/);
if (!match) return false;
const [, keyPrefix, timestamp, expected] = match;
const signedPayload = `${timestamp}.${rawBody}`;
const digest = crypto
.createHmac("sha256", secret)
.update(signedPayload)
.digest("hex");
return keyPrefix.startsWith("whsk_") &&
crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(expected));
}Agents
member โ agentEndpoints
member โ agent โ endpointOutbound Mail
POST /v1/mail charges the member's card on file immediately. Four safeguards:X-Max-Cost-Cents: 1500 header. If the computed cost exceeds it, the request is rejected (422) before any charge โ no record created, no money moved.2. Dry run โ send
dry_run=true to validate your document and get an exact cost breakdown without creating a record or charging.3. Approval flow โ send
requires_approval=true to defer charging until the member approves in their dashboard.4. Test keys โ use an
sk_agent_test_ key during development. Identical flow, zero charges. Swap to a live key when ready.5. Platform limits โ per-transaction and daily spend limits are enforced server-side. If a charge exceeds either limit, the request is rejected (422) before any charge. Contact support if you need higher limits.
Upload a document as multipart form data. Supported local-only formats are PDF, DOCX, JPG, PNG, TXT, and CSV. The original file is stored in mailbox.bot storage and previewed in a same-origin print view. PDF is WYSIWYG; DOCX may require an explicit page_count when embedded Office page metadata is missing. Requires an agent-scoped key with mail.send scope and X-Mailbox-MD-Version header.
POST /v1/mail
Authorization: Bearer sk_agent_...
X-Mailbox-MD-Version: 3
X-Max-Cost-Cents: 1500
Content-Type: multipart/form-data
document: (PDF, DOCX, JPG, PNG, TXT, or CSV file, max 10MB)
recipient_name: State of Delaware
recipient_line1: 401 Federal Street, Suite 4
recipient_city: Dover
recipient_state: DE
recipient_zip: 19901
mail_class: certified_return_receipt
page_count: 6
dry_run: true โ preview cost without chargingdocument โ supported formats: PDF, DOCX, JPG, PNG, TXT, CSVpage_count โ optional explicit page count for non-PDF uploads; when supplied for DOCX, TXT, or CSV it overrides local detection and makes pricing deterministicmail_class โ first_class (default), priority, certified, certified_return_receipt, fedex_ground, fedex_express, fedex_2day, fedex_overnight, ups_ground, ups_2day, ups_next_daycolor โ true for color printing (+$0.25/page surcharge)requires_approval โ true to require member dashboard approval before printing (no charge until approved)inbound_capture_id โ optional inbound mail item UUID when this outbound piece is replying to forwarded inbound contextpostal_mail_thread_id โ optional physical-mail thread UUID to keep inbound review and outbound send in one workflowdry_run โ true to validate and get cost breakdown without creating a record or chargingmetadata โ JSON object echoed in every response and webhook payloadcost_breakdown with line-item detail: printing per page, postage by class and zone, and color surcharge. Submission responses also include document metadata; pdf_url is only populated for actual PDFs.// Dry-run response (200) โ no charge
{
"cost_cents": 1257,
"cost_display": "$12.57",
"cost_breakdown": {
"printing_cents": 180,
"printing_per_page_cents": 30,
"postage_cents": 1077,
"postage_description": "Certified + Return Receipt",
"page_count": 6,
"weight_oz": 1.43
},
"dry_run": true
}fields[] array) ยท 402 (card declined / no payment method / suspended) ยท 404 (no active mailbox) ยท 409 (MAILBOX.md version mismatch) ยท 422 (cost exceeds X-Max-Cost-Cents, per-transaction limit, or daily spend cap โ no charge made)Inbound Mail Context
draft_context -> draft with the LLM -> send outbound mail with inbound_capture_id and, when present, postal_mail_thread_id.Packages โ real mailing address beta
member โ agent โ endpoint โ package{
"event": "package.received",
"package_id": "pkg-uuid",
"mailbox_id": "MB-7F3A2K9P",
"suite": "7F3A",
"agent": "procurement-bot",
"carrier": "fedex",
"tracking": "794644790132",
"weight_oz": 12.4,
"photos": [
"https://cdn.mailbox.bot/pkg/7F3A2K9P_001.jpg"
],
"received_at": "2026-02-09T14:32:00Z"
}Forwarding โ real mailing address beta
Actions โ real mailing address beta
Scanning โ real mailing address beta
/v1/inbound. BILLABLEAgent Memory โ account-enabled
Standing Instructions โ account-enabled
Webhook Delivery Settings
{
"agent_id": "agent-uuid",
"webhook_url": "https://yourapp.com/webhooks/mailbox",
"enabled": true,
"event_types": [
"mail.submitted",
"mail.ready",
"mail.mailed",
"mail.delivered"
]
}Use ["*"] for all events. Setting webhook_url on an agent with no signing key returns a one-time webhook_signing_key for HMAC verification.
{
"agent_id": "agent-uuid",
"auth_type": "bearer",
"auth_token": "oc_live_abc123...",
"payload_format": "openclaw"
}Auth types: hmac (default โ HMAC-SHA256 signature) bearer (Authorization header) header (custom header name)
Payload formats: standard (raw JSON) openclaw (wrapped as { message, agentId, deliver })
Browse recent webhook deliveries for your account. Agent-scoped keys auto-filter to their own events. Returns event type, delivery status, and timestamps.
status โ filter by delivery status: delivered pending failedevent_type โ e.g. mail.mailedagent_id โ filter by agent (member keys only)limit / offset โ pagination (max 100){
"events": [
{
"id": "evt_550e8400-...",
"event_type": "mail.mailed",
"status": "delivered",
"created_at": "2026-04-30T18:00:00Z",
"delivered_at": "2026-04-30T18:00:01Z"
}
],
"pagination": { "total": 42, "has_more": true }
}Scope: webhook.read. Both sandbox and live events appear here.


sk_agent_test_ key or POST /v1/test/webhook) shows up here immediately. Click any row to see the full JSON payload and every delivery attempt with HTTP status code, response time, and error details.Live events โ production webhooks from real mailings appear in the same view. Filter by status to find failures, then inspect the delivery attempt to see exactly what your server returned.
Sandbox
POST /v1/agents/:id/credentials with "environment": "test". You get an sk_agent_test_ key.2. Use the same endpoints โ sandbox keys work on every production endpoint (mail, packages, actions, agents, webhooks). Same routes, same headers, same body shape. The four
/v1/test/* helpers below exist only to synthesize state you can't produce locally (an address/package beta item, a manual webhook fire, a no-PDF mail submission, a lifecycle step).3. No charge โ the response includes
cost_cents: 0 (no Stripe charge) plus estimated_live_cost_cents showing exactly what production would cost, with a full cost_breakdown (printing per page, postage by class, color surcharge).4. Webhooks fire normally โ your webhook endpoint receives HMAC-signed payloads with the same structure as production.
5. Advance through fulfillment โ call
POST /v1/test/mail/:id/advance to step the record through submitted โ ready โ mailed โ delivered. Each step simulates real facility work: fulfillment photos, tracking numbers, dispatch method. Open your dashboard Webhooks tab to watch each event arrive, and the Mail tab to see the progress tracker with photos.6. Go live โ swap
sk_agent_test_ for sk_agent_. No code changes.POST /v1/agents/:id/credentials
Authorization: Bearer sk_live_...
{ "scopes": ["mail.send", "package.read", "webhook.manage"], "environment": "test" }Returns an sk_agent_test_ key. Use it on any production endpoint โ same code, no charge, HMAC-signed webhooks fire normally.
| sk_agent_test_ | sk_agent_ | |
|---|---|---|
| Endpoint | POST /v1/mail | POST /v1/mail |
| Body | Multipart (real document) | Multipart (real document) |
| Document validation | Full validation | Full validation |
| Header | X-Mailbox-MD-Version | X-Mailbox-MD-Version |
| Charge | cost_cents: 0 | Real Stripe charge |
| Cost breakdown | cost_breakdown: {...} | cost_breakdown: {...} |
| Cost estimate | estimated_live_cost_cents | dry_run=true for preview |
| test_mode field | true (always present) | false (always present) |
| X-Test-Mode header | true | false |
| Webhooks | HMAC-signed | HMAC-signed |
| Facility | Not queued | Real fulfillment |
| Webhook logs | Dashboard โ Webhooks | Dashboard โ Webhooks |
| Go live | Swap the key | โ |
These guards apply identically to both sandbox and live keys โ test the integration once and the same protections run in production:
POST /v1/mail to validate the document and return the full cost_breakdown + warnings array without creating a record or charging. Works with any key.X-Max-Cost-Cents โ set this header to a cap (e.g.
5000) and the request rejects with 422 before any charge if the computed cost exceeds it.force_approval โ set on the credential at mint time. Every submission with this key routes to
pending_approval regardless of requires_approval in the body.max_daily_pieces โ set on the credential to cap outbound mail per 24h window per key. Surfaces as a
warnings entry in dry_run; rejects with 422 once exceeded.Dashboard segmentation โ sandbox traffic appears under the orange Sandbox tab on
/dashboard/mail and /dashboard/webhooks. Live and test data never co-mingle in the default views.These endpoints skip document upload entirely โ useful for quick webhook testing and lifecycle exploration.
Discovery & Protocols
/.well-known/agent.json, and REST API.v1.0 โ live