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
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.