Build with Postcard.bot

Let AI agents send real physical postcards worldwide. Integrate via MCP server or REST API.

MCP Compatible
REST API
From $0.69/postcard
Quick Start
Get up and running in 3 steps
1

Get an API key

Sign up at postcard.bot, go to your account page, and generate an API key in the API Keys tab.

2

Add balance

Add credits via postcard.bot/buy-credits or the POST /api/v1/credits endpoint. Prepaid balance — funds must be available before sending. Volume pricing from $0.69/postcard.

3

Configure your MCP client

Add the MCP server to Claude Desktop, Claude Code, or any MCP-compatible client.

Remote MCP Server
Recommended
Zero setup — just paste a URL and sign in with your Postcard.bot account

The fastest way to get started. No API key needed, no local install — OAuth handles authentication automatically. Works with Claude Desktop, Claude Code, ChatGPT, and any MCP client that supports remote servers.

https://postcard.bot/api/mcp

Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

claude_desktop_config.json
{
  "mcpServers": {
    "postcardbot": {
      "url": "https://postcard.bot/api/mcp"
    }
  }
}

Claude Code

claude mcp add --transport http postcardbot https://postcard.bot/api/mcp

How it works

  1. Add the URL to your MCP client
  2. Your client opens a browser window for you to sign in
  3. Sign in with Google or email (same Postcard.bot account)
  4. Click "Allow" to authorize access
  5. Start sending postcards — your balance and pricing tier apply automatically
Local MCP Server (npm)
Run the MCP server locally with an API key

Install via npm

npx -y @postcardbot/mcp-server

Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

claude_desktop_config.json
{
  "mcpServers": {
    "postcardbot": {
      "command": "npx",
      "args": ["-y", "@postcardbot/mcp-server"],
      "env": {
        "POSTCARDBOT_API_KEY": "pk_live_your_key_here"
      }
    }
  }
}

Claude Code

claude mcp add postcardbot -- npx -y @postcardbot/mcp-server

Then set POSTCARDBOT_API_KEY in your shell environment.

MCP Tools Reference
Tools available through the MCP server

send_postcard

POST

Send a physical postcard. Cards are printed and shipped within 24 hours. Delivery takes 5-10 business days.

Parameters:

  • to — Recipient address (name, address_line1, city required; address_line2, state, zip, country optional)
  • from — Sender/return address (same fields)
  • message — Back-of-card message (max 350 characters)
  • image_url — Front image URL (publicly accessible, min 1875x1275px recommended)

Example prompt:

"Send a postcard to Jane Doe at 123 Main St, San Francisco CA 94102 with a photo of the Golden Gate Bridge and the message 'Wish you were here!'"

bulk_send

POST

Send the same postcard to multiple recipients (async). Up to 5,000 recipients per request. Cards are processed in background batches (~25/minute).

Parameters:

  • recipients — Array of recipient addresses (max 5,000, same fields as to)
  • from — Sender/return address (same for all postcards)
  • message — Back-of-card message (max 350 characters)
  • image_url — Front image URL (publicly accessible, min 1875x1275px recommended)

Example prompt:

"Send a holiday postcard to my 3 friends: Jane at 123 Main St SF CA, John at 456 Oak Ave NYC NY, and Bob at 789 Pine Rd Austin TX"

Returns a bulk_id immediately. Total cost is reserved upfront. Failed cards are automatically refunded. Poll status_url for progress.

check_balance

GET

Check account balance, total postcards sent, and current volume pricing tier. Use this before sending to know your per-postcard cost.

get_pricing

GET

Get all volume pricing tiers. No parameters required.

check_status

GET

Check delivery status of a previously sent postcard.

Parameters:

  • postcard_id — The ID returned from send_postcard

create_webhook

POST

Register a URL to receive real-time postcard event notifications. Events are signed with HMAC-SHA256. Max 10 webhooks per account.

Parameters:

  • url — HTTPS URL to receive webhook POST requests
  • events — Event types: postcard.sent, postcard.delivered, postcard.failed, postcard.returned

The signing secret is returned only on creation — save it securely.

list_webhooks

GET
/

delete_webhook

DELETE

List all registered webhooks or delete one by its webhook_id.

REST API Reference
Direct HTTP API for custom integrations

All API requests require authentication via Bearer token. Include your API key in the Authorization header.

Authorization: Bearer pk_live_your_key_here
POST
/api/v1/postcards

Send a postcard

Request body
{
  "to": {
    "name": "Jane Doe",
    "address_line1": "123 Main St",
    "city": "San Francisco",
    "state": "CA",
    "zip": "94102",
    "country": "US"
  },
  "from": {
    "name": "John Smith",
    "address_line1": "456 Oak Ave",
    "city": "New York",
    "state": "NY",
    "zip": "10001",
    "country": "US"
  },
  "message": "Wish you were here!",
  "image_url": "https://example.com/golden-gate.jpg"
}
Response
{
  "id": "pc_abc123",
  "status": "sent",
  "service": "lob",
  "expected_delivery_date": "2025-03-15",
  "price": 1.99,
  "balance_remaining": 18.01
}
POST
/api/v1/postcards/bulk

Send postcards to multiple recipients — async (up to 5,000)

Request body
{
  "recipients": [
    { "name": "Jane Doe", "address_line1": "123 Main St", "city": "San Francisco", "state": "CA", "zip": "94102", "country": "US" },
    { "name": "John Smith", "address_line1": "456 Oak Ave", "city": "New York", "state": "NY", "zip": "10001", "country": "US" }
  ],
  "from": { "name": "Acme Inc", "address_line1": "789 Pine Rd", "city": "Austin", "state": "TX", "zip": "78701", "country": "US" },
  "message": "Happy holidays from Acme!",
  "image_url": "https://example.com/holiday-card.jpg"
}
Response (202 Accepted)
{
  "bulk_id": "bulk_1710000000_abc123",
  "status": "queued",
  "total": 2,
  "total_cost": 3.98,
  "status_url": "https://postcard.bot/api/v1/postcards/bulk/bulk_1710000000_abc123",
  "message": "Job queued. 2 postcards will be processed shortly. Poll status_url for progress."
}

Cost is reserved upfront. Cards are processed in background batches (~25/minute). Failed cards are automatically refunded. Poll status_url to check progress.

GET
/api/v1/postcards/bulk/:bulk_id

Check bulk job progress

Response
{
  "bulk_id": "bulk_1710000000_abc123",
  "status": "completed",
  "total": 500,
  "processed": 500,
  "succeeded": 498,
  "failed": 2,
  "total_cost": 492.02,
  "created_at": "2026-03-10T12:00:00Z",
  "completed_at": "2026-03-10T12:20:00Z",
  "postcard_ids": ["pc_abc1", "pc_abc2", "..."],
  "failures": [
    { "index": 42, "to": "Bad Address, Nowhere", "error": "Invalid address" }
  ]
}
GET
/api/v1/balance

Check account balance and current pricing tier

Response
{
  "balance": 47.50,
  "currency": "USD",
  "lifetime_top_up": 150.00,
  "tier": { "number": 3, "name": "Silver" },
  "current_pricing": {
    "usa": 1.29,
    "international": 2.29
  },
  "top_up_url": "https://postcard.bot/buy-credits"
}
POST
/api/v1/credits

Add credits via Stripe checkout (min $5, max $10,000)

Request body
{
  "amount": 50.00
}
Response
{
  "checkout_url": "https://checkout.stripe.com/...",
  "amount": 50.00,
  "currency": "USD"
}
GET
/api/v1/postcards/:id

Check postcard status

Response
{
  "id": "pc_abc123",
  "status": "sent",
  "delivery_status": "in_transit",
  "service": "lob",
  "expected_delivery_date": "2025-03-15",
  "to": {
    "name": "Jane Doe",
    "city": "San Francisco",
    "country": "US"
  },
  "created_at": "2025-03-10T12:00:00Z",
  "sent_at": "2025-03-10T12:01:00Z"
}
GET
/api/v1/pricing

Get pricing tiers (no authentication required)

Response
{
  "postcards": {
    "usa": { "price": 1.99, "currency": "USD" },
    "international": { "price": 2.99, "currency": "USD" }
  },
  "volume_tiers": [
    { "tier": 1, "name": "Starter", "min_top_up": 1, "max_top_up": 19, "usa_price": 1.49, "international_price": 2.49 },
    { "tier": 2, "name": "Bronze", "min_top_up": 20, "max_top_up": 49, "usa_price": 1.29, "international_price": 2.29 },
    { "tier": 3, "name": "Silver", "min_top_up": 50, "max_top_up": 199, "usa_price": 0.99, "international_price": 1.99 },
    { "tier": 4, "name": "Gold", "min_top_up": 200, "max_top_up": 499, "usa_price": 0.85, "international_price": 1.85 },
    { "tier": 5, "name": "Platinum", "min_top_up": 500, "max_top_up": 999, "usa_price": 0.79, "international_price": 1.79 },
    { "tier": 6, "name": "Diamond", "min_top_up": 1000, "max_top_up": null, "usa_price": 0.69, "international_price": 1.69 }
  ]
}
POST
/api/v1/webhooks

Register a webhook URL for postcard event notifications. Max 10 per account. HTTPS only.

Request body
{
  "url": "https://example.com/webhooks/postcards",
  "events": ["postcard.created", "postcard.sent", "postcard.delivered", "postcard.failed", "postcard.returned"]
}
Response (201 Created)
{
  "id": "wh_abc123",
  "url": "https://example.com/webhooks/postcards",
  "events": ["postcard.created", "postcard.sent", "postcard.delivered", "postcard.failed", "postcard.returned"],
  "secret": "whsec_...",
  "active": true,
  "created_at": "2026-03-09T12:00:00Z"
}

The secret is only returned on creation. Save it securely — use it to verify webhook signatures via HMAC-SHA256 in the X-PostcardBot-Signature header.

GET
/api/v1/webhooks

List all webhooks for your account

Response
{
  "webhooks": [
    {
      "id": "wh_abc123",
      "url": "https://example.com/webhooks/postcards",
      "events": ["postcard.sent", "postcard.delivered"],
      "active": true,
      "created_at": "2026-03-09T12:00:00Z"
    }
  ]
}
PUT
/api/v1/webhooks/:id

Update a webhook (URL, events, or active status)

Request body
{
  "url": "https://example.com/new-endpoint",
  "events": ["postcard.sent"],
  "active": false
}
DELETE
/api/v1/webhooks/:id

Delete a webhook

Webhook Payload

Events are sent as POST requests with HMAC-SHA256 signature in the X-PostcardBot-Signature header (format: t=timestamp,v1=base64signature).

Example payload
{
  "event": "postcard.sent",
  "postcard_id": "pc_abc123",
  "status": "sent",
  "to": {
    "name": "Jane Doe",
    "city": "San Francisco",
    "country": "US"
  },
  "timestamp": "2026-03-09T12:01:00Z"
}

Webhooks that return 410 Gone are automatically deactivated.

Error Responses

401

Unauthorized

Missing or invalid API key

402

Insufficient balance

Top up at postcard.bot/buy-credits

400

Validation error

Missing or invalid fields (details in response body)

Code Examples
Send real postcards from code in any language

curl

Send a postcard
curl -X POST https://postcard.bot/api/v1/postcards \
  -H "Authorization: Bearer pk_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "to": {
      "name": "Jane Doe",
      "address_line1": "123 Main St",
      "city": "San Francisco",
      "state": "CA",
      "zip": "94102",
      "country": "US"
    },
    "from": {
      "name": "John Smith",
      "address_line1": "456 Oak Ave",
      "city": "New York",
      "state": "NY",
      "zip": "10001"
    },
    "message": "Wish you were here!",
    "image_url": "https://example.com/photo.jpg"
  }'

Python

send_postcard.py
import requests

response = requests.post(
    "https://postcard.bot/api/v1/postcards",
    headers={"Authorization": "Bearer pk_live_your_key_here"},
    json={
        "to": {
            "name": "Jane Doe",
            "address_line1": "123 Main St",
            "city": "San Francisco",
            "state": "CA",
            "zip": "94102",
            "country": "US",
        },
        "from": {
            "name": "John Smith",
            "address_line1": "456 Oak Ave",
            "city": "New York",
            "state": "NY",
            "zip": "10001",
        },
        "message": "Wish you were here!",
        "image_url": "https://example.com/photo.jpg",
    },
)

result = response.json()
print(f"Postcard {result['id']} sent! Delivery: {result['expected_delivery_date']}")

Node.js / TypeScript

send-postcard.ts
const response = await fetch("https://postcard.bot/api/v1/postcards", {
  method: "POST",
  headers: {
    "Authorization": "Bearer pk_live_your_key_here",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    to: {
      name: "Jane Doe",
      address_line1: "123 Main St",
      city: "San Francisco",
      state: "CA",
      zip: "94102",
      country: "US",
    },
    from: {
      name: "John Smith",
      address_line1: "456 Oak Ave",
      city: "New York",
      state: "NY",
      zip: "10001",
    },
    message: "Wish you were here!",
    image_url: "https://example.com/photo.jpg",
  }),
});

const result = await response.json();
console.log(`Postcard ${result.id} sent! Delivery: ${result.expected_delivery_date}`);

Full API spec available at /api/v1/openapi (OpenAPI 3.0 JSON). Import into Postman, Swagger UI, or any API client.

Pricing
Prepaid balance. Volume discounts based on lifetime top-up amount.
TierLifetime top-upUSAInternational
Pay-as-you-go$0$1.99$2.99
Starter$1–$19$1.49$2.49
Bronze$20–$49$1.29$2.29
Silver$50–$199$0.99$1.99
Gold$200–$499$0.85$1.85
Platinum$500–$999$0.79$1.79
Diamond$1,000+$0.69$1.69

Pricing is based on your lifetime top-up amount. Your tier upgrades automatically as you add more credits. Prepaid balance required — add credits via postcard.bot/buy-credits or the API. Auto-recharge coming soon.

Environment Variables
VariableRequiredDescription
POSTCARDBOT_API_KEYYesYour API key from postcard.bot/account
POSTCARDBOT_API_URLNoOverride API base URL (default: https://postcard.bot)

Get API Key · Buy Credits · npm Package · MCP Server · Code Examples · OpenAPI Spec