Passthrough API
Proxy requests to Amazon SP-API with automatic data handling
Passthrough API
The Passthrough API proxies requests to whitelisted Amazon SP-API endpoints with automatic authentication, data scrubbing, and optional label handling.
Overview
GET|POST|DELETE /passthrough-api/*
Requests to /passthrough-api/orders/v0/orders are proxied to https://sellingpartnerapi-{region}.amazon.com/orders/v0/orders with:
- Automatic Amazon access token injection
- Automatic RDT (Restricted Data Token) generation when needed
- PII scrubbing from responses
- Optional label storage for shipping endpoints
Authentication
| Header | Required | Description |
|---|---|---|
x-seller-access-token | Yes | Valid seller access token |
x-amazon-token-secret | Yes | Secret used during OAuth |
Basic Usage
Get Orders
curl -X GET "https://adb.example.com/passthrough-api/orders/v0/orders?MarketplaceIds=ATVPDKIKX0DER" \
-H "x-seller-access-token: YOUR_SELLER_TOKEN" \
-H "x-amazon-token-secret: YOUR_TOKEN_SECRET"
Response
{
"payload": {
"Orders": [
{
"AmazonOrderId": "123-4567890-1234567",
"OrderStatus": "Unshipped",
"PurchaseDate": "2024-01-15T10:30:00Z",
"OrderTotal": {
"CurrencyCode": "USD",
"Amount": "29.99"
}
}
]
}
}
ShippingAddress is automatically scrubbed. Use the PII API or Label Proxy to access customer addresses.
Transparent RDT Handling
Data Border automatically handles Restricted Data Tokens for endpoints that require them:
sequenceDiagram
participant WMS
participant ADB
participant Amazon as Amazon SP-API
WMS->>ADB: GET /passthrough-api/orders/v0/orders/123/buyerInfo
ADB->>ADB: Detect RDT requirement
ADB->>Amazon: POST /tokens/2021-03-01/restrictedDataToken
Amazon->>ADB: Return RDT
ADB->>Amazon: GET /orders/v0/orders/123/buyerInfo<br/>with RDT token
Amazon->>ADB: Return buyer info
ADB->>ADB: Scrub PII if configured
ADB->>WMS: Return dataNo code changes needed - RDT generation is fully automatic.
Label Handling Mode
When shipping-related headers are provided, Data Border enters label handling mode:
Additional Headers
| Header | Description |
|---|---|
x-amazon-order-id | Amazon order ID for PII lookup |
x-unique-shipment-id | Your unique shipment identifier |
x-unredacted-pii | Set to "true" for unredacted response |
Label Mode Response
{
"success": true,
"data": {
"scrubbed_response": { /* SP-API response with PII removed */ },
"shipment_id": "ship_abc123def456",
"amazon_order_id": "123-4567890-1234567",
"unique_shipment_id": "WMS-SHIP-001",
"documents": [
{ "uuid": "550e8400-e29b-41d4-a716-446655440000", "path": "label.pdf" }
]
}
}
Example: Shipping with Label Storage
curl -X POST "https://adb.example.com/passthrough-api/shipping/v2/shipments" \
-H "Content-Type: application/json" \
-H "x-seller-access-token: YOUR_SELLER_TOKEN" \
-H "x-amazon-token-secret: YOUR_TOKEN_SECRET" \
-H "x-amazon-order-id: 123-4567890-1234567" \
-H "x-unique-shipment-id: WMS-SHIP-001" \
-d '{ "shipment data..." }'
Whitelisted Endpoints
Orders API
| Endpoint | Method | Description |
|---|---|---|
/orders/v0/orders | GET | List orders (addresses scrubbed) |
/orders/v0/orders/{orderId} | GET | Get order details |
/orders/v0/orders/{orderId}/orderItems | GET | Get order items |
/orders/v0/orders/{orderId}/buyerInfo | GET | Get buyer info (RDT auto) |
/orders/v0/orders/{orderId}/address | GET | Get address (RDT auto) |
Reports API
| Endpoint | Method | Description |
|---|---|---|
/reports/2021-06-30/reports | GET, POST | Report management |
/reports/2021-06-30/documents/{id} | GET | Download reports |
Fulfillment
| Endpoint | Method | Description |
|---|---|---|
/mfn/v0/shipments | GET, POST | MFN shipments |
/shipping/v2/shipments | POST | Buy Shipping labels |
/fba/outbound/2020-07-01/* | GET, POST | FBA fulfillment |
Catalog & Listings
| Endpoint | Method | Description |
|---|---|---|
/catalog/2022-04-01/items | GET | Product catalog |
/listings/2021-08-01/* | GET, PATCH, PUT, DELETE | Listing management |
Other APIs
| Endpoint | Method | Description |
|---|---|---|
/feeds/2021-06-30/* | GET, POST | Feed submissions |
/products/pricing/v0/* | GET | Product pricing |
/products/fees/v0/* | GET, POST | FBA fees |
/sales/v1/* | GET | Sales analytics |
/finances/2024-06-19/* | GET | Financial data |
/notifications/v1/* | GET, POST, DELETE | Notification subscriptions |
/services/v1/* | GET, POST | Service API |
/vendor/directFulfillment/* | GET, POST | Vendor Central |
Data Scrubbing
What Gets Scrubbed
Data Border automatically removes PII from responses:
| Field | Scrubbed To |
|---|---|
ShippingAddress.Name | [REDACTED] |
ShippingAddress.AddressLine1 | [REDACTED] |
ShippingAddress.AddressLine2 | [REDACTED] |
ShippingAddress.City | [REDACTED] |
ShippingAddress.Phone | [REDACTED] |
BuyerEmail | [REDACTED] |
BuyerName | [REDACTED] |
Preserved Data
Non-PII fields are preserved:
- Order IDs, status, dates
- Product information (ASINs, SKUs, prices)
- Tracking numbers
- Country codes, state codes, postal codes (partial)
Implementation Examples
Sync Orders
async function syncOrders(sellerId, marketplaceId, lastSync) {
const { token, secret } = await tokenManager.getSellerAccessToken(sellerId)
const params = new URLSearchParams({
MarketplaceIds: marketplaceId,
CreatedAfter: lastSync.toISOString()
})
const response = await fetch(
`${ADB_URL}/passthrough-api/orders/v0/orders?${params}`,
{
headers: {
'x-seller-access-token': token,
'x-amazon-token-secret': secret
}
}
)
const data = await response.json()
for (const order of data.payload.Orders) {
await db.orders.upsert({
amazonOrderId: order.AmazonOrderId,
sellerId,
status: order.OrderStatus,
purchaseDate: order.PurchaseDate,
total: order.OrderTotal?.Amount
// Note: Address is scrubbed
})
}
return data.payload.Orders.length
}
Create Shipment with Labels
async function createShipmentWithLabels(order, shipmentData) {
const { token, secret } = await tokenManager.getSellerAccessToken(order.sellerId)
const response = await fetch(
`${ADB_URL}/passthrough-api/shipping/v2/shipments`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-seller-access-token': token,
'x-amazon-token-secret': secret,
'x-amazon-order-id': order.amazonOrderId,
'x-unique-shipment-id': `${order.id}-${Date.now()}`
},
body: JSON.stringify(shipmentData)
}
)
const data = await response.json()
// Store shipment with document references
await db.shipments.create({
orderId: order.id,
adbShipmentId: data.data.shipment_id,
trackingNumber: data.data.scrubbed_response.trackingId,
documents: data.data.documents
})
return data.data
}
Error Handling
Data Border Errors
{
"is_adb_error": true,
"success": false,
"error": {
"message": "Endpoint not whitelisted",
"details": "/some/custom/endpoint is not in the allowed list"
}
}
Amazon SP-API Errors
Amazon errors are passed through:
{
"errors": [
{
"code": "InvalidInput",
"message": "MarketplaceIds is required",
"details": ""
}
]
}
Common Error Codes
| Status | Message | Cause |
|---|---|---|
| 400 | Endpoint not whitelisted | URL not in allowed list |
| 401 | Invalid seller access token | Token expired |
| 403 | Access denied | Seller not authorized for endpoint |
| 429 | Rate limit exceeded | Amazon throttling |
| 500 | Amazon API error | Upstream Amazon error |
Best Practices
Use Query Parameters Correctly
// Good: URL-encode marketplace IDs
const params = new URLSearchParams({
MarketplaceIds: 'ATVPDKIKX0DER'
})
// Good: Multiple values
params.append('OrderStatuses', 'Unshipped')
params.append('OrderStatuses', 'PartiallyShipped')
Handle Pagination
async function getAllOrders(sellerId, marketplaceId) {
const orders = []
let nextToken = null
do {
const params = new URLSearchParams({ MarketplaceIds: marketplaceId })
if (nextToken) params.set('NextToken', nextToken)
const data = await fetchOrders(sellerId, params)
orders.push(...data.payload.Orders)
nextToken = data.payload.NextToken
} while (nextToken)
return orders
}
Respect Rate Limits
Amazon SP-API has per-endpoint rate limits. Implement backoff:
async function fetchWithRetry(url, options, retries = 3) {
for (let i = 0; i < retries; i++) {
const response = await fetch(url, options)
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || (2 ** i)
await delay(retryAfter * 1000)
continue
}
return response
}
throw new Error('Rate limit exceeded')
}
