{"openapi":"3.0.0","paths":{"/api/supplier-api/v1/orders":{"get":{"description":"Supports page/limit, statusIn (CSV), keyword, customerRef (exact, case-insensitive match on either account reference of the sending store) and updatedSince (ISO 8601, strictly-after) for incremental sync: poll with your previous run's high-water mark to receive only what changed.","operationId":"SupplierApiOrdersController_list","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (starts from 1)","schema":{"minimum":1,"default":1,"example":1,"type":"integer"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page","schema":{"minimum":1,"maximum":500,"default":50,"example":50,"type":"integer"}},{"name":"sortBy","required":false,"in":"query","description":"Field to sort by","schema":{"default":"createdAt","example":"createdAt","type":"string"}},{"name":"sortDir","required":false,"in":"query","description":"Sort direction (asc or desc)","schema":{"default":"DESC","example":"ASC","type":"string","enum":["ASC","DESC"]}},{"name":"keyword","required":false,"in":"query","description":"Case-insensitive substring match against the purchase order number, the sending store's name, your customer code for the store (customerRef) or the store's trade account reference (accountRef)","schema":{"example":"PO-1042","type":"string"}},{"name":"customerRef","required":false,"in":"query","description":"Only purchase orders from the store whose account reference equals this value (case-insensitive, exact). Matches either your customer code for the store (store.customerRef) or the store's own trade account reference with you (store.accountRef).","schema":{"maxLength":100,"example":"ACME-0421","type":"string"}},{"name":"status","required":false,"in":"query","description":"Filter to one status (superseded by statusIn when both are sent)","schema":{"type":"string","enum":["sent","acknowledged","partially_fulfilled","fulfilled","cancelled","declined"]}},{"name":"statusIn","required":false,"in":"query","description":"Comma-separated list of statuses to include (e.g. sent,acknowledged)","schema":{"type":"array","items":{"type":"string","enum":["sent","acknowledged","partially_fulfilled","fulfilled","cancelled","declined"]}}},{"name":"updatedSince","required":false,"in":"query","description":"Only return purchase orders updated strictly after this ISO 8601 timestamp","schema":{"example":"2026-09-01T00:00:00.000Z","type":"string"}},{"name":"storeId","required":false,"in":"query","description":"Only purchase orders from this store id","schema":{"type":"string"}},{"name":"createdFrom","required":false,"in":"query","description":"Only purchase orders created at or after this ISO 8601 timestamp","schema":{"example":"2026-09-01T00:00:00.000Z","type":"string"}},{"name":"createdTo","required":false,"in":"query","description":"Only purchase orders created before this ISO 8601 timestamp","schema":{"example":"2026-09-30T00:00:00.000Z","type":"string"}},{"name":"late","required":false,"in":"query","description":"Only overdue purchase orders","schema":{"type":"boolean"}},{"name":"cancellationRequested","required":false,"in":"query","description":"Only purchase orders with an open cancellation request","schema":{"type":"boolean"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List purchase orders (paginated, newest first)","tags":["supplier-api-orders"]}},"/api/supplier-api/v1/orders/{id}":{"get":{"operationId":"SupplierApiOrdersController_findOne","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Fetch one purchase order with line items","tags":["supplier-api-orders"]}},"/api/supplier-api/v1/orders/{id}/acknowledge":{"post":{"operationId":"SupplierApiOrdersController_acknowledge","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcknowledgePurchaseOrderDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Acknowledge a purchase order, optionally with an expected despatch date and per-line accepted quantities","tags":["supplier-api-orders"]}},"/api/supplier-api/v1/orders/{id}/cancellation/accept":{"post":{"operationId":"SupplierApiOrdersController_acceptCancellation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Accept the vendor's cancellation request","tags":["supplier-api-orders"]}},"/api/supplier-api/v1/orders/{id}/cancellation/reject":{"post":{"operationId":"SupplierApiOrdersController_rejectCancellation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RejectCancellationDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Reject the vendor's cancellation request","tags":["supplier-api-orders"]}},"/api/supplier-api/v1/orders/{id}/invoice":{"post":{"operationId":"SupplierApiOrdersController_uploadInvoice","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Attach your invoice (PDF, up to 10 MB) to a purchase order; replaces any earlier invoice","tags":["supplier-api-orders"]},"get":{"operationId":"SupplierApiOrdersController_downloadInvoice","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Download the invoice attached to a purchase order","tags":["supplier-api-orders"]},"delete":{"operationId":"SupplierApiOrdersController_removeInvoice","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Remove the invoice attached to a purchase order","tags":["supplier-api-orders"]}},"/api/supplier-api/v1/orders/{id}/hold":{"post":{"operationId":"SupplierApiOrdersController_hold","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldPurchaseOrderDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Put an open purchase order on hold (backorder)","tags":["supplier-api-orders"]}},"/api/supplier-api/v1/orders/{id}/release":{"post":{"operationId":"SupplierApiOrdersController_release","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Release a purchase order from hold","tags":["supplier-api-orders"]}},"/api/supplier-api/v1/orders/{id}/shipments":{"post":{"operationId":"SupplierApiOrdersController_createShipment","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Any unique string (e.g. a UUID). Replays with the same key within 24h return the original response with Idempotency-Replayed: true.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierApiCreateShipmentDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Record a shipment (per-parcel quantities, per-line tracking)","tags":["supplier-api-orders"]}},"/api/supplier-api/v1/stock/bulk":{"post":{"description":"Each row sets an absolute quantity (and optionally the low-stock threshold) for one identifier: a supplier product `productId`, or a `sku`. A sku is matched case-insensitively against the SKUs of this supplier's products and product variants; every listing carrying the SKU is updated, and variant matches win over product matches. Products that own variants must be addressed through their variant SKUs. By default an identifier that matches nothing rejects the whole call with a 400 naming it; set `skipUnmatched: true` to apply the matched rows and receive the rest in `unmatched`. Writes are transactional and idempotent: re-sending the same levels reports `updated: 0`.","operationId":"SupplierApiStockController_bulkUpdate","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkStockUpdateDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkStockUpdateResultDto"}}}}},"security":[{"bearer":[]}],"summary":"Bulk-update stock levels by productId or SKU (max 500 rows)","tags":["supplier-api-stock"]}},"/api/supplier-api/v1/tracking-updates":{"post":{"description":"Each row resolves via PO number → vendor order number → external ref, then applies quantity + tracking to the matched line. Rows are processed independently: a failing row reports its error in place and never aborts the batch. Set dryRun: true to preview outcomes without writing.","operationId":"SupplierApiTrackingController_trackingUpdates","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierApiTrackingUpdatesDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Push tracking rows against POs by business reference (max 500 rows)","tags":["supplier-api-tracking"]}},"/api/supplier-api/v1/carriers":{"get":{"operationId":"SupplierApiTrackingController_carriers","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List active shipping carriers (name, code, URL pattern)","tags":["supplier-api-tracking"]}},"/api/supplier-api/v1/webhooks/topics":{"get":{"operationId":"SupplierApiWebhooksController_getTopics","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List subscribable supplier webhook topics","tags":["supplier-api-webhooks"]}},"/api/supplier-api/v1/webhooks":{"get":{"operationId":"SupplierApiWebhooksController_list","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List this key's webhook subscriptions (never the secrets)","tags":["supplier-api-webhooks"]},"post":{"operationId":"SupplierApiWebhooksController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSupplierWebhookSubscriptionDto"}}}},"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Create a webhook subscription (signing secret returned exactly once)","tags":["supplier-api-webhooks"]}},"/api/supplier-api/v1/webhooks/{id}":{"patch":{"operationId":"SupplierApiWebhooksController_setActive","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetOutboundWebhookActiveDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Pause or resume a webhook subscription this key owns (resume clears the failure streak)","tags":["supplier-api-webhooks"]},"delete":{"operationId":"SupplierApiWebhooksController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Delete a webhook subscription this key owns","tags":["supplier-api-webhooks"]}},"/api/supplier-api/v1/webhooks/{id}/deliveries":{"get":{"operationId":"SupplierApiWebhooksController_deliveries","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Last 20 delivery attempts for a subscription","tags":["supplier-api-webhooks"]}},"/api/supplier-api/v1/webhooks/{id}/test":{"post":{"operationId":"SupplierApiWebhooksController_test","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Send a signed test ping to the endpoint","tags":["supplier-api-webhooks"]}},"/api/supplier-api/v1/webhooks/{id}/deliveries/{deliveryId}/redeliver":{"post":{"operationId":"SupplierApiWebhooksController_redeliver","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"deliveryId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedeliverWebhookDeliveryDto"}}}},"responses":{"202":{"description":""}},"security":[{"bearer":[]}],"summary":"Re-send a stored delivery (same event id, new delivery)","tags":["supplier-api-webhooks"]}}},"info":{"title":"Orbit Commerce Supplier API","description":"REST API (v1) for supplier machine integrations (ERP/WMS). Authenticate with a supplier API key (`sup_sk_…`) as a Bearer token; access is narrowed by the scopes granted to the key.","version":"v1","contact":{}},"tags":[],"servers":[],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"Supplier API key (sup_sk_...)","type":"http"}},"schemas":{"AcknowledgeLineDto":{"type":"object","properties":{"itemId":{"type":"string"},"acceptedQuantity":{"type":"number","description":"Quantity the supplier commits to ship; below the ordered quantity records a short-ship, 0 rejects the line","minimum":0},"note":{"type":"string","maxLength":500}},"required":["itemId","acceptedQuantity"]},"AcknowledgePurchaseOrderDto":{"type":"object","properties":{"expectedDespatchDate":{"type":"string","description":"Expected despatch date (YYYY-MM-DD)","example":"2026-09-12"},"lines":{"type":"array","items":{"$ref":"#/components/schemas/AcknowledgeLineDto"}}}},"RejectCancellationDto":{"type":"object","properties":{"reason":{"type":"string","maxLength":500,"example":"Already packed and collected"}},"required":["reason"]},"HoldPurchaseOrderDto":{"type":"object","properties":{"reason":{"type":"string","maxLength":500,"example":"Awaiting stock from the factory"},"expectedShipDate":{"type":"string","description":"Expected ship date (YYYY-MM-DD)","example":"2026-09-20"}},"required":["reason"]},"SupplierApiShipmentLineDto":{"type":"object","properties":{"itemId":{"type":"string","description":"Purchase-order line item id"},"quantity":{"type":"number","description":"Units shipped in THIS parcel (incremental, not cumulative). The server adds it to the line’s current fulfilled quantity; the result must not exceed the ordered quantity.","minimum":1},"trackingNumber":{"type":"string","maxLength":100},"trackingCarrier":{"type":"string","maxLength":50},"trackingUrl":{"type":"string","maxLength":1000}},"required":["itemId","quantity"]},"SupplierApiCreateShipmentDto":{"type":"object","properties":{"lines":{"type":"array","items":{"$ref":"#/components/schemas/SupplierApiShipmentLineDto"}}},"required":["lines"]},"BulkStockUpdateItemDto":{"type":"object","properties":{"productId":{"type":"string","format":"uuid","example":"3f9c2d6e-8a41-4b7e-9c1d-2e5f6a7b8c9d","description":"Supplier product id to update. Use this OR `sku`, not both. Products that own variants cannot be addressed by productId: send the variant SKUs instead."},"sku":{"type":"string","minLength":1,"maxLength":255,"example":"WIDGET-BLUE-M","description":"SKU to update. Use this OR `productId`, not both. Matched case-insensitively against the SKUs of this supplier's products and product variants (surrounding whitespace is ignored). SKUs are not unique per supplier, so every listing carrying the SKU is updated: a shared SKU means shared physical stock. When the same SKU sits on both a variant and a product, the variant matches win and the product is left untouched. A SKU that only matches a product owning variants is rejected; address its variant SKUs."},"quantity":{"type":"number","minimum":0,"example":42,"description":"Absolute stock level to store (not a delta)."},"lowStockThreshold":{"type":"number","minimum":0,"example":5,"description":"Low-stock threshold. Omit to leave the stored threshold unchanged."}},"required":["quantity"]},"BulkStockUpdateDto":{"type":"object","properties":{"updates":{"minItems":1,"maxItems":500,"example":[{"sku":"WIDGET-BLUE-M","quantity":42},{"sku":"widget-red-l","quantity":0,"lowStockThreshold":5},{"productId":"3f9c2d6e-8a41-4b7e-9c1d-2e5f6a7b8c9d","quantity":12}],"description":"One row per listing to update. Rows may mix `productId` and `sku` addressing. When two rows address the same listing the last row wins.","type":"array","items":{"$ref":"#/components/schemas/BulkStockUpdateItemDto"}},"skipUnmatched":{"type":"boolean","default":false,"example":false,"description":"When false (default) any productId or sku that matches nothing for this supplier rejects the whole request with a 400 listing the unmatched identifiers and nothing is written. When true the matched rows are applied and the unmatched identifiers are reported in the response instead."}},"required":["updates"]},"BulkStockUpdateResultDto":{"type":"object","properties":{"updated":{"type":"number","example":2,"description":"Listings (products + variants) whose stored quantity or threshold actually changed. Rows that restated the current value are not counted."},"matched":{"type":"number","example":3,"description":"Listings (products + variants) addressed by the request, whether or not their values changed. Can exceed the row count when a SKU is shared by several listings."},"unmatched":{"example":["OLD-SKU-1"],"description":"Identifiers (productId or sku, as sent) that matched no listing. Always empty unless `skipUnmatched` is true, because otherwise the request is rejected.","type":"array","items":{"type":"string"}}},"required":["updated","matched","unmatched"]},"SupplierApiTrackingRowDto":{"type":"object","properties":{"orderRef":{"type":"string","description":"Order reference: the supplier PO number (e.g. PO-2026-0001) or the vendor order number","maxLength":255},"lineSku":{"type":"string","description":"Line SKU within the matched PO. Optional when the PO has exactly one line","maxLength":255},"externalRef":{"type":"string","description":"The supplier's own external reference for the PO","maxLength":255},"trackingNumber":{"type":"string","maxLength":100},"trackingCarrier":{"type":"string","maxLength":50},"trackingUrl":{"type":"string","maxLength":1000},"quantityShipped":{"type":"number","description":"Cumulative units shipped for the line. Defaults to the whole line when omitted (\"I have tracking\" implies \"this line is out the door\")","minimum":1}},"required":["orderRef","trackingNumber"]},"SupplierApiTrackingUpdatesDto":{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/SupplierApiTrackingRowDto"}},"dryRun":{"type":"boolean","description":"Validate + resolve every row and report what WOULD happen, without writing anything","default":false}},"required":["rows"]},"CreateSupplierWebhookSubscriptionDto":{"type":"object","properties":{"topic":{"type":"string","enum":["supplier.purchase-order.sent","supplier.purchase-order.cancelled","supplier.purchase-order.held","supplier.purchase-order.released","supplier.purchase-order.cancellation-requested","supplier.purchase-order.cancellation-withdrawn"],"description":"Supplier webhook topic to subscribe to"},"webhookUrl":{"type":"string","example":"https://erp.example.com/hooks/orbit","description":"HTTPS endpoint that will receive signed webhook POSTs"}},"required":["topic","webhookUrl"]},"SetOutboundWebhookActiveDto":{"type":"object","properties":{"isActive":{"type":"boolean","example":true,"description":"true switches the subscription on: it re-enables one the platform disabled after repeated failed deliveries, clearing its failure streak, or resumes one that was paused by hand. One the platform paused (its pausedReasons is not empty: the plugin deactivated or suspended) is not switched on here; its own lifecycle resumes it. false pauses it: events that happen while it is paused are not sent."}},"required":["isActive"]},"RedeliverWebhookDeliveryDto":{"type":"object","properties":{"resendExpired":{"type":"boolean","description":"Send an expired delivery's event again anyway. A delivery expires when it is not delivered within 72 hours (it is then failed with the error \"expired\" and not tried again); redelivering one is refused (409 WEBHOOK_DELIVERY_EXPIRED) unless this is true. The event goes out under its original id and created_at, as a new delivery with its own 72 hours.","default":false}}}}}}