Plug any warehouse into ProfitDesk.
The Prep API makes your software a prep center in ProfitDesk. Announcements reach you as signed webhooks, you report receipts and outbounds back, and every unit stays traceable from purchase to FBA shipment.
- Signed webhooks
- REST API
- OpenAPI 3.1
- Event replay
POST https://wms.example.com/webhooks/profitdesk
X-Webhook-Event-Type: inbound.announced
X-Webhook-Signature: sha256=9f2c41…
{
"type": "inbound.announced",
"connection_id": "pcn_812",
"data": {
"inbound": {
"id": "inb_2000000123",
"fulfillment": "FBA",
"tracking_numbers": ["1Z999AA10123456784"],
"lines": [{
"id": "inl_99",
"purchase_id": "pur_4711",
"item": { "sku": "AB-123", "asin": "B0ABCDEFGH" },
"quantity_expected": 6
}]
}
}
}POST /v1/prep/inbounds/inb_2000000123/receipts
Authorization: Bearer ppk_3f9a…
{
"event_id": "rcpt-8812",
"status": "received",
"lines": [{ "line_id": "inl_99", "quantity_received": 6 }]
}Two ways in
One API, built for two kinds of builders.
- 1Add your softwareSettings → Integrations → Prep centers → Add → “Own software (Webhook)”. Enter your HTTPS endpoint and pick the events you want.
- 2Store two credentialsA signing secret (whsec_…) and an API key (ppk_…), shown once. Rotate both anytime.
- 3Link a warehousePurchases in that warehouse are announced to your software from now on.
- Key sees
- This one connection
- Rate limit
- 2 req/s, bursts of 10
- Testing
- “Send test” ping and a delivery log
- Mix & match
- Next to PrepBusiness or PrepTool, per warehouse
How it works
From purchase to FBA shipment,
in five events.
- 01ProfitDeskGoods are on their way
inbound.announcedA signed webhook with lines, SKU, ASIN and tracking. Answer 2xx, optionally with your own external_id. - 02ProfitDeskDetails follow
inbound.updatedNew tracking codes and the FNSKU once Amazon assigns it, so you can print labels. - 03YouYou count the goods
POST …/receiptsAbsolute counts per line, damaged units included. ProfitDesk updates the purchase. - 04YouYou ship them out
POST /v1/Lines plus the FBA shipment id link your outbound to Amazon’s shipment.prep/ outbounds - 05AmazonAmazon receives
FBA15XYZProfitDesk books the forwarded quantity from Amazon’s own data. Your outbound is the trace.
Every unit, traceable end to end.
Each object carries an id with a type prefix. The purchase id never changes, even when goods are re-announced or move warehouse.
pur_4711PurchaseThe seller’s buy. The key to follow a unit, forever.inb_2000000123InboundOne announcement. A re-announcement gets a new one.inl_99Inbound lineOne purchase inside it. Use it in receipts and outbounds.out_55OutboundWhat you shipped, addressed by your own external_id.FBA15XYZFBA shipmentAmazon’s shipment id, linked from your outbound.
Use your own references too: every path that takes an id also accepts ext:<your id>.
Built for production
The boring parts
are already solved.
HMAC-SHA256 in a dozen lines.
Sign the raw body with your whsec_ secret and the timestamp, compare in constant time, reject anything older than five minutes. During a secret rotation both signatures are sent for 24 hours.
import crypto from "node:crypto";
function verify(rawBody, headers, secret) {
const ts = headers["x-webhook-timestamp"];
const signature = headers["x-webhook-signature"];
if (!ts || !signature || !/^\d+$/.test(ts)) return false;
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
return signature.split(",").some((sig) => {
const a = Buffer.from(sig.trim()), b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
});
}import hashlib, hmac, time
def verify(raw_body: bytes, headers, secret: str) -> bool:
ts = headers.get("X-Webhook-Timestamp", "")
signature = headers.get("X-Webhook-Signature", "")
if not ts.isdigit() or not signature:
return False
if abs(time.time() - int(ts)) > 300:
return False
expected = "sha256=" + hmac.new(secret.encode(), ts.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return any(hmac.compare_digest(sig.strip(), expected) for sig in signature.split(","))function verify(string $rawBody, array $headers, string $secret): bool {
$ts = $headers['X-Webhook-Timestamp'] ?? '';
$signature = $headers['X-Webhook-Signature'] ?? '';
if (!ctype_digit($ts) || $signature === '') return false;
if (abs(time() - (int)$ts) > 300) return false;
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $rawBody, $secret);
foreach (explode(',', $signature) as $sig) {
if (hash_equals($expected, trim($sig))) return true;
}
return false;
}func verify(rawBody []byte, h http.Header, secret string) bool {
ts := h.Get("X-Webhook-Timestamp")
sec, err := strconv.ParseInt(ts, 10, 64)
if err != nil || math.Abs(float64(time.Now().Unix()-sec)) > 300 {
return false
}
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(ts + "."))
mac.Write(rawBody)
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
for _, sig := range strings.Split(h.Get("X-Webhook-Signature"), ",") {
if hmac.Equal([]byte(strings.TrimSpace(sig)), []byte(expected)) {
return true
}
}
return false
}Endpoints
Base URL https://api.profitpath.app · Bearer ppk_…
| GET | /v1/The key’s connection | The key’s connection |
| GET | /v1/List inbounds | List inbounds |
| GET | /v1/One inbound, also by ext: | One inbound, also by ext: |
| PUT | /v1/Set your reference | Set your reference |
| POST | /v1/Report goods received | Report goods received |
| POST | /v1/Report an outbound | Report an outbound |
| GET | /v1/One outbound | One outbound |
| GET | /v1/Items, one per SKU | Items, one per SKU |
| GET | /v1/Replay webhook events | Replay webhook events |
Webhook events
Delivered at least once, in order per inbound.
connection.created · updated · removedA seller connects, changes details or leaves | Providers |
inbound.announcedGoods are on their way to you | On |
inbound.updatedTracking, FNSKU, external id or lines changed | On |
inbound.cancelledCancelled, moved or re-announced | On |
inbound.restoredA cancelled inbound is active again | On |
inbound.receivedEcho of your own receipt | Opt-in |
outbound.recordedEcho of your own outbound | Opt-in |
ping“Send test” from ProfitDesk | Own software |
Docs for your own software
FAQ
Questions from developers
If you run your own warehouse software for your own Amazon business, use the own-software connection in your ProfitDesk settings. If you build software that many prep centers or sellers use, register as a prep provider: one endpoint and one key cover every seller.
With your own software, use a test warehouse and a test purchase in your own account, and “Send test” for connectivity. As a prep provider, ask us for a test connection from a test seller account, and run the whole flow before your first real seller connects. Events can always be replayed.
Goods are announced as soon as the Amazon listing exists. Amazon assigns the FNSKU a little later, and it follows in inbound.updated.
ProfitDesk retries for up to 72 hours. After that you can catch up anytime with GET /v1/prep/events.
No. Outbounds are a trace. Forwarded quantities come from Amazon’s own shipment data, linked through the FBA shipment id you send.
Yes. Poll GET /v1/prep/events every minute from the last event id you processed, and deduplicate by id.
Building prep software
for many sellers?
Get listed in every ProfitDesk seller’s prep-center picker. One endpoint and one key cover all of them.
support@profitpath.com