Track sales in your own system
A worked example: carrying your own references and fulfillment states on CardNexus sales, so your ERP, WMS, or back office can drive them without keeping a separate table keyed on order numbers.
Every sale has a metadata object — your own keys, your own values. CardNexus never reads or changes them, and the buyer never sees them. You can filter your sales by them, which is what makes them a working queue rather than just a note.
1. Stamp your reference when the order arrives
When an order comes in, write your own identifier onto it with PATCH /v1/sales/{orderNumber}/metadata:
curl -X PATCH https://public-api.cardnexus.com/v1/sales/OR-ASNBC-1/metadata \
-H "Authorization: Bearer cnk_live_4f2d8c91e5b34a7f9e6c2d1a8b5f7e3c" \
-H "Idempotency-Key: 9b4e7c20-5d1a-4f8e-b3c6-a7d9e1f2c4b8" \
-H "Content-Type: application/json" \
-d '{
"metadata": {
"internal_order_ref": "SO-10432",
"fulfillment_stage": "received"
}
}'
The response is the full sale, with your keys under metadata:
{
"orderNumber": "OR-ASNBC-1",
"status": "pending_shipment",
"metadata": {
"internal_order_ref": "SO-10432",
"fulfillment_stage": "received"
}
}
Keys start with a letter and may contain letters, digits, _, . and -, up to 64 characters. Values are strings of up to 500 characters, and a sale holds at most 50 keys.
2. Move the order through your own stages
CardNexus tracks an order as pending_shipment until you mark it shipped. If your process has steps in between — picking, packing, waiting on a restock — put them in metadata and advance them as they happen:
curl -X PATCH https://public-api.cardnexus.com/v1/sales/OR-ASNBC-1/metadata \
-H "Authorization: Bearer cnk_live_4f2d8c91e5b34a7f9e6c2d1a8b5f7e3c" \
-H "Content-Type: application/json" \
-d '{"metadata": {"fulfillment_stage": "packed", "packed_by": "camille"}}'
The write merges. Keys you send are set, keys you leave out keep their value, so internal_order_ref from step 1 is still there. That also means two of your systems can each own their own keys on the same sale without overwriting each other.
To remove a key, send it as null:
{"metadata": {"packed_by": null}}
An empty string is a value, not a removal. Sending {} changes nothing.
3. Pull your work queue
Filter your sales on any of your keys with metadata[key]=value on GET /v1/sales. Everything still on the packing bench:
curl -G https://public-api.cardnexus.com/v1/sales \
-H "Authorization: Bearer cnk_live_4f2d8c91e5b34a7f9e6c2d1a8b5f7e3c" \
--data-urlencode "status=pending_shipment" \
--data-urlencode "metadata[fulfillment_stage]=packed"
Repeat the parameter with another key to require both — up to 10 keys per call:
?metadata[fulfillment_stage]=packed&metadata[warehouse]=lyon
Matching is exact and case-sensitive: there is no partial or prefix match, so SO-104 will not find SO-10432. Combine it freely with status, placedFrom and placedTo, and page through the results with pagination.nextCursor as usual.
4. Ship and stamp in one call
POST /v1/sales/{orderNumber}/mark-shipped takes the same metadata object, so the last stage change can ride along with the tracking number:
curl -X POST https://public-api.cardnexus.com/v1/sales/OR-ASNBC-1/mark-shipped \
-H "Authorization: Bearer cnk_live_4f2d8c91e5b34a7f9e6c2d1a8b5f7e3c" \
-H "Idempotency-Key: 2c7f1a94-8e3b-4d6a-9f1c-5b8e2a4d7c31" \
-H "Content-Type: application/json" \
-d '{
"trackingNumber": "6A18432197431",
"metadata": {"fulfillment_stage": "shipped"}
}'
The two either both apply or neither does: a tracking number rejected as invalid leaves your metadata untouched, and metadata that would take the sale past its key limit returns 422 METADATA_LIMIT_EXCEEDED without shipping the order.
5. Match webhooks back to your records
The order.status.changed webhook carries your metadata on the seller's copy, so a delivery can be routed to the right record in your system without a follow-up call:
{
"type": "order.status.changed",
"recipientRole": "seller",
"data": {
"orderNumber": "OR-ASNBC-1",
"status": "delivered",
"previousStatus": "shipped",
"metadata": {
"internal_order_ref": "SO-10432",
"fulfillment_stage": "shipped"
}
}
}
data.metadata is null on the buyer's copy of the same event — metadata is yours alone.
Reading it back
metadata is on every sales response: GET /v1/sales, GET /v1/sales/{orderNumber}, and the responses to mark-shipped and cancel. It never appears on the buyer's purchase.
Metadata can be written in any status, including on completed and cancelled sales — so a reconciliation run can still stamp an invoice number long after the order closed.