CryptoPostage
Back to home

API Documentation

A REST API for creating shipping labels from your own software. Verify addresses, get live rates, and buy labels instantly — paid from a prepaid credit balance you fund with Bitcoin or Monero.

1. Create an API key

Sign in and create a key under Account → API keys. A verified email is required.

2. Top up credits

Fund your balance with BTC or XMR via the top-up endpoint. Credits are USD-denominated.

3. Buy labels instantly

Purchases debit your balance and return the label in the same request — no per-order payment wait.

Authentication

Every request must include your API key as a Bearer token. Keys start with cp_live_ and are shown exactly once at creation. Keep them secret — anyone with your key can spend your credits. Revoke a compromised key immediately from your account page.

curl https://www.cryptopostage.xyz/api/v1/credits \
  -H "Authorization: Bearer cp_live_YOUR_API_KEY"

Request and response bodies are JSON. Errors use a consistent envelope with a stable machine-readable code and an HTTP status (400 validation, 401 auth, 402 insufficient credits, 404 not found, 422 unprocessable, 429 rate limited):

{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Insufficient credits: this label costs $8.99 but your balance is $2.50. Top up via POST /api/v1/credits/topup."
  }
}

Idempotency

POST /api/v1/purchase and POST /api/v1/credits/topup accept an optional Idempotency-Key header (any unique string up to 200 characters, e.g. a UUID). If a request times out or your connection drops, retry with the same key and body: instead of creating and charging a second order, the API returns the original result with status 200 and an Idempotency-Replayed: true header. Reusing a key with a different body returns 409. Strongly recommended for all purchase requests.

curl -X POST https://www.cryptopostage.xyz/api/v1/purchase \
  -H "Authorization: Bearer cp_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-8271-attempt" \
  -d '{ ...purchase body... }'

Endpoints

  • POST /api/v1/verify-address — validate and normalize an address
  • POST /api/v1/rates — get available services and prices
  • POST /api/v1/purchase — buy a label with credits
  • GET /api/v1/orders — list your orders
  • GET /api/v1/orders/{order_id} — retrieve one order
  • GET /api/v1/credits — credit balance and recent activity
  • POST /api/v1/credits/topup — fund credits with crypto

Verify Address

POSThttps://www.cryptopostage.xyz/api/v1/verify-address

Checks deliverability and returns the standardized form of an address. The body is a single address object.

curl -X POST https://www.cryptopostage.xyz/api/v1/verify-address \
  -H "Authorization: Bearer cp_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Jane Doe", "street1": "456 Oak Ave", "city": "Los Angeles",
    "state": "CA", "zip": "90001", "country": "US"
  }'

Response: { valid, normalized, issues }.

Get Rates

POSThttps://www.cryptopostage.xyz/api/v1/rates

Returns available services with customer prices (postage + service fee) in USD cents. Optional carriers array (e.g. ["USPS"]) restricts results; customs is required for international shipments (see below). Weight is in ounces, dimensions in inches. For carrier flat-rate packaging, set parcel.predefinedPackage (e.g. FlatRateEnvelope) and omit dimensions — the same codes as the CSV guide.

curl -X POST https://www.cryptopostage.xyz/api/v1/rates \
  -H "Authorization: Bearer cp_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fromAddress": {
      "name": "John Smith", "street1": "123 Main St", "city": "New York",
      "state": "NY", "zip": "10001", "country": "US"
    },
    "toAddress": {
      "name": "Jane Doe", "street1": "456 Oak Ave", "city": "Los Angeles",
      "state": "CA", "zip": "90001", "country": "US"
    },
    "parcel": { "weightOz": 16, "lengthIn": 10, "widthIn": 8, "heightIn": 4 }
  }'
{
  "rates": [
    {
      "carrier": "USPS",
      "service": "Priority",
      "service_name": "USPS Priority Mail",
      "amount_usd_cents": 899,
      "estimated_delivery_days": 2,
      "international": false
    },
    {
      "carrier": "USPS",
      "service": "GroundAdvantage",
      "service_name": "USPS Ground Advantage",
      "amount_usd_cents": 649,
      "estimated_delivery_days": 3,
      "international": false
    }
  ]
}

Note: UPS, FedEx, and DHL require a funded Adjustment Reserve on your account before purchase.

Create Purchase

POSThttps://www.cryptopostage.xyz/api/v1/purchase

Buys a label using your credit balance. Send the same shipment fields as /rates plus carrier and service (the service code from the rates response). The price is re-quoted server-side at purchase time; your balance is debited the exact live price. Returns 402 when the balance can't cover the label.

curl -X POST https://www.cryptopostage.xyz/api/v1/purchase \
  -H "Authorization: Bearer cp_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fromAddress": { ...same as rates... },
    "toAddress": { ...same as rates... },
    "parcel": { "weightOz": 16, "lengthIn": 10, "widthIn": 8, "heightIn": 4 },
    "carrier": "USPS",
    "service": "Priority"
  }'
{
  "order_id": "CP-A1B2C3D4E5",
  "type": "SHIPPING",
  "status": "COMPLETED",
  "total_usd_cents": 899,
  "credit_applied_cents": 899,
  "created_at": "2026-09-02T18:00:00.000Z",
  "shipments": [
    {
      "id": "...",
      "status": "LABEL_READY",
      "carrier": "USPS",
      "service": "Priority",
      "tracking_number": "9400100000000000000000",
      "tracking_url": "https://tools.usps.com/...",
      "label_url": "https://www.cryptopostage.xyz/api/labels/download?..."
    }
  ],
  "credit_balance_usd_cents": 4101
}

For international shipments, include a customs declaration and phone numbers for both addresses:

"customs": {
  "contentsType": "merchandise",
  "signer": "John Smith",
  "certify": true,
  "customsItems": [
    {
      "description": "Cotton T-shirt",
      "quantity": 2,
      "valueUsdCents": 4000,
      "weightOz": 12,
      "hsTariffNumber": "6109.10",
      "originCountry": "US"
    }
  ]
}

The label is normally generated in the same request (status: "COMPLETED", label_url set). If generation is still in progress or held for review, poll GET /api/v1/orders/{order_id} until the shipment reaches LABEL_READY. Label download links are signed and expire; fetch a fresh one from the order endpoint any time.

List Orders

GEThttps://www.cryptopostage.xyz/api/v1/orders

Lists your orders, newest first. Query params: limit (1–100, default 20) and cursor (from next_cursor in the previous page).

Retrieve Order

GEThttps://www.cryptopostage.xyz/api/v1/orders/{order_id}

Returns one order with shipments, tracking, and fresh signed label URLs. Accepts the public order id (CP-…). For unpaid top-up orders the response includes the active payment details.

Get Credits

GEThttps://www.cryptopostage.xyz/api/v1/credits

Returns balance_usd_cents and the last 20 credit events (top-ups, purchases, adjustments).

Top Up Credits

POSThttps://www.cryptopostage.xyz/api/v1/credits/topup

Creates a crypto payment for the requested USD amount ($5–$10,000). Send the exact amount_crypto to the returned address before it expires; the balance is credited automatically once the payment confirms on-chain.

curl -X POST https://www.cryptopostage.xyz/api/v1/credits/topup \
  -H "Authorization: Bearer cp_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "amount_usd_cents": 5000, "coin": "BTC" }'
{
  "order_id": "CP-F6G7H8I9J0",
  "status": "awaiting_payment",
  "amount_usd_cents": 5000,
  "coin": "BTC",
  "payment": {
    "address": "bc1q...",
    "amount_crypto": "0.00043210",
    "payment_uri": "bitcoin:bc1q...?amount=0.00043210",
    "expires_at": "2026-09-02T18:20:00.000Z"
  }
}

Webhooks

Configure a webhook endpoint on your account page to receive events instead of polling. We POST JSON to your https URL for:

  • order.completed — every shipment on an order has its label (fires for held-for-review orders once approved)
  • order.failed — a label purchase failed; the data.error field explains why
  • topup.confirmed — a credit top-up payment confirmed and your balance was credited
{
  "event": "order.completed",
  "created_at": "2026-09-02T18:00:05.000Z",
  "data": {
    "order_id": "CP-A1B2C3D4E5",
    "status": "COMPLETED",
    "total_usd_cents": 899,
    "shipments": [
      {
        "status": "LABEL_READY",
        "carrier": "USPS",
        "service": "Priority",
        "tracking_number": "9400100000000000000000",
        "tracking_url": "https://tools.usps.com/go/TrackConfirmAction?tLabels=..."
      }
    ]
  }
}

Every delivery is signed. Compute HMAC-SHA256 of <timestamp>.<body> with your signing secret (shown once when you save the URL) and compare it to the X-Webhook-Signature header. Reject deliveries whose timestamp is older than a few minutes to prevent replays.

// Node.js — verify a delivery
const crypto = require("crypto");

const signature = req.headers["x-webhook-signature"]; // "sha256=..."
const timestamp = req.headers["x-webhook-timestamp"];
const expected =
  "sha256=" +
  crypto
    .createHmac("sha256", process.env.WEBHOOK_SECRET) // whsec_...
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

const valid = crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));

Respond with a 2xx within 10 seconds. Failed deliveries are retried with exponential backoff (roughly 1 minute to 1 hour between attempts) for up to 8 attempts. Deliveries are visible on your account page. Events may occasionally arrive out of order — use created_at and the order status as the source of truth.

Rate limits & fair use

Per key: 30 rate requests/min, 20 purchases/min, 60 reads/min, 5 top-ups/min. Responses include Retry-After when limited. Need more? Contact support.