Integration · four calls

Four calls, and not one of them returns a buyer address.

This is the whole integration. If your warehouse system can already POST to a carrier API, most of the work is changing the URL and swapping five values for placeholders.

Step 01

Authorize the seller once

The seller approves access in Seller Central. You exchange the authorization code for a seller access token; the refresh token is sealed on our side with a secret you supply and never store with us.

Request

curl -X POST "https://adb.example.com/api/claim-code" \
  -H "x-adb-access-token: $TENANT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "code": "ANWxDySpXWvSCcQoTZ", "state": "wh-1" }'

Response

{
  "success": true,
  "data": {
    "seller_id": "seller_9f2c1a",
    "seller_access_token": "sat_...",
    "marketplace_region": "na"
  }
}
  • You never register an application with Amazon — the registration, the questionnaire and the attestation are ours.
  • Refresh tokens are stored AES-256-GCM encrypted; the key is derived from the x-amazon-token-secret you send on each request.
  • Authorization endpoints are rate limited to 5 requests per 10 seconds per IP.

Step 02

Buy the label with placeholders

Send the carrier request you would normally send, with placeholder tokens where the buyer details go. The border resolves them against the Selling Partner API, calls the carrier, then scrubs the response before you see it.

Request

curl -X POST "https://adb.example.com/api/label-proxy/forward" \
  -H "x-seller-access-token: $SELLER_TOKEN" \
  -H "x-amazon-token-secret: $TOKEN_SECRET" \
  -H "x-original-url: https://api.easypost.com/v2/shipments" \
  -H "x-amazon-order-id: 113-9483920-1029338" \
  -H "x-unique-shipment-id: WMS-SHIP-88214" \
  -H "Authorization: Bearer $EASYPOST_KEY" \
  -d '{ "shipment": { "to_address": {
        "name": "{{ship_to_name}}",
        "street1": "{{ship_to_address1}}",
        "city": "{{ship_to_city}}",
        "zip": "{{ship_to_zip}}",
        "phone": "{{ship_to_phone}}" },
      "from_address": { "...": "your warehouse" },
      "parcel": { "length": 10, "width": 8, "height": 4, "weight": 16 } } }'

Response

{
  "success": true,
  "data": {
    "scrubbed_response": {
      "to_address": { "name": "[REDACTED]", "street1": "[REDACTED]" },
      "tracking_code": "1Z999AA10123456784"
    },
    "shipment_id": "ship_7c1e9a2b4d",
    "documents": [{ "uuid": "9f4c2e10-...", "path": "label.pdf" }]
  }
}
  • Eleven placeholders are available, from {{ship_to_name}} through {{buyer_email}}. An unknown placeholder is a 400, not a silent pass-through.
  • x-original-url must match one of seven allow-listed carrier origins: EasyPost, ShipStation, Shippo, UPS, FedEx, USPS, DHL.
  • Your carrier account and rate agreements stay yours — the key rides through in the Authorization header.

Step 03

Print it on your own printer

The unredacted label never travels through your network. Reference it by document UUID and Device Hub pulls it from inside the border straight to the printer on the pack bench.

Request

curl -X POST "https://adb.example.com/api/print/send" \
  -H "x-seller-access-token: $SELLER_TOKEN" \
  -H "x-amazon-token-secret: $TOKEN_SECRET" \
  -d '{ "shipment_id": "ship_7c1e9a2b4d",
        "printer_type": "label",
        "printer_id": "warehouse-zebra-01",
        "label_uuids": ["9f4c2e10-..."] }'

Response

{
  "success": true,
  "data": {
    "status": "sent",
    "documents_sent": 1,
    "print_count": 1,
    "is_reprint": false
  }
}
  • Device Hub runs as a Windows desktop app or a background service; one machine can serve every network printer in the building.
  • printer_type label sends native ZPL when the carrier provides it, and falls back to PDF. Laser printers get the PDF.
  • A second call to the same shipment returns print_count 2, is_reprint true and the timestamp of the first print.

Step 04

Close the order when the parcel leaves

Completing an order is what turns the tap off. Any later attempt to read that buyer address returns 403, forever, and the event is in the audit log with the time, the IP and the seller.

Request

curl -X POST "https://adb.example.com/api/pii/completeOrder" \
  -H "x-seller-access-token: $SELLER_TOKEN" \
  -H "x-amazon-token-secret: $TOKEN_SECRET" \
  -d '{ "orderId": "113-9483920-1029338" }'

Response

{ "success": true, "data": { "message": "Order marked complete" } }

// any later read of that order:
403 { "error": { "message": "PII access blocked for this order" } }
  • blockPII does the same thing without marking the order shipped — use it for cancellations and deletion requests.
  • Until an order is completed, address reads are still capped at one per hour.
  • Every call in this walkthrough writes an audit record you can pull for a compliance review.

Afterwards

What ends up in your database.

Five columns, all of them opaque identifiers. This is the row your auditor looks at, and there is nothing in it to protect.

shipments

amazon_order_id113-9483920-1029338
adb_shipment_idship_7c1e9a2b4d
tracking_code1Z999AA10123456784
document_uuid9f4c2e10-77ab-4d02-9d55-1c0a6b3ef841
print_count1

Not present

ship_to_name · ship_to_address1 · ship_to_address2 · ship_to_city · ship_to_state · ship_to_zip · ship_to_phone · buyer_name · buyer_email

Which is the point of the exercise: your backups, your replicas, your staging refresh and your log pipeline inherit nothing that would put them inside the audit boundary.

Read the reference, or just start posting.

A sandbox seller and a test tenant take a few minutes to set up, and the first label request is the same call you will run in production.