Inventory webhooks

CardNexus emits webhook events when your inventory changes. Subscribe to them to keep your own system in step without polling. For delivery, signing, and how to subscribe, see the Webhooks guide.

Three events cover inventory:

Event Fires when
inventory.quantity.changed The quantity on one or more of your lines changes.
inventory.import.completed One of your import jobs finishes.
inventory.export.completed One of your export jobs finishes.

Every payload carries type, an eventId, a timestamp, and a data block. eventId is stable across retries of the same delivery — dedupe on it.

inventory.quantity.changed

Fires when the quantity on one or more of your lines changes — after a sale, an order cancellation, an import, a direct edit, or a listing operation. data.reason tells you which:

reason Cause
order A sale reduced your stock.
order_cancelled A cancelled order put stock back.
import A bulk import ran.
edit Your inventory was edited directly.
listing A listing operation changed quantities.

data.changes carries one entry per line whose quantity changed, with the before and after quantities. Lines whose quantity stayed the same aren't included.

{
  "type": "inventory.quantity.changed",
  "eventId": "9c41be2f3a07d815-0",
  "timestamp": "2026-06-02T09:41:27.000Z",
  "data": {
    "reason": "order",
    "changes": [
      {
        "inventoryId": "665f3a2b1c8d4e9f7a6b5c4d",
        "customId": "YRG|5555-5DS1-2EX-006-f-2-2008-08-01",
        "productId": 50212,
        "before": 4,
        "after": 3
      },
      {
        "inventoryId": "665f3a2b1c8d4e9f7a6b5c4e",
        "customId": null,
        "productId": 50213,
        "before": 12,
        "after": 10
      }
    ]
  }
}

Each entry carries its inventoryId, your customId for the line (null when you haven't set one), and the line's productId.

Chunking

Each delivery carries up to 500 changed lines. A larger change set — a big import, say — arrives as several deliveries, each with its own eventId. Treat the deliveries independently rather than expecting one event per change set.

inventory.import.completed

Fires when one of your import jobs finishes, whether it completed or failed.

{
  "type": "inventory.import.completed",
  "eventId": "6842f1c09b3a5d7e2c4b8a1f.completed",
  "timestamp": "2026-06-02T09:48:03.000Z",
  "data": {
    "jobId": "6842f1c09b3a5d7e2c4b8a1f",
    "status": "completed",
    "counts": { "total": 1842, "succeeded": 1836, "failed": 6 },
    "errorReportUrl": "https://cardnexus-bulk-jobs.s3.eu-west-1.amazonaws.com/imports/6842f1c09b3a5d7e2c4b8a1f/error-report.json?X-Amz-Expires=3600&X-Amz-Signature=4e9a..."
  }
}
Field Description
jobId The import job's identifier, as returned when you started the import.
status completed or failed.
counts.total Rows in the file you submitted.
counts.succeeded Rows that were imported.
counts.failed Rows that couldn't be imported.
errorReportUrl Time-limited link to a report of the failed rows, valid for one hour. null when there's none.

If the link lapses, fetch the job with GET /v1/inventory/bulk/jobs/{jobId} for a fresh one — the report itself is kept for 7 days.

inventory.export.completed

Fires when one of your export jobs finishes. A completed export includes a time-limited download link.

{
  "type": "inventory.export.completed",
  "eventId": "6842f4a87d1e9b3c5f2a6d04.completed",
  "timestamp": "2026-06-02T10:12:46.000Z",
  "data": {
    "jobId": "6842f4a87d1e9b3c5f2a6d04",
    "status": "completed",
    "downloadUrl": "https://cardnexus-bulk-jobs.s3.eu-west-1.amazonaws.com/exports/6842f4a87d1e9b3c5f2a6d04/inventory.csv.gz?X-Amz-Expires=3600&X-Amz-Signature=b27c...",
    "expiresAt": "2026-06-02T11:12:46.000Z"
  }
}
Field Description
jobId The export job's identifier, as returned when you started the export.
status completed or failed.
downloadUrl Time-limited link to the exported file, valid for one hour. null when the export failed.
expiresAt When downloadUrl stops working. null when there's no link.

Download the file before expiresAt. If the link lapses, fetch the job with GET /v1/inventory/bulk/jobs/{jobId} for a fresh one — the file itself is kept for 7 days.