{"info":{"name":"SKU.io API","description":"Auto-generated from the SKU.io OpenAPI spec — the same source that powers https://developer.sku.io. Set the `baseUrl` variable to your tenant (default https://app.sku.io — replace `app` with your subdomain) and put a Personal Access Token in the `token` variable (Settings → Developer → Personal Access Tokens). Spec version 1.0.0.","schema":"https://schema.getpostman.com/json/collection/v2.1.0/collection.json"},"auth":{"type":"bearer","bearer":[{"key":"token","value":"{{token}}","type":"string"}]},"variable":[{"key":"baseUrl","value":"https://app.sku.io","type":"string"},{"key":"token","value":"","type":"string"}],"item":[{"name":"Core API","item":[{"name":"Accounting","item":[{"name":"Get Sync Settings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/accounting/sync-settings","host":["{{baseUrl}}"],"path":["api","accounting","sync-settings"]},"description":"Returns the accounting sync settings for the configured integration, including per-type sync enable/disable controls and sync start dates.\n\n**Required scope: `accounting:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nEvery transaction type is returned, whether or not it has been saved before. A type that has never been saved inherits the control of the type it was split out of — `inventory_revaluation` (automatic cost true-ups) inherits `inventory_adjustment` until you save it a setting of its own.\n\n**mirror_sync_controls** (Xero connections only; empty object otherwise): per-entity toggles for the nightly mirror downloads FROM Xero (`bank_transactions`, `payments`). Disabled by default — the scheduled download runs only for enabled entities. Manual downloads via the download endpoints work regardless."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/accounting/sync-settings","host":["{{baseUrl}}"],"path":["api","accounting","sync-settings"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"integration_instance_id\": 5,\n    \"integration_name\": \"Xero\",\n    \"sync_controls_by_type\": {\n      \"sales_order_invoice\": {\n        \"enabled\": true,\n        \"sync_start_date\": \"2024-01-01\",\n        \"label\": \"Sales Order Invoice\"\n      },\n      \"purchase_order_invoice\": {\n        \"enabled\": true,\n        \"sync_start_date\": null,\n        \"label\": \"Purchase Order Invoice\"\n      },\n      \"credit_note\": {\n        \"enabled\": false,\n        \"sync_start_date\": null,\n        \"label\": \"Credit Note\"\n      },\n      \"inventory_adjustment\": {\n        \"enabled\": true,\n        \"sync_start_date\": \"2025-01-01\",\n        \"label\": \"Inventory Adjustment\"\n      },\n      \"inventory_revaluation\": {\n        \"enabled\": true,\n        \"sync_start_date\": \"2025-01-01\",\n        \"label\": \"Inventory Revaluation\"\n      }\n    },\n    \"mirror_sync_controls\": {\n      \"bank_transactions\": {\n        \"enabled\": false,\n        \"label\": \"Bank Transactions (download from Xero)\"\n      },\n      \"payments\": {\n        \"enabled\": true,\n        \"label\": \"Payments (download from Xero)\"\n      }\n    }\n  }\n}"}]},{"name":"Update Sync Settings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/accounting/sync-settings","host":["{{baseUrl}}"],"path":["api","accounting","sync-settings"]},"description":"Updates the accounting sync settings. Unsubmitted types are preserved (partial updates supported).\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nEach key of `sync_controls_by_type` must be a valid transaction type value — call `GET /api/accounting/sync-settings/types` for the current list. An unknown key returns 422.\n\nFields per type:\n- `enabled` (boolean, required) — whether documents of this type sync to the accounting provider\n- `sync_start_date` (date, nullable) — documents dated before this are never synced\n\nSaving a type gives it a control of its own, which ends any inheritance from the type it was split out of (`inventory_revaluation` from `inventory_adjustment`).\n\nBoth top-level keys are optional — send either or both:\n- `sync_controls_by_type` — per-transaction-type auto-sync toggles (as before)\n- `mirror_sync_controls` (Xero only) — `{\"bank_transactions\": {\"enabled\": bool}, \"payments\": {\"enabled\": bool}}`; enables the nightly mirror download for that entity. Unsubmitted keys keep their stored state.\nA request with neither key returns 422.","body":{"mode":"raw","raw":"{\n  \"sync_controls_by_type\": {\n    \"sales_order_invoice\": {\n      \"enabled\": true,\n      \"sync_start_date\": \"2024-01-01\"\n    },\n    \"credit_note\": {\n      \"enabled\": false,\n      \"sync_start_date\": null\n    },\n    \"inventory_revaluation\": {\n      \"enabled\": true,\n      \"sync_start_date\": \"2025-01-01\"\n    }\n  },\n  \"mirror_sync_controls\": {\n    \"payments\": {\n      \"enabled\": true\n    }\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/accounting/sync-settings","host":["{{baseUrl}}"],"path":["api","accounting","sync-settings"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"integration_instance_id\": 5,\n    \"integration_name\": \"Xero\",\n    \"sync_controls_by_type\": {\n      \"sales_order_invoice\": {\n        \"enabled\": true,\n        \"sync_start_date\": \"2024-01-01\",\n        \"label\": \"Sales Order Invoice\"\n      },\n      \"credit_note\": {\n        \"enabled\": false,\n        \"sync_start_date\": null,\n        \"label\": \"Credit Note\"\n      },\n      \"inventory_revaluation\": {\n        \"enabled\": true,\n        \"sync_start_date\": \"2025-01-01\",\n        \"label\": \"Inventory Revaluation\"\n      }\n    },\n    \"mirror_sync_controls\": {\n      \"bank_transactions\": {\n        \"enabled\": false,\n        \"label\": \"Bank Transactions (download from Xero)\"\n      },\n      \"payments\": {\n        \"enabled\": true,\n        \"label\": \"Payments (download from Xero)\"\n      }\n    }\n  },\n  \"message\": \"Sync settings updated successfully.\"\n}"}]},{"name":"Get Transaction Types","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/accounting/sync-settings/types","host":["{{baseUrl}}"],"path":["api","accounting","sync-settings","types"]},"description":"Returns all available accounting transaction types with their human-readable labels. Use these values as the keys of `sync_controls_by_type` when updating sync settings.\n\n**Required scope: `accounting:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/accounting/sync-settings/types","host":["{{baseUrl}}"],"path":["api","accounting","sync-settings","types"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"value\": \"sales_order_invoice\",\n      \"label\": \"Sales Order Invoice\"\n    },\n    {\n      \"value\": \"purchase_order_invoice\",\n      \"label\": \"Purchase Order Invoice\"\n    },\n    {\n      \"value\": \"credit_note\",\n      \"label\": \"Credit Note\"\n    },\n    {\n      \"value\": \"payment\",\n      \"label\": \"Payment\"\n    },\n    {\n      \"value\": \"inventory_adjustment\",\n      \"label\": \"Inventory Adjustment\"\n    },\n    {\n      \"value\": \"inventory_revaluation\",\n      \"label\": \"Inventory Revaluation\"\n    }\n  ]\n}"}]},{"name":"Get Connection Status","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/accounting/sync-settings/connection","host":["{{baseUrl}}"],"path":["api","accounting","sync-settings","connection"]},"description":"Reports whether the accounting connection is currently able to sync, and what is blocking it when it is not.\n\n**Required scope: `accounting:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nTwo independent things must both hold before any document syncs to the accounting provider:\n1. The connection is authorized — `sync_status` is `Active`. It flips to `Inactive` automatically when the provider rejects the stored authorization.\n2. At least one transaction type is enabled — see `GET /api/accounting/sync-settings`.\n\n`blocker` names whichever to fix first, in that order:\n- `connection_inactive` — the authorization is dead, which makes every per-type toggle moot. Repair it with `POST /api/accounting/sync-settings/connection`.\n- `no_types_enabled` — the connection is healthy but nothing is selected to sync. Enable types with `PUT /api/accounting/sync-settings`.\n- `null` — nothing is blocking sync.\n\n`types_enabled` / `types_total` count the per-type controls, so `0 of 24` and `23 of 24` are distinguishable states rather than one undifferentiated \"paused\".\n\nThis endpoint is read-only — it never changes the connection.\n\nReturns 404 when no accounting integration is configured."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/accounting/sync-settings/connection","host":["{{baseUrl}}"],"path":["api","accounting","sync-settings","connection"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"integration_instance_id\": 5,\n    \"integration_name\": \"Xero\",\n    \"sync_status\": \"Active\",\n    \"is_connection_active\": true,\n    \"types_enabled\": 12,\n    \"types_total\": 24,\n    \"blocker\": null\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Verify and Reactivate Connection","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/accounting/sync-settings/connection","host":["{{baseUrl}}"],"path":["api","accounting","sync-settings","connection"]},"description":"Verifies the stored authorization with one live call to the accounting provider and reactivates the connection only if that call succeeds.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Also requires the `integrations.update` permission.\n\nNo request body.\n\n`sync_status` goes `Inactive` when the provider rejects the stored authorization, so switching it back on unconditionally would re-arm a dead credential — the next scheduled sync would fail and set it straight back to `Inactive`. The verification therefore happens first:\n- Xero — reads the connected organisation\n- QuickBooks Online — reads the connected company\n\nBoth are reads; neither writes anything to the provider.\n\nOn success (200) `sync_status` becomes `Active` and the response carries the refreshed connection state, plus `probe_detail` naming the organisation or company that answered. The standing \"Integration Authorization Expired\" alert raised when the connection was deactivated is resolved at the same time, and the documents stranded while the connection was inactive are queued for sending — reactivating on its own would only resume sync from that moment forward and leave the whole outage window behind.\n\nA connection left `Inactive` is also re-verified automatically once an hour, and comes back on its own as soon as its stored authorization works again — so a connection dropped by a transient provider rejection recovers without anyone calling this endpoint.\n\nOn a failed verification (422) nothing changes, `message` carries the reason, and `probe_detail` carries the provider's own error where it gave one. Reauthorize the connection, then call this again.\n\nReturns 404 when no accounting integration is configured."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/accounting/sync-settings/connection","host":["{{baseUrl}}"],"path":["api","accounting","sync-settings","connection"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"integration_instance_id\": 5,\n    \"integration_name\": \"Xero\",\n    \"sync_status\": \"Active\",\n    \"is_connection_active\": true,\n    \"types_enabled\": 12,\n    \"types_total\": 24,\n    \"blocker\": null,\n    \"probe_detail\": \"Acme Trading Ltd\"\n  },\n  \"message\": \"Connected to Acme Trading Ltd. Automatic sync is active again.\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Accounting Integration","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/accounting/integration","host":["{{baseUrl}}"],"path":["api","accounting","integration"]},"description":"Returns the currently configured accounting integration instance (QBO or Xero).\n\n**Required scope: `accounting:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/accounting/integration","host":["{{baseUrl}}"],"path":["api","accounting","integration"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 5,\n  \"integration_id\": 3,\n  \"name\": \"My Xero\",\n  \"is_active\": true,\n  \"created_at\": \"2023-01-01T00:00:00.000000Z\"\n}"}]}]},{"name":"Financials","item":[{"name":"Recalculate Daily Financials","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/financials/daily-financials/recalculate","host":["{{baseUrl}}"],"path":["api","financials","daily-financials","recalculate"]},"description":"Triggers recalculation of the specified daily financial summary records. For large datasets (using filters), processing is dispatched as a background job.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required)\n\nRequest body:\n- ids (array, optional): Array of daily financial summary IDs to recalculate\n- filters (string, optional): Filter string to select records for recalculation (triggers background job)\n\nConcurrency: daily financials for an account are rebuilt one run at a time. If a background recalculation is already running, a synchronous request (ids) waits up to 10 seconds for it to finish and otherwise returns 409 Conflict with error key daily_financials_busy. Retry after a short delay. Requests using filters are queued and wait their turn automatically.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1500,\n    1501,\n    1502\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/financials/daily-financials/recalculate","host":["{{baseUrl}}"],"path":["api","financials","daily-financials","recalculate"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Recalculation successful\"\n}"}]},{"name":"Get Daily Financial Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/financials/daily-summary/{daily_summary}","host":["{{baseUrl}}"],"path":["api","financials","daily-summary",":daily_summary"],"variable":[{"key":"daily_summary","value":"1500","description":"Daily financial summary record ID"}]},"description":"Returns the details of a single daily financial summary record.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/financials/daily-summary/{daily_summary}","host":["{{baseUrl}}"],"path":["api","financials","daily-summary",":daily_summary"],"variable":[{"key":"daily_summary","value":"1500","description":"Daily financial summary record ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1500,\n    \"date\": \"2024-01-14\",\n    \"reportable\": {\n      \"id\": 100,\n      \"type\": \"App\\\\Models\\\\Product\",\n      \"product\": {\n        \"id\": 100,\n        \"sku\": \"SKU-001\",\n        \"brand\": \"ACME\"\n      }\n    },\n    \"quantity\": 25,\n    \"quantity_returned\": 2,\n    \"num_orders\": 10,\n    \"revenue\": 1250,\n    \"total_revenue\": 1175,\n    \"cost\": 500,\n    \"total_cost\": 460,\n    \"profit\": 715,\n    \"profit_margin\": 60.85,\n    \"created_at\": \"2024-01-14T23:59:59.000000Z\",\n    \"updated_at\": \"2024-01-15T01:00:00.000000Z\"\n  }\n}"}]},{"name":"Update Daily Financial Summary","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/financials/daily-summary/{daily_summary}","host":["{{baseUrl}}"],"path":["api","financials","daily-summary",":daily_summary"],"variable":[{"key":"daily_summary","value":"1500","description":"Daily financial summary record ID"}]},"description":"Updates the specified daily financial summary record.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required)","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/financials/daily-summary/{daily_summary}","host":["{{baseUrl}}"],"path":["api","financials","daily-summary",":daily_summary"],"variable":[{"key":"daily_summary","value":"1500","description":"Daily financial summary record ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1500\n  },\n  \"message\": \"Updated successfully.\"\n}"}]},{"name":"Delete Daily Financial Summary","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/financials/daily-summary/{daily_summary}","host":["{{baseUrl}}"],"path":["api","financials","daily-summary",":daily_summary"],"variable":[{"key":"daily_summary","value":"1500","description":"Daily financial summary record ID"}]},"description":"Deletes the specified daily financial summary record.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required)"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/financials/daily-summary/{daily_summary}","host":["{{baseUrl}}"],"path":["api","financials","daily-summary",":daily_summary"],"variable":[{"key":"daily_summary","value":"1500","description":"Daily financial summary record ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Deleted successfully.\"\n}"}]},{"name":"Create Daily Financial Summary","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/financials/daily-summary","host":["{{baseUrl}}"],"path":["api","financials","daily-summary"]},"description":"Creates a new daily financial summary record.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required)","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/financials/daily-summary","host":["{{baseUrl}}"],"path":["api","financials","daily-summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1501\n  },\n  \"message\": \"Created successfully.\"\n}"}]},{"name":"Get Financial Summary by Period","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/financials/summary-by-period?period=monthly&trailing_days=90","host":["{{baseUrl}}"],"path":["api","financials","summary-by-period"],"query":[{"key":"period","value":"monthly","description":"Period grouping (required). Values: daily, weekly, monthly, quarterly, yearly","disabled":true},{"key":"trailing_days","value":"90","description":"Number of trailing days to include in the report (optional). If omitted, uses all available data.","disabled":true}]},"description":"Returns aggregated financial data (revenue, cost, profit) grouped by the specified time period. Used for financial trend charts and reporting dashboards.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required)\n\nResponse: Array of period summaries with:\n- period: The period label (e.g. '2024-01', 'Q1 2024')\n- revenue, cost, profit: Aggregated financial totals\n- num_orders: Total order count for the period"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/financials/summary-by-period?period=monthly&trailing_days=90","host":["{{baseUrl}}"],"path":["api","financials","summary-by-period"],"query":[{"key":"period","value":"monthly","description":"Period grouping (required). Values: daily, weekly, monthly, quarterly, yearly","disabled":true},{"key":"trailing_days","value":"90","description":"Number of trailing days to include in the report (optional). If omitted, uses all available data.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"period\": \"2024-01\",\n      \"revenue\": 45250,\n      \"cost\": 18100,\n      \"profit\": 27150,\n      \"num_orders\": 182\n    },\n    {\n      \"period\": \"2023-12\",\n      \"revenue\": 38900,\n      \"cost\": 15560,\n      \"profit\": 23340,\n      \"num_orders\": 156\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Get Product Financial Summary by Period","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/financials/products/{product_id}/summary-by-period?period=monthly&trailing_days=180","host":["{{baseUrl}}"],"path":["api","financials","products",":product_id","summary-by-period"],"query":[{"key":"period","value":"monthly","description":"Period grouping (required). Values: daily, weekly, monthly, quarterly, yearly","disabled":true},{"key":"trailing_days","value":"180","description":"Number of trailing days to include (optional)","disabled":true}],"variable":[{"key":"product_id","value":"100","description":"Product ID"}]},"description":"Returns financial summary data for a specific product, grouped by the specified time period. Used for product-level financial analytics.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required)\n\nResponse: Array of period summaries with revenue, cost, profit broken down for the specific product."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/financials/products/{product_id}/summary-by-period?period=monthly&trailing_days=180","host":["{{baseUrl}}"],"path":["api","financials","products",":product_id","summary-by-period"],"query":[{"key":"period","value":"monthly","description":"Period grouping (required). Values: daily, weekly, monthly, quarterly, yearly","disabled":true},{"key":"trailing_days","value":"180","description":"Number of trailing days to include (optional)","disabled":true}],"variable":[{"key":"product_id","value":"100","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"period\": \"2024-01\",\n      \"revenue\": 1250,\n      \"cost\": 500,\n      \"profit\": 750,\n      \"num_orders\": 10,\n      \"quantity\": 25\n    },\n    {\n      \"period\": \"2023-12\",\n      \"revenue\": 980,\n      \"cost\": 392,\n      \"profit\": 588,\n      \"num_orders\": 8,\n      \"quantity\": 20\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"List Top Products by Financial Metric","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/financials/top-products?metric=revenue&limit=10","host":["{{baseUrl}}"],"path":["api","financials","top-products"],"query":[{"key":"metric","value":"revenue","description":"Metric to rank products by (required). Values: 'revenue', 'profit'","disabled":true},{"key":"limit","value":"10","description":"Number of top products to return (optional, default: 10)","disabled":true}]},"description":"Returns the top-performing products ranked by the specified financial metric. Used for dashboard widgets and financial analysis.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required)\n\nResponse fields per product:\n- product.id, product.name, product.sku, product.image_url\n- product.brand: {id, name} (when available)\n- quantity_sold: Total units sold\n- revenue: Total revenue generated\n- costs: Total cost of goods sold\n- profit: Total profit (revenue - costs)\n- num_orders: Total number of orders"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/financials/top-products?metric=revenue&limit=10","host":["{{baseUrl}}"],"path":["api","financials","top-products"],"query":[{"key":"metric","value":"revenue","description":"Metric to rank products by (required). Values: 'revenue', 'profit'","disabled":true},{"key":"limit","value":"10","description":"Number of top products to return (optional, default: 10)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product\": {\n        \"id\": 100,\n        \"name\": \"Widget A\",\n        \"sku\": \"SKU-001\",\n        \"image_url\": \"https://cdn.example.com/products/sku-001.jpg\",\n        \"brand\": {\n          \"id\": 5,\n          \"name\": \"ACME Brand\"\n        }\n      },\n      \"quantity_sold\": 250,\n      \"revenue\": 12500,\n      \"costs\": 5000,\n      \"profit\": 7500,\n      \"num_orders\": 100\n    },\n    {\n      \"product\": {\n        \"id\": 101,\n        \"name\": \"Widget B\",\n        \"sku\": \"SKU-002\",\n        \"image_url\": null,\n        \"brand\": null\n      },\n      \"quantity_sold\": 180,\n      \"revenue\": 9000,\n      \"costs\": 3600,\n      \"profit\": 5400,\n      \"num_orders\": 72\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Recalculate Sales Order Line Financials","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/financials/sales-order-line-financials/recalculate-lines","host":["{{baseUrl}}"],"path":["api","financials","sales-order-line-financials","recalculate-lines"]},"description":"Triggers recalculation of financial data for the specified sales order lines. For large datasets (using filters), processing is chunked and dispatched as background jobs (10,000 records per chunk).\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required)\n\nRequest body:\n- ids (array, optional): Array of sales order line IDs to recalculate\n- filters (string, optional): Filter string to select records for recalculation (triggers chunked background jobs)","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    8001,\n    8002,\n    8003\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/financials/sales-order-line-financials/recalculate-lines","host":["{{baseUrl}}"],"path":["api","financials","sales-order-line-financials","recalculate-lines"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Recalculation successful\"\n}"}]},{"name":"Export Sales Order Line Financials","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/financials/sales-order-lines/export","host":["{{baseUrl}}"],"path":["api","v2","financials","sales-order-lines","export"]},"description":"Exports sales order line financials. The endpoint picks one of two response modes based on dataset size:\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n1. INLINE (≤ 1,000 rows, or scope=current_page, which exports only the page/per_page window) — returns the file as the response body. format=xlsx returns an Open XML workbook (Content-Type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); format=csv returns a UTF-8 CSV stream (Content-Type text/csv).\n\n2. QUEUED (> 1,000 rows) — dispatches a background job and returns a JSON envelope with the tracked_job_log_id. Poll the tracked job log endpoints; when the job completes, fetch the resulting file from the export/download endpoint.\n\nBoth modes produce the same columns in the same order, so the file does not change shape when a dataset crosses the 1,000-row boundary.\n\nAuthentication: Requires Bearer token (Power User access required).\n\nFilters and sorts mirror the List Sales Order Line Financials endpoint.\n\nExported columns include an 'Allocation' column (after 'Revenue Allocated') — 'Cost-corrected' when the line's revenue was re-allocated in proportion to bundle-group COGS, otherwise 'Document'."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/financials/sales-order-lines/export","host":["{{baseUrl}}"],"path":["api","v2","financials","sales-order-lines","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"<binary file — Open XML workbook>"}]},{"name":"Download Sales Order Line Financials Export","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/financials/sales-order-lines/export/download?file=sales-order-line-financials-2026-05-22-13-00-00.xlsx","host":["{{baseUrl}}"],"path":["api","v2","financials","sales-order-lines","export","download"],"query":[{"key":"file","value":"sales-order-line-financials-2026-05-22-13-00-00.xlsx","description":"Filename returned in the completed tracked job's results.file. The server basenames this value and serves from the model-exports disk; unknown filenames yield 404.","disabled":true}]},"description":"Downloads a completed background export produced by Export Sales Order Line Financials (queued path). The filename is the one reported in the tracked job log's results.file field.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required).\n\nResponse is the binary file (CSV or XLSX) with an attachment Content-Disposition header. Returns 404 if the file param is empty or the file no longer exists on the model-exports disk."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/financials/sales-order-lines/export/download?file=sales-order-line-financials-2026-05-22-13-00-00.xlsx","host":["{{baseUrl}}"],"path":["api","v2","financials","sales-order-lines","export","download"],"query":[{"key":"file","value":"sales-order-line-financials-2026-05-22-13-00-00.xlsx","description":"Filename returned in the completed tracked job's results.file. The server basenames this value and serves from the model-exports disk; unknown filenames yield 404.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"<binary file>"}]},{"name":"Daily Financial Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/financials/daily-summary","host":["{{baseUrl}}"],"path":["api","v2","financials","daily-summary"]},"description":"Returns paginated daily financial summary rows. Each row is backed by a polymorphic reportable: a Product or a Financial Line Type.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFilters (Spatie QueryBuilder syntax, filter[name]=value):\n- filter[search] — searches product SKU/name and financial line type name/classification; numeric values also match row ID\n- filter[date_from] / filter[date_to] — date range on the summary date (YYYY-MM-DD)\n- filter[id] / filter[reportable_type] — exact match\n- filter[financial_line_type.{operator}] — financial line type NAME on FinancialLineType-backed rows. The financial_line_type filter value can be a plain name (\"Shipping\") or a classification-qualified value (\"Revenue: Shipping\" / \"Cost: Shipping\") — line type names are only unique per classification, so the qualified form (which the application UI now sends) constrains both classification and name. Plain names still match across all classifications for backward compatibility. Advanced text filter operators (append to the filter name): is, is_not, contains, does_not_contain, is_one_of (comma-separated), starts_with, does_not_start_with, ends_with, does_not_end_with, is_empty, is_not_empty. Bare filter[financial_line_type]=value acts as implicit 'is'. Positive operators return only financial-line-type rows that match; negative operators (is_not, does_not_*) exclude matching line-type rows while keeping all other rows (e.g. product-backed rows).\n- filter[classification.{operator}] — financial line type classification ('revenue' or 'cost'), same operators as above\n- Numeric advanced filters on: id, quantity, quantity_returned, num_orders, revenue, revenue_allocated, revenue_credits, total_revenue, cost, cost_allocated, cost_invoices, cost_returned, total_cost, profit, profit_margin (operators: equals, not_equals, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between, is_empty, is_not_empty)\n- Date advanced filters on: date, created_at, updated_at\n\nSorting: sort param, prefix with - for DESC. Allowed sorts: id, date, quantity, num_orders, total_revenue, total_cost, profit, profit_margin, created_at, updated_at. Default: -date.\n\nPagination: page, per_page (default 15).\n\nGrouped filters: this endpoint also accepts a filter_groups query param — a base64-encoded JSON tree with AND/OR conjunctions and nested groups (this is what the application UI advanced filters modal sends). Example JSON before encoding: {\"conjunction\":\"and\",\"children\":[{\"type\":\"condition\",\"condition\":{\"column\":\"financial_line_type\",\"operator\":\"is\",\"value\":\"Shipping Revenue\"}}]}. Conditions reference the same filter columns/operators listed above. When filter_groups is present, tree conditions are applied with proper parenthesization; flat filter[...] params still work for non-tree filters (search, date_from, date_to)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/financials/daily-summary","host":["{{baseUrl}}"],"path":["api","v2","financials","daily-summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 102,\n      \"date\": \"2024-03-01\",\n      \"reportable\": {\n        \"id\": 3,\n        \"type\": \"App\\\\Models\\\\FinancialLineType\",\n        \"financial_line_type\": {\n          \"id\": 3,\n          \"name\": \"Shipping Revenue\",\n          \"classification\": \"revenue\"\n        }\n      },\n      \"quantity\": 0,\n      \"quantity_returned\": 0,\n      \"num_orders\": 18,\n      \"revenue\": \"240.00\",\n      \"revenue_allocated\": \"0.00\",\n      \"revenue_credits\": \"0.00\",\n      \"revenue_discount\": \"0.00\",\n      \"total_revenue\": \"240.00\",\n      \"cost\": \"0.00\",\n      \"cost_allocated\": \"0.00\",\n      \"cost_invoices\": \"0.00\",\n      \"cost_returned\": \"0.00\",\n      \"cost_breakdown\": null,\n      \"total_cost\": \"0.00\",\n      \"profit\": \"240.00\",\n      \"profit_margin\": \"100.00\",\n      \"updated_at\": \"2024-03-02T04:10:11.000000Z\",\n      \"created_at\": \"2024-03-02T04:10:11.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1,\n  \"first_page_url\": \"https://sku-alt2.test/api/v2/financials/daily-summary?page=1\",\n  \"last_page_url\": \"https://sku-alt2.test/api/v2/financials/daily-summary?page=1\",\n  \"next_page_url\": null,\n  \"prev_page_url\": null,\n  \"path\": \"https://sku-alt2.test/api/v2/financials/daily-summary\",\n  \"links\": []\n}"}]},{"name":"Get Daily Financial Summary Column Totals","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/financials/daily-summary/aggregates?aggregates[quantity]=sum","host":["{{baseUrl}}"],"path":["api","v2","financials","daily-summary","aggregates"],"query":[{"key":"aggregates[quantity]","value":"sum","description":"aggregates[&lt;column>]=&lt;calc>. Repeat for each column to total.","disabled":true}]},"description":"Column-summary totals for the report's \"Calculate\" footer — computes Sum, Average, Min, Max, Range, or a Count variant per column over the FULL filtered set (not just the current page), in one query.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAggregatable columns: quantity, quantity_returned, num_orders, revenue, revenue_allocated, revenue_credits, total_revenue, cost, cost_allocated, cost_invoices, cost_returned, total_cost, profit. Supported calcs: sum, avg, min, max, range, count_all, count_values, count_unique, count_empty, count_not_empty. Pass them as aggregates[&lt;column>]=&lt;calc>. Totals cover the quantity, order-count and revenue/cost/profit columns.\n\nApply the same filters and search as the list endpoint to scope the totals. An unknown calc returns 422. Authentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/financials/daily-summary/aggregates?aggregates[quantity]=sum","host":["{{baseUrl}}"],"path":["api","v2","financials","daily-summary","aggregates"],"query":[{"key":"aggregates[quantity]","value":"sum","description":"aggregates[&lt;column>]=&lt;calc>. Repeat for each column to total.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_revenue\": {\n      \"calc\": \"sum\",\n      \"value\": 128450\n    },\n    \"profit\": {\n      \"calc\": \"sum\",\n      \"value\": 54120.75\n    },\n    \"num_orders\": {\n      \"calc\": \"sum\",\n      \"value\": 812\n    }\n  }\n}"}]},{"name":"Sales Order Line Financials","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/financials/sales-order-lines?per_page=10&page=1","host":["{{baseUrl}}"],"path":["api","v2","financials","sales-order-lines"],"query":[{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"page","value":"1","description":"Page number (default 1).","disabled":true}]},"description":"Returns paginated financial data at the sales order line level.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/financials/sales-order-lines?per_page=10&page=1","host":["{{baseUrl}}"],"path":["api","v2","financials","sales-order-lines"],"query":[{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"page","value":"1","description":"Page number (default 1).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 55,\n      \"sales_order_id\": 30,\n      \"product_id\": 15,\n      \"quantity\": 10,\n      \"unit_price\": \"9.99\",\n      \"unit_cost\": \"4.50\",\n      \"revenue\": \"99.90\",\n      \"cogs\": \"45.00\",\n      \"gross_profit\": \"54.90\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 100,\n  \"per_page\": 10,\n  \"total\": 998\n}"}]},{"name":"Get Sales Order Financials Column Totals","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/financials/sales-order-lines/aggregates?aggregates[quantity]=sum","host":["{{baseUrl}}"],"path":["api","v2","financials","sales-order-lines","aggregates"],"query":[{"key":"aggregates[quantity]","value":"sum","description":"aggregates[&lt;column>]=&lt;calc>. Repeat for each column to total.","disabled":true}]},"description":"Column-summary totals for the report's \"Calculate\" footer — computes Sum, Average, Min, Max, Range, or a Count variant per column over the FULL filtered set (not just the current page), in one query.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAggregatable columns: quantity, revenue, revenue_allocated, credits, discount, cogs, cogs_returned, cost_allocated, total_cost, profit. Supported calcs: sum, avg, min, max, range, count_all, count_values, count_unique, count_empty, count_not_empty. Pass them as aggregates[&lt;column>]=&lt;calc>. Totals cover the revenue, cost and profit money columns plus quantity.\n\nApply the same filters and search as the list endpoint to scope the totals. An unknown calc returns 422. Authentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/financials/sales-order-lines/aggregates?aggregates[quantity]=sum","host":["{{baseUrl}}"],"path":["api","v2","financials","sales-order-lines","aggregates"],"query":[{"key":"aggregates[quantity]","value":"sum","description":"aggregates[&lt;column>]=&lt;calc>. Repeat for each column to total.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"revenue\": {\n      \"calc\": \"sum\",\n      \"value\": 312004.55\n    },\n    \"profit\": {\n      \"calc\": \"sum\",\n      \"value\": 213784.1\n    },\n    \"quantity\": {\n      \"calc\": \"sum\",\n      \"value\": 24910\n    }\n  }\n}"}]}]},{"name":"Bills","item":[{"name":"List Bills","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/bills/list?page=1&per_page=10&sort=-created_at","host":["{{baseUrl}}"],"path":["api","bills","list"],"query":[{"key":"page","value":"1","description":"Page number (default 1)","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default 10)","disabled":true},{"key":"sort","value":"-created_at","description":"Sort field; prefix with - for descending. Allowed: id, invoice_number, invoice_date, supplier_name, supplier_id, link_type, link_reference, proration_strategy, currency_code, currency_rate, line_count, subtotal, tax_total, grand_total, allocated_amount, unallocated_amount, allocation_status, documents_count, created_at, updated_at. Default: -created_at","disabled":true}]},"description":"Paginated landed cost bills listing for the list view (`/v2/purchases/bills`). Lists every bill across all three link types (Purchase Order, Warehouse Transfer, Inbound Shipment) with computed totals, allocation status, and source (OCR vs manual).\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nUses Spatie QueryBuilder with grouped-filter (tree) support.\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write).\n\n## Pagination\n- `page` — page number (default 1)\n- `per_page` — items per page (default 10)\n\n## Sorting\n`sort` — prefix with `-` for descending. Default: `-created_at`.\nAllowed sorts: `id`, `invoice_number`, `invoice_date`, `supplier_name`, `supplier_id`, `link_type`, `link_reference`, `proration_strategy`, `currency_code`, `currency_rate`, `line_count`, `subtotal`, `tax_total`, `grand_total`, `allocated_amount`, `unallocated_amount`, `allocation_status`, `created_at`, `updated_at`\n\n## Search\n`filter[search]` — searches Invoice Number (partial), Bill ID (exact), Supplier name, Bill line descriptions, and the linked document reference (PO number / transfer reference / shipment reference).\n\n## Advanced filters\nAdvanced filters use the `filter[{column}.{operator}]=value` syntax. A bare `filter[{column}]=value` acts as an implicit `is`.\n\n**Text columns** (operators: contains, does_not_contain, is, is_not, is_one_of, starts_with, does_not_start_with, ends_with, does_not_end_with, is_empty, is_not_empty):\n- `invoice_number`, `proration_strategy`, `link_reference`, `currency_code`\n- `link_type` — values: `purchase_order`, `warehouse_transfer`, `inbound_shipment`\n- `source` — values: `ocr`, `manual`\n- `allocation_status` — values: `empty`, `unallocated`, `partial`, `allocated`\n- `supplier_name`, `line_description`, `line_nominal_code`, `cost_category` (related-table text filters)\n- `supplier_id`, `cost_category_id` (ID-select filters, text-typed)\n\n**Numeric columns** (operators: is, is_not, is_one_of, greater_than, less_than, greater_than_or_equal, less_than_or_equal, between, is_empty, is_not_empty):\n- `id`, `currency_rate`, `subtotal`, `tax_total`, `total`, `grand_total`, `allocated_amount`, `unallocated_amount`, `line_count`, `line_quantity`, `line_amount`\n- `between` takes two comma-separated values, e.g. `filter[grand_total.between]=100,500`\n\n**Date columns** (operators: is, is_not, before, after, on_or_before, on_or_after, between, is_empty, is_not_empty, today, yesterday, tomorrow, past_week, past_month, past_year, next_week, next_month, next_year, days_ago, days_from_now, past_days, next_days):\n- `invoice_date`, `created_at`, `updated_at`\n- Dates use `YYYY-MM-DD`; `between` takes two comma-separated dates\n\n**Boolean filters:**\n- `filter[has_accounting_transaction]=true|false` — whether an accounting transaction has been generated for the bill\n\n## Grouped filters (filter_groups)\n`filter_groups` — base64-encoded JSON tree supporting AND/OR conjunctions and nested groups. Takes precedence over flat `filter[...]` params when present.\n\nJSON structure before encoding:\n```json\n{\n  \"conjunction\": \"and\",\n  \"children\": [\n    {\"type\": \"condition\", \"condition\": {\"column\": \"allocation_status.is\", \"operator\": \"is\", \"value\": \"partial\"}},\n    {\"type\": \"group\", \"group\": {\"conjunction\": \"or\", \"children\": [...]}}\n  ]\n}\n```\n\n## Response extras\nOn top of Laravel's standard paginator JSON, the response includes:\n- `unfiltered_total` — total bill count with all filters cleared (for the export modal's \"All records\" label)\n- `summary` — tenant-currency stat card totals across the **filtered** result set: `total_billed_tenant`, `total_allocated_tenant`, `total_unallocated_tenant`, `bill_count`\n\nEach bill includes `documents_count` (number of attached source documents) and `document_filenames` (their file names). Sortable by `documents_count`. Filter with `filter[documents_count]` (e.g. `filter[documents_count.is]=0` for bills with no source document) or `filter[document_filename]` to match a source document's file name."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/bills/list?page=1&per_page=10&sort=-created_at","host":["{{baseUrl}}"],"path":["api","bills","list"],"query":[{"key":"page","value":"1","description":"Page number (default 1)","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default 10)","disabled":true},{"key":"sort","value":"-created_at","description":"Sort field; prefix with - for descending. Allowed: id, invoice_number, invoice_date, supplier_name, supplier_id, link_type, link_reference, proration_strategy, currency_code, currency_rate, line_count, subtotal, tax_total, grand_total, allocated_amount, unallocated_amount, allocation_status, documents_count, created_at, updated_at. Default: -created_at","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 12,\n      \"invoice_number\": \"INV-2026-0042\",\n      \"invoice_date\": \"2026-05-28\",\n      \"due_date\": \"2026-06-27\",\n      \"payment_status\": \"unpaid\",\n      \"payment_due_status\": \"not_due\",\n      \"days_until_due\": 30,\n      \"supplier_id\": 3,\n      \"supplier_name\": \"Global Freight Co\",\n      \"supplier_type\": \"service\",\n      \"link_type\": \"purchase_order\",\n      \"link_id\": 42,\n      \"link_reference\": \"PO-00042\",\n      \"link_url_path\": \"/orders/purchase-orders/42\",\n      \"proration_strategy\": \"cost_based\",\n      \"currency_code\": \"USD\",\n      \"currency_rate\": 1,\n      \"tenant_currency_code\": \"USD\",\n      \"line_count\": 2,\n      \"subtotal\": 750,\n      \"tax_total\": 0,\n      \"grand_total\": 750,\n      \"subtotal_tenant\": 750,\n      \"allocated_amount\": 500,\n      \"allocated_amount_tenant\": 500,\n      \"unallocated_amount\": 250,\n      \"allocation_status\": \"partial\",\n      \"cost_categories\": [\n        {\n          \"id\": 1,\n          \"name\": \"Freight\"\n        },\n        {\n          \"id\": 2,\n          \"name\": \"Duty\"\n        }\n      ],\n      \"has_accounting_transaction\": true,\n      \"source\": \"ocr\",\n      \"ocr_extraction_id\": 7,\n      \"settlement_reference_count\": 0,\n      \"documents_count\": 2,\n      \"document_filenames\": [\n        \"proforma.pdf\",\n        \"freight-invoice.pdf\"\n      ],\n      \"created_at\": \"2026-05-28T14:22:10.000000Z\",\n      \"updated_at\": \"2026-06-02T09:15:44.000000Z\"\n    }\n  ],\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/bills/list?page=1\",\n  \"from\": 1,\n  \"last_page\": 5,\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/bills/list?page=5\",\n  \"links\": [],\n  \"next_page_url\": \"{{protocol}}{{domain}}/api/bills/list?page=2\",\n  \"path\": \"{{protocol}}{{domain}}/api/bills/list\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 10,\n  \"total\": 47,\n  \"unfiltered_total\": 143,\n  \"summary\": {\n    \"total_billed_tenant\": 38450.25,\n    \"total_allocated_tenant\": 31200.1,\n    \"total_unallocated_tenant\": 7250.15,\n    \"bill_count\": 47\n  }\n}"}]},{"name":"Export Bills","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/bills/list/export?format=xlsx&content_mode=summary&scope=filtered","host":["{{baseUrl}}"],"path":["api","bills","list","export"],"query":[{"key":"format","value":"xlsx","description":"File format: xlsx (default) or csv","disabled":true},{"key":"content_mode","value":"summary","description":"summary (one row per bill, default) or line_items (one row per bill line)","disabled":true},{"key":"scope","value":"filtered","description":"all | filtered (default) | current_page | selected","disabled":true}]},"description":"Export landed cost bills as XLSX or CSV. The response is a binary file download, not JSON.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write).\n\n## Parameters\n- `format` — `xlsx` (default) or `csv`\n- `content_mode` — `summary` (one row per bill, default) or `line_items` (one row per bill line; bill columns repeat per line, line-item columns are appended)\n- `scope` — which bills to export:\n  - `all` — every bill, ignoring active filters\n  - `filtered` — bills matching the current `filter[...]` / `filter_groups` params (default)\n  - `current_page` — only the current page (honors `page` / `per_page` / `sort`)\n  - `selected` — only the bills in `ids`\n- `columns` — comma-separated bill column keys to include (preserves order). Omit for all columns. Available keys: `id`, `invoice_number`, `invoice_date`, `supplier_name`, `link_type`, `link_reference`, `proration_strategy`, `currency_code`, `currency_rate`, `line_count`, `subtotal`, `tax_total`, `grand_total`, `allocated_amount`, `unallocated_amount`, `allocation_status`, `cost_categories`, `has_accounting_transaction`, `source`, `settlement_reference_count`, `created_at`, `updated_at`\n- `ids` — comma-separated bill IDs (required when `scope=selected`)\n\nAll `filter[...]`, `filter_groups`, and `sort` params from the List Bills endpoint also apply when `scope=filtered`.\n\nIn `line_items` mode these line columns are always appended: Line Description, Line Cost Category, Line Qty, Line Amount, Line Extended, Line Tax Rate.\n\n## Response\nBinary file download:\n- XLSX: `Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`\n- CSV: `Content-Type: text/csv`\n- Filename: `landed-cost-bills-{timestamp}.{ext}` (summary) or `landed-cost-bills-detail-{timestamp}.{ext}` (line_items)\n\n**Documents.** `documents_count` is available as a `columns` key and exports as a `Documents` column holding the number of attached supporting documents."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/bills/list/export?format=xlsx&content_mode=summary&scope=filtered","host":["{{baseUrl}}"],"path":["api","bills","list","export"],"query":[{"key":"format","value":"xlsx","description":"File format: xlsx (default) or csv","disabled":true},{"key":"content_mode","value":"summary","description":"summary (one row per bill, default) or line_items (one row per bill line)","disabled":true},{"key":"scope","value":"filtered","description":"all | filtered (default) | current_page | selected","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary XLSX/CSV file)"}]},{"name":"Get Bill","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/bills/{id}","host":["{{baseUrl}}"],"path":["api","bills",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns a bill with its supplier, lines, allocations, and related purchase order / warehouse transfer data.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/bills/{id}","host":["{{baseUrl}}"],"path":["api","bills",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"supplier_id\": 3,\n    \"supplier_name\": \"Acme Supplier Ltd\",\n    \"supplier_type\": \"service\",\n    \"invoice_number\": \"INV-2024-001\",\n    \"invoice_date\": \"2024-01-15\",\n    \"due_date\": \"2024-02-14\",\n    \"due_date_source\": \"derived\",\n    \"payment_due_status\": \"not_due\",\n    \"days_until_due\": 30,\n    \"link_type\": \"purchase_order\",\n    \"link_id\": 42,\n    \"currency_id\": 1,\n    \"currency_code\": \"USD\",\n    \"currency_rate\": 1,\n    \"proration_strategy\": \"cost_based\",\n    \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"lines\": [\n      {\n        \"id\": 1,\n        \"description\": \"Product A - Qty 100\",\n        \"quantity\": 100,\n        \"amount\": 5.5,\n        \"extended_amount\": 550,\n        \"nominal_code_id\": 10,\n        \"tax_rate_id\": null,\n        \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n        \"updated_at\": \"2024-01-15T10:00:00.000000Z\"\n      }\n    ],\n    \"descriptions\": [\n      \"Product A - Qty 100\"\n    ],\n    \"total_amount\": 550,\n    \"totals\": {\n      \"line_count\": 1,\n      \"amount\": 550,\n      \"amount_in_order_currency\": 550,\n      \"tax\": 0,\n      \"grand_total\": 550\n    },\n    \"allocations\": [\n      {\n        \"id\": 1,\n        \"allocation_amount\": 550,\n        \"allocation_percentage\": 100,\n        \"proration_strategy\": \"cost_based\",\n        \"purchase_order_line_id\": 55,\n        \"warehouse_transfer_line_id\": null,\n        \"purchase_order_line\": {\n          \"id\": 55,\n          \"description\": \"Product A\",\n          \"quantity\": 100,\n          \"product\": {\n            \"id\": 10,\n            \"name\": \"Product A\",\n            \"sku\": \"PROD-A-001\"\n          }\n        },\n        \"warehouse_transfer_line\": null\n      }\n    ],\n    \"ledger\": {\n      \"enabled\": true,\n      \"entries\": [\n        {\n          \"id\": 9954,\n          \"status\": \"draft\",\n          \"reference\": \"BILL-319\",\n          \"total\": 250,\n          \"posted_at\": null,\n          \"effective_at\": \"2026-03-30T00:00:00+00:00\"\n        }\n      ]\n    }\n  }\n}"}]},{"name":"Update Bill","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/bills/{id}","host":["{{baseUrl}}"],"path":["api","bills",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Updates an existing bill's header data and allocations.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- invoice_number (required): Updated invoice number\n- invoice_date (required): Updated invoice date (YYYY-MM-DD)\n- supplier_id (required): Supplier ID — must be a service-type supplier\n- currency_id (optional): Currency ID\n- currency_rate (optional): Exchange rate\n- proration_strategy (optional): One of cost_based, quantity_based, manual\n- allocations (optional): Updated allocations array (replaces existing)\n\nAllowed `proration_strategy` values: `cost_based`, `quantity_based`, `weight_based`, `volume_based`, `specific_line`, `revenue_based`, `manual`, `duty_estimate_based`. `duty_estimate_based` (\"By estimated duty\") splits the bill across purchase order lines in proportion to each line's estimated customs duty — the duty frozen at receipt, or the shipment-stage estimate for units not yet received. If every covered line's estimated duty is zero it falls back to `cost_based` and the bill response carries an `allocation_warnings` entry with code `duty_estimates_zero_fallback`.","body":{"mode":"raw","raw":"{\n  \"invoice_number\": \"INV-2024-001-REVISED\",\n  \"invoice_date\": \"2024-01-20\",\n  \"supplier_id\": 3,\n  \"currency_id\": 1,\n  \"currency_rate\": 1,\n  \"proration_strategy\": \"cost_based\",\n  \"allocations\": [\n    {\n      \"purchase_order_line_id\": 55,\n      \"allocation_amount\": 550\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/bills/{id}","host":["{{baseUrl}}"],"path":["api","bills",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"supplier_id\": 3,\n    \"supplier_name\": \"Acme Supplier Ltd\",\n    \"supplier_type\": \"service\",\n    \"invoice_number\": \"INV-2024-001-REVISED\",\n    \"invoice_date\": \"2024-01-20\",\n    \"link_type\": \"purchase_order\",\n    \"link_id\": 42,\n    \"total_amount\": 550\n  }\n}"}]},{"name":"Delete Bill","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/bills/{id}","host":["{{baseUrl}}"],"path":["api","bills",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Deletes a bill.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/bills/{id}","host":["{{baseUrl}}"],"path":["api","bills",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Bill deleted successfully\"\n}"}]},{"name":"Get Bill Line Items","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/bills/{id}/line-items","host":["{{baseUrl}}"],"path":["api","bills",":id","line-items"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Lean lines + allocations payload for the expandable row in the landed cost bills list view.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the bill's lines (with tax rate and cost category), its allocations (with the target PO/WT line, parent document ID, and product), and aggregate totals.\n\n`target_type` is `purchase_order_line` or `warehouse_transfer_line`; `target_document_id` is the parent purchase order / warehouse transfer ID.\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/bills/{id}/line-items","host":["{{baseUrl}}"],"path":["api","bills",":id","line-items"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"lines\": [\n      {\n        \"id\": 31,\n        \"description\": \"Ocean freight — container MSKU1234567\",\n        \"quantity\": 1,\n        \"amount\": 500,\n        \"extended_amount\": 500,\n        \"tax_rate_id\": null,\n        \"tax_rate_name\": null,\n        \"tax_rate\": null,\n        \"cost_category\": {\n          \"id\": 1,\n          \"name\": \"Freight\"\n        }\n      },\n      {\n        \"id\": 32,\n        \"description\": \"Import duty\",\n        \"quantity\": 1,\n        \"amount\": 250,\n        \"extended_amount\": 250,\n        \"tax_rate_id\": 2,\n        \"tax_rate_name\": \"GST 10%\",\n        \"tax_rate\": 10,\n        \"cost_category\": {\n          \"id\": 2,\n          \"name\": \"Duty\"\n        }\n      }\n    ],\n    \"allocations\": [\n      {\n        \"id\": 55,\n        \"purchase_order_line_id\": 101,\n        \"warehouse_transfer_line_id\": null,\n        \"target_type\": \"purchase_order_line\",\n        \"target_document_id\": 42,\n        \"product_id\": 10,\n        \"product_sku\": \"PROD-A-001\",\n        \"product_name\": \"Product A\",\n        \"description\": \"Product A\",\n        \"allocation_amount\": 500,\n        \"allocation_amount_tenant\": 500,\n        \"proration_strategy\": \"cost_based\"\n      }\n    ],\n    \"totals\": {\n      \"subtotal\": 750,\n      \"allocated\": 500,\n      \"allocated_tenant\": 500\n    }\n  }\n}"}]},{"name":"Get Bill Payees","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/bills/payees","host":["{{baseUrl}}"],"path":["api","bills","payees"]},"description":"Lists the distinct suppliers actually referenced by existing bills, ordered by name (may include grandfathered goods suppliers on migrated bills). Backs the Supplier filter on the bills list page.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFor the payee picker on landed-cost / cost-entry create/edit forms, use GET /api/bills/eligible-payees instead.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/bills/payees","host":["{{baseUrl}}"],"path":["api","bills","payees"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3,\n      \"name\": \"Freight Co.\",\n      \"company_name\": \"Freight Co. LLC\",\n      \"email\": \"billing@freightco.test\",\n      \"type\": \"service\",\n      \"type_label\": \"Service Provider\"\n    },\n    {\n      \"id\": 8,\n      \"name\": \"DHL Express\",\n      \"company_name\": \"DHL\",\n      \"email\": \"ap@dhl.test\",\n      \"type\": \"service\",\n      \"type_label\": \"Service Provider\"\n    }\n  ]\n}"}]},{"name":"Get Eligible Bill Payees","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/bills/eligible-payees","host":["{{baseUrl}}"],"path":["api","bills","eligible-payees"]},"description":"Lists all suppliers eligible to be billed: non-archived, service-type suppliers, ordered by name. Backs the payee picker on the landed-cost and cost-entry create/edit forms (where new bills are created).\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nDistinct from GET /api/bills/payees, which backs the bills-list Supplier filter and returns only suppliers that already appear on a bill (and may include grandfathered goods suppliers).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/bills/eligible-payees","host":["{{baseUrl}}"],"path":["api","bills","eligible-payees"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3,\n      \"name\": \"Freight Co.\",\n      \"company_name\": \"Freight Co. LLC\",\n      \"email\": \"billing@freightco.test\",\n      \"type\": \"service\",\n      \"type_label\": \"Service Provider\"\n    },\n    {\n      \"id\": 8,\n      \"name\": \"DHL Express\",\n      \"company_name\": \"DHL\",\n      \"email\": \"ap@dhl.test\",\n      \"type\": \"service\",\n      \"type_label\": \"Service Provider\"\n    }\n  ]\n}"}]},{"name":"Create Bill","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/bills","host":["{{baseUrl}}"],"path":["api","bills"]},"description":"Creates a standalone bill. Note: This endpoint is currently not used in practice — bills are typically created via purchase order or warehouse transfer nested routes.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- supplier_id (required): Supplier ID — must be a service-type supplier\n- invoice_number (required, unique): Invoice reference number\n- invoice_date (required): Invoice date (YYYY-MM-DD)\n- link_type (required): Type of link — 'purchase_order' or 'warehouse_transfer'\n- link_id (required): ID of the linked record\n- currency_id (optional): Currency ID\n- currency_rate (optional): Exchange rate\n- proration_strategy (required): One of cost_based, quantity_based, manual\n- lines (required): Array of bill lines\n  - lines.*.description (required): Line description — must be unique among the lines on this bill (case-insensitive)\n  - lines.*.quantity: Quantity\n  - lines.*.amount: Unit amount\n  - lines.*.nominal_code_id (optional, nullable): The line's debit (expense/clearing) account that faces Accounts Payable. Optional — omit it or send null and NO default is frozen onto the line at save; the GL debit is resolved at posting time from the bill's link_type (landed-cost bills linked to a purchase order / warehouse transfer / inbound shipment capitalize to the configured inventory_control account).\n  - lines.*.tax_rate_id: Tax rate ID (optional)\n- allocations (optional): Array of allocation objects\n  - allocations.*.purchase_order_line_id: PO line ID\n  - allocations.*.allocation_amount: Amount to allocate\n\nLine descriptions must be unique within a bill. Two lines on the same bill cannot share a description, and the comparison ignores letter case — \"Freight\" and \"freight\" collide. A duplicate is rejected with 422 before anything is saved.\n\nAllowed `proration_strategy` values: `cost_based`, `quantity_based`, `weight_based`, `volume_based`, `specific_line`, `revenue_based`, `manual`, `duty_estimate_based`. `duty_estimate_based` (\"By estimated duty\") splits the bill across purchase order lines in proportion to each line's estimated customs duty — the duty frozen at receipt, or the shipment-stage estimate for units not yet received. If every covered line's estimated duty is zero it falls back to `cost_based` and the bill response carries an `allocation_warnings` entry with code `duty_estimates_zero_fallback`.","body":{"mode":"raw","raw":"{\n  \"supplier_id\": 3,\n  \"invoice_number\": \"INV-2024-001\",\n  \"invoice_date\": \"2024-01-15\",\n  \"link_type\": \"purchase_order\",\n  \"link_id\": 42,\n  \"currency_id\": 1,\n  \"currency_rate\": 1,\n  \"proration_strategy\": \"cost_based\",\n  \"lines\": [\n    {\n      \"description\": \"Product A - 100 units\",\n      \"quantity\": 100,\n      \"amount\": 5.5,\n      \"nominal_code_id\": 10,\n      \"tax_rate_id\": null\n    }\n  ],\n  \"allocations\": [\n    {\n      \"purchase_order_line_id\": 55,\n      \"allocation_amount\": 550\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/bills","host":["{{baseUrl}}"],"path":["api","bills"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"supplier_id\": 3,\n    \"supplier_name\": \"Acme Supplier Ltd\",\n    \"supplier_type\": \"service\",\n    \"invoice_number\": \"INV-2024-001\",\n    \"invoice_date\": \"2024-01-15\",\n    \"link_type\": \"purchase_order\",\n    \"link_id\": 42,\n    \"currency_code\": \"USD\",\n    \"currency_rate\": 1,\n    \"proration_strategy\": \"cost_based\",\n    \"total_amount\": 550,\n    \"totals\": {\n      \"line_count\": 1,\n      \"amount\": 550,\n      \"amount_in_order_currency\": 550,\n      \"tax\": 0,\n      \"grand_total\": 550\n    }\n  }\n}"}]},{"name":"Bulk Delete Bills","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/bills","host":["{{baseUrl}}"],"path":["api","bills"]},"description":"Bulk delete landed cost bills by explicit IDs or by list filters (\"all filtered\" scope).\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nProvide ONE of:\n- `ids` — array of bill IDs (each must exist)\n- `filters` — object of List Bills filter params (same keys as the `filter[...]` query params, plus optional `filter_groups`); the matching bill IDs are resolved server-side\n- `apply_to_all: true` — delete ALL bills (optionally narrowed by `filters`); without this flag an empty/missing `filters` object deletes nothing\n\nBehavior:\n- **25 bills or fewer** → deleted synchronously; response includes per-bill results\n- **More than 25 bills** → dispatched as a Tracked Job (`Delete Landed Cost Bills: N bill(s)`); response returns `tracked_job_log_id` for the tracked job log endpoints\n- Deleting a bill removes its lines/allocations and triggers FIFO landed-cost recalculation on affected layers\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/bills","host":["{{baseUrl}}"],"path":["api","bills"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"deleted\": 3,\n    \"failed\": []\n  },\n  \"message\": \"Deleted 3 bill(s)\"\n}"}]},{"name":"Update Bill Lines","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/bills/{id}/lines","host":["{{baseUrl}}"],"path":["api","bills",":id","lines"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Batch updates the lines of a bill. Replaces existing lines with the provided collection.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody:\n- lines (required): Array of bill line objects\n  - lines.*.id (optional): Existing line ID (omit for new lines)\n  - lines.*.description: Line description\n  - lines.*.quantity: Quantity\n  - lines.*.amount: Unit amount\n  - lines.*.nominal_code_id (optional, nullable): The line's debit (expense/clearing) account. Send an explicit null to CLEAR the nominal code on the line (it persists as null). OMIT the field entirely to leave an existing line's code untouched. No inventory_control default is frozen onto the line — when the line carries no code, the GL debit is resolved at posting time from the bill's link_type (landed-cost bills linked to a purchase order / warehouse transfer / inbound shipment capitalize to the configured inventory_control account).\n  - lines.*.tax_rate_id: Tax rate ID (optional)","body":{"mode":"raw","raw":"{\n  \"lines\": [\n    {\n      \"id\": 1,\n      \"description\": \"Product A - Updated\",\n      \"quantity\": 100,\n      \"amount\": 6,\n      \"nominal_code_id\": 10,\n      \"tax_rate_id\": null\n    },\n    {\n      \"description\": \"New Line Item\",\n      \"quantity\": 50,\n      \"amount\": 3,\n      \"nominal_code_id\": 10\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/bills/{id}/lines","host":["{{baseUrl}}"],"path":["api","bills",":id","lines"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"supplier_id\": 3,\n    \"supplier_name\": \"Acme Supplier Ltd\",\n    \"supplier_type\": \"service\",\n    \"invoice_number\": \"INV-2024-001\",\n    \"invoice_date\": \"2024-01-15\",\n    \"lines\": [\n      {\n        \"id\": 1,\n        \"description\": \"Product A - Updated\",\n        \"quantity\": 100,\n        \"amount\": 6,\n        \"extended_amount\": 600\n      },\n      {\n        \"id\": 2,\n        \"description\": \"New Line Item\",\n        \"quantity\": 50,\n        \"amount\": 3,\n        \"extended_amount\": 150\n      }\n    ],\n    \"total_amount\": 750,\n    \"totals\": {\n      \"line_count\": 2,\n      \"amount\": 750,\n      \"amount_in_order_currency\": 750,\n      \"tax\": 0,\n      \"grand_total\": 750\n    }\n  }\n}"}]},{"name":"Get Available Lines for Manual Allocation","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/bills/{id}/available-lines","host":["{{baseUrl}}"],"path":["api","bills",":id","available-lines"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns purchase order and warehouse transfer lines that are available for manual cost allocation to this bill. Includes pre-calculated allocation amounts.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/bills/{id}/available-lines","host":["{{baseUrl}}"],"path":["api","bills",":id","available-lines"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 55,\n      \"type\": \"purchase_order_line\",\n      \"description\": \"Product A\",\n      \"quantity\": 100,\n      \"unit_cost\": 5.5,\n      \"total_cost\": 550,\n      \"suggested_allocation_amount\": 550,\n      \"product\": {\n        \"id\": 10,\n        \"name\": \"Product A\",\n        \"sku\": \"PROD-A-001\"\n      }\n    }\n  ]\n}"}]},{"name":"Generate Accounting Transaction","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/bills/{id}/generate-accounting-transaction","host":["{{baseUrl}}"],"path":["api","bills",":id","generate-accounting-transaction"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Generates (or regenerates) the accounting posting for a landed-cost bill. On tenants using the v2 ledger, this synchronously rebuilds the bill's ledger journal entry; on legacy tenants it creates the accounting transaction. Safe to call repeatedly — an existing entry is superseded, not duplicated.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/bills/{id}/generate-accounting-transaction","host":["{{baseUrl}}"],"path":["api","bills",":id","generate-accounting-transaction"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"ledger_entries\": [\n      {\n        \"id\": 9954,\n        \"status\": \"draft\",\n        \"reference\": \"BILL-319\",\n        \"total\": 250,\n        \"posted_at\": null,\n        \"effective_at\": \"2026-03-30T00:00:00+00:00\"\n      }\n    ]\n  },\n  \"message\": \"Ledger entry generated successfully.\"\n}"}]},{"name":"Create Cost Entries from Bill Lines","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/bills/{bill}/cost-entries","host":["{{baseUrl}}"],"path":["api","bills",":bill","cost-entries"],"variable":[{"key":"bill","value":"15","description":"Bill ID"}]},"description":"Create one cost entry per selected bill line, each tracking a chosen amount of that line for profitability analysis. This is the `scope: \"lines\"` mode of the endpoint; to track the whole remaining bill as a single entry, see \"Create Cost Entry from Bill\" (same URL, `scope: \"entire\"`).\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach allocation amount may be the full line total or a partial amount, but cannot exceed what is still untracked on that line (check with the lines-availability endpoints first). When more than one line is sent, each entry's description is the `name` with the line's description appended. Entries inherit the bill's supplier, currency, exchange rate, and invoice number, are dated with the bill's invoice date, and start in `pending_allocation` status.\n\nRequest fields:\n- `scope` (required) - `lines` for this mode.\n- `name` (required, max 255) - Base description for the cost entries.\n- `cost_entry_type_id` (optional) - ID of a cost entry type (see `GET /api/cost-entry-types`); applies to every entry created. Omit or send null to leave them untyped.\n- `notes` (optional, max 1000) - Free-form notes.\n- `line_allocations` (required in this mode, min 1) - Array of allocations:\n  - `line_allocations[].bill_line_id` (required, integer) - ID of a line on this bill.\n  - `line_allocations[].amount` (required, numeric, min 0.01) - Amount of that line to track.\n\nReturns 422 when a line does not belong to the bill or an amount exceeds the line's untracked remainder.\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write).","body":{"mode":"raw","raw":"{\n  \"scope\": \"lines\",\n  \"name\": \"June shipment landed costs\",\n  \"cost_entry_type_id\": 3,\n  \"notes\": \"Split by freight and duty lines\",\n  \"line_allocations\": [\n    {\n      \"bill_line_id\": 31,\n      \"amount\": 1850\n    },\n    {\n      \"bill_line_id\": 32,\n      \"amount\": 650\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/bills/{bill}/cost-entries","host":["{{baseUrl}}"],"path":["api","bills",":bill","cost-entries"],"variable":[{"key":"bill","value":"15","description":"Bill ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"message\": \"Created 2 cost entry(s) from bill\",\n    \"cost_entries\": [\n      {\n        \"id\": 89,\n        \"supplier_id\": 12,\n        \"supplier_name\": \"Pacific Freight Forwarding\",\n        \"reference_number\": \"INV-20441\",\n        \"description\": \"June shipment landed costs - Ocean freight (container MSKU1234567)\",\n        \"cost_date\": \"2026-06-15\",\n        \"amount\": 1850,\n        \"amount_in_tenant_currency\": 1850,\n        \"currency_id\": 1,\n        \"currency_code\": \"USD\",\n        \"currency_rate\": 1,\n        \"nominal_code_id\": null,\n        \"nominal_code_name\": null,\n        \"cost_entry_type_id\": 3,\n        \"cost_entry_type\": {\n          \"id\": 3,\n          \"name\": \"Freight\"\n        },\n        \"status\": \"pending_allocation\",\n        \"bill_id\": 15,\n        \"bill_line_id\": 31,\n        \"source_type\": \"bill_line\",\n        \"source_description\": \"Bill #INV-20441 - Line: Ocean freight (container MSKU1234567)\",\n        \"source_link_type\": null,\n        \"source_id\": null,\n        \"source_label\": null,\n        \"source_period\": null,\n        \"reconciliation\": null,\n        \"created_at\": \"2026-07-07T14:02:11.000000Z\",\n        \"updated_at\": \"2026-07-07T14:02:11.000000Z\",\n        \"totals\": {\n          \"allocated\": 0,\n          \"unallocated\": 1850,\n          \"is_fully_allocated\": false\n        }\n      },\n      {\n        \"id\": 90,\n        \"supplier_id\": 12,\n        \"supplier_name\": \"Pacific Freight Forwarding\",\n        \"reference_number\": \"INV-20441\",\n        \"description\": \"June shipment landed costs - Import duty (HS 8516.71)\",\n        \"cost_date\": \"2026-06-15\",\n        \"amount\": 650,\n        \"amount_in_tenant_currency\": 650,\n        \"currency_id\": 1,\n        \"currency_code\": \"USD\",\n        \"currency_rate\": 1,\n        \"nominal_code_id\": null,\n        \"nominal_code_name\": null,\n        \"cost_entry_type_id\": 3,\n        \"cost_entry_type\": {\n          \"id\": 3,\n          \"name\": \"Freight\"\n        },\n        \"status\": \"pending_allocation\",\n        \"bill_id\": 15,\n        \"bill_line_id\": 32,\n        \"source_type\": \"bill_line\",\n        \"source_description\": \"Bill #INV-20441 - Line: Import duty (HS 8516.71)\",\n        \"source_link_type\": null,\n        \"source_id\": null,\n        \"source_label\": null,\n        \"source_period\": null,\n        \"reconciliation\": null,\n        \"created_at\": \"2026-07-07T14:02:11.000000Z\",\n        \"updated_at\": \"2026-07-07T14:02:11.000000Z\",\n        \"totals\": {\n          \"allocated\": 0,\n          \"unallocated\": 650,\n          \"is_fully_allocated\": false\n        }\n      }\n    ]\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Get Bill Profitability Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/bills/{bill}/profitability-summary","host":["{{baseUrl}}"],"path":["api","bills",":bill","profitability-summary"],"variable":[{"key":"bill","value":"15","description":"Bill ID"}]},"description":"Get a profitability-tracking summary for a bill: how much of the bill's total has been turned into cost entries, how much remains untracked, and the full list of cost entries created from the bill.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nResponse fields:\n- `bill_total` - The bill's grand total.\n- `tracked_for_profitability` - Sum of the cost entries created from this bill, in the account's base currency.\n- `not_tracked` - Remainder of the bill total not yet tracked.\n- `tracking_percentage` - Tracked amount as a percentage of the bill total (rounded to 2 decimals).\n- `cost_entries` - Every cost entry created from this bill or its lines, including its allocation status and allocated/unallocated totals.\n\nUse this to decide whether more of the bill can still be tracked via `POST /api/bills/:bill/cost-entries`.\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/bills/{bill}/profitability-summary","host":["{{baseUrl}}"],"path":["api","bills",":bill","profitability-summary"],"variable":[{"key":"bill","value":"15","description":"Bill ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"bill_id\": 15,\n    \"bill_total\": 2500,\n    \"tracked_for_profitability\": 1850,\n    \"not_tracked\": 650,\n    \"tracking_percentage\": 74,\n    \"cost_entries\": [\n      {\n        \"id\": 89,\n        \"supplier_id\": 12,\n        \"supplier_name\": \"Pacific Freight Forwarding\",\n        \"reference_number\": \"INV-20441\",\n        \"description\": \"June shipment landed costs - Ocean freight (container MSKU1234567)\",\n        \"cost_date\": \"2026-06-15\",\n        \"amount\": 1850,\n        \"amount_in_tenant_currency\": 1850,\n        \"currency_id\": 1,\n        \"currency_code\": \"USD\",\n        \"currency_rate\": 1,\n        \"nominal_code_id\": null,\n        \"nominal_code_name\": null,\n        \"cost_entry_type_id\": 3,\n        \"cost_entry_type\": {\n          \"id\": 3,\n          \"name\": \"Freight\"\n        },\n        \"status\": \"pending_allocation\",\n        \"bill_id\": 15,\n        \"bill_line_id\": 31,\n        \"source_type\": \"bill_line\",\n        \"source_description\": \"Bill #INV-20441 - Line: Ocean freight (container MSKU1234567)\",\n        \"source_link_type\": null,\n        \"source_id\": null,\n        \"source_label\": null,\n        \"source_period\": null,\n        \"reconciliation\": null,\n        \"created_at\": \"2026-07-07T14:02:11.000000Z\",\n        \"updated_at\": \"2026-07-07T14:02:11.000000Z\",\n        \"totals\": {\n          \"allocated\": 0,\n          \"unallocated\": 1850,\n          \"is_fully_allocated\": false\n        }\n      }\n    ]\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Get Bill Lines Availability","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/bills/{bill}/lines-availability","host":["{{baseUrl}}"],"path":["api","bills",":bill","lines-availability"],"variable":[{"key":"bill","value":"15","description":"Bill ID"}]},"description":"List every line on a bill with how much of each line has already been tracked as cost entries and how much is still available, plus bill-level totals.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nResponse fields:\n- `bill_total`, `total_tracked`, `total_available` - Bill-level totals; `total_available` is the bill total minus the sum tracked across all lines.\n- `lines[]` - Per line: `line_total`, `already_tracked`, `available_for_tracking`, and `tracking_percentage` (0 for zero-total lines).\n\nUse this before calling `POST /api/bills/:bill/cost-entries` with `scope: \"lines\"` to choose valid allocation amounts.\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/bills/{bill}/lines-availability","host":["{{baseUrl}}"],"path":["api","bills",":bill","lines-availability"],"variable":[{"key":"bill","value":"15","description":"Bill ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"bill_id\": 15,\n    \"bill_total\": 2500,\n    \"total_tracked\": 1850,\n    \"total_available\": 650,\n    \"lines\": [\n      {\n        \"bill_line_id\": 31,\n        \"description\": \"Ocean freight (container MSKU1234567)\",\n        \"line_total\": 1850,\n        \"already_tracked\": 1850,\n        \"available_for_tracking\": 0,\n        \"tracking_percentage\": 100\n      },\n      {\n        \"bill_line_id\": 32,\n        \"description\": \"Import duty (HS 8516.71)\",\n        \"line_total\": 650,\n        \"already_tracked\": 0,\n        \"available_for_tracking\": 650,\n        \"tracking_percentage\": 0\n      }\n    ]\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Get Bill Line Availability","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/bills/{bill}/lines/{billLine}/availability","host":["{{baseUrl}}"],"path":["api","bills",":bill","lines",":billLine","availability"],"variable":[{"key":"bill","value":"15","description":"Bill ID"},{"key":"billLine","value":"32","description":"Bill line ID (must belong to the bill)"}]},"description":"Get the tracking availability for a single bill line: its total, how much has already been tracked as cost entries, and how much is still available.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nResponse fields:\n- `line_total` - The line's extended total.\n- `already_tracked` - Amount of this line already covered by cost entries.\n- `available_for_tracking` - Remainder that can still be tracked.\n- `tracking_percentage` - Tracked amount as a percentage of the line total (0 for zero-total lines).\n\nReturns 404 when the line exists but does not belong to the given bill.\n\nUse this before calling `POST /api/bills/:bill/cost-entries` with `scope: \"lines\"` to validate a single allocation amount.\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/bills/{bill}/lines/{billLine}/availability","host":["{{baseUrl}}"],"path":["api","bills",":bill","lines",":billLine","availability"],"variable":[{"key":"bill","value":"15","description":"Bill ID"},{"key":"billLine","value":"32","description":"Bill line ID (must belong to the bill)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"bill_line_id\": 32,\n    \"description\": \"Import duty (HS 8516.71)\",\n    \"line_total\": 650,\n    \"already_tracked\": 250,\n    \"available_for_tracking\": 400,\n    \"tracking_percentage\": 38.46\n  },\n  \"status\": \"success\"\n}"}]},{"name":"List Bill Payments","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/bills/{id}/payments","host":["{{baseUrl}}"],"path":["api","bills",":id","payments"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Lists all payments recorded against a landed-cost bill (supplier payments applied to the bill). Payments two-way sync with the connected accounting provider.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/bills/{id}/payments","host":["{{baseUrl}}"],"path":["api","bills",":id","payments"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"amount\": 250,\n      \"cost\": 0,\n      \"payment_date\": \"2026-07-09T00:00:00.000000Z\",\n      \"payment_type\": {\n        \"id\": 3,\n        \"name\": \"Bank Transfer\"\n      },\n      \"external_reference\": \"WIRE-99182\",\n      \"type\": null,\n      \"currency_code\": \"USD\",\n      \"sales_credit_allocation\": null\n    }\n  ]\n}"}]},{"name":"Add Bill Payment","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/bills/{id}/payments","host":["{{baseUrl}}"],"path":["api","bills",":id","payments"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Records a payment against a landed-cost bill and recalculates the bill's paid status. The payment is pushed to the connected accounting provider (Xero / QuickBooks Online).\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields:\n- payment_date (optional, date, defaults to today)\n- payment_type_id (required, an existing payment type)\n- amount (required, numeric, must be greater than 0)\n- external_reference (optional, string, max 255)\n- currency_id (optional; defaults to the bill's currency)\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"payment_date\": \"2026-07-09\",\n  \"payment_type_id\": 3,\n  \"amount\": 250,\n  \"external_reference\": \"WIRE-99182\",\n  \"currency_id\": 1\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/bills/{id}/payments","host":["{{baseUrl}}"],"path":["api","bills",":id","payments"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"amount\": 250,\n    \"cost\": 0,\n    \"payment_date\": \"2026-07-09T00:00:00.000000Z\",\n    \"payment_type\": {\n      \"id\": 3,\n      \"name\": \"Bank Transfer\"\n    },\n    \"external_reference\": \"WIRE-99182\",\n    \"type\": null,\n    \"currency_code\": \"USD\",\n    \"sales_credit_allocation\": null\n  },\n  \"message\": \"Bill payment created successfully\"\n}"}]},{"name":"Update Bill Payment","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/bills/{id}/payments/{id2}","host":["{{baseUrl}}"],"path":["api","bills",":id","payments",":id2"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]},"description":"Updates an existing bill payment and recalculates the bill's paid status. The change is re-synced to the connected accounting provider.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"payment_date\": \"2026-07-09\",\n  \"amount\": 300,\n  \"external_reference\": \"WIRE-99182\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/bills/{id}/payments/{id2}","host":["{{baseUrl}}"],"path":["api","bills",":id","payments",":id2"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"amount\": 300,\n    \"cost\": 0,\n    \"payment_date\": \"2026-07-09T00:00:00.000000Z\",\n    \"payment_type\": {\n      \"id\": 3,\n      \"name\": \"Bank Transfer\"\n    },\n    \"external_reference\": \"WIRE-99182\",\n    \"type\": null,\n    \"currency_code\": \"USD\",\n    \"sales_credit_allocation\": null\n  },\n  \"message\": \"Bill payment updated successfully\"\n}"}]},{"name":"Delete Bill Payment","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/bills/{id}/payments/{id2}","host":["{{baseUrl}}"],"path":["api","bills",":id","payments",":id2"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]},"description":"Deletes a bill payment and recalculates the bill's paid status. The corresponding provider payment is voided/removed on the connected accounting provider.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/bills/{id}/payments/{id2}","host":["{{baseUrl}}"],"path":["api","bills",":id","payments",":id2"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Bill payment deleted successfully\"\n}"}]},{"name":"Get Bill Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/bills/{id}/activity-log","host":["{{baseUrl}}"],"path":["api","bills",":id","activity-log"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Paginated audit history for a bill — who changed what and when, including field-level before/after values.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/bills/{id}/activity-log","host":["{{baseUrl}}"],"path":["api","bills",":id","activity-log"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 481,\n      \"description\": \"updated\",\n      \"event\": \"updated\",\n      \"subject_type\": \"Bill\",\n      \"subject_id\": 1,\n      \"properties\": {\n        \"source\": \"manual\",\n        \"old\": {\n          \"invoice_number\": \"INV-2024-000\"\n        },\n        \"attributes\": {\n          \"invoice_number\": \"INV-2024-001\"\n        }\n      },\n      \"causer_name\": \"Jane Doe\",\n      \"created_at\": \"2026-07-09T10:00:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1\n}"}]},{"name":"List Bill Notes","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/bills/{id}/notes","host":["{{baseUrl}}"],"path":["api","bills",":id","notes"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Paginated notes attached to a bill, newest first.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/bills/{id}/notes","host":["{{baseUrl}}"],"path":["api","bills",":id","notes"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 7,\n      \"note\": \"Awaiting supplier credit note.\",\n      \"is_pinned\": false,\n      \"link_type\": \"bill\",\n      \"link_id\": 1,\n      \"user\": {\n        \"id\": 2,\n        \"name\": \"Jane Doe\"\n      },\n      \"created_at\": \"2026-07-09T10:00:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1\n}"}]},{"name":"Add Bill Note","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/bills/{id}/notes","host":["{{baseUrl}}"],"path":["api","bills",":id","notes"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Adds a note to a bill.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields:\n- note (required, string)\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"note\": \"Awaiting supplier credit note.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/bills/{id}/notes","host":["{{baseUrl}}"],"path":["api","bills",":id","notes"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 7,\n    \"note\": \"Awaiting supplier credit note.\",\n    \"is_pinned\": false,\n    \"link_type\": \"bill\",\n    \"link_id\": 1,\n    \"user\": {\n      \"id\": 2,\n      \"name\": \"Jane Doe\"\n    },\n    \"created_at\": \"2026-07-09T10:00:00.000000Z\"\n  },\n  \"message\": \"Note created successfully\"\n}"}]},{"name":"Toggle Bill Note Pin","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/bills/{id}/notes/{id2}/toggle-pin","host":["{{baseUrl}}"],"path":["api","bills",":id","notes",":id2","toggle-pin"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]},"description":"Pins or unpins a bill note (toggles its pinned state).\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/bills/{id}/notes/{id2}/toggle-pin","host":["{{baseUrl}}"],"path":["api","bills",":id","notes",":id2","toggle-pin"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 7,\n    \"note\": \"Awaiting supplier credit note.\",\n    \"is_pinned\": true,\n    \"link_type\": \"bill\",\n    \"link_id\": 1\n  },\n  \"message\": \"Note pinned\"\n}"}]},{"name":"Delete Bill Note","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/bills/{id}/notes/{id2}","host":["{{baseUrl}}"],"path":["api","bills",":id","notes",":id2"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]},"description":"Deletes a bill note.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/bills/{id}/notes/{id2}","host":["{{baseUrl}}"],"path":["api","bills",":id","notes",":id2"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Note deleted successfully\"\n}"}]},{"name":"Sync Bill Tags","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/bills/{id}/tags","host":["{{baseUrl}}"],"path":["api","bills",":id","tags"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Replaces the full set of tags on a bill with the provided list.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields:\n- tags (required, array of strings, each max 64 chars)\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"tags\": [\n    \"freight\",\n    \"customs\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/bills/{id}/tags","host":["{{baseUrl}}"],"path":["api","bills",":id","tags"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tags\": [\n      \"freight\",\n      \"customs\"\n    ]\n  },\n  \"message\": \"Bill tags updated successfully\"\n}"}]},{"name":"List Bill Documents","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/bills/{id}/documents","host":["{{baseUrl}}"],"path":["api","bills",":id","documents"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Lists the scanned source invoice documents linked to a bill.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/bills/{id}/documents","host":["{{baseUrl}}"],"path":["api","bills",":id","documents"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 90,\n      \"file_name\": \"supplier-invoice-INV-2024-001.pdf\",\n      \"status\": \"confirmed\",\n      \"page_count\": 2,\n      \"created_at\": \"2026-07-09T10:00:00.000000Z\"\n    }\n  ]\n}"}]},{"name":"Reset Bill Due Date to Term","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/bills/{bill}/reset-due-date","host":["{{baseUrl}}"],"path":["api","bills",":bill","reset-due-date"],"variable":[{"key":"bill","value":"","description":"The bill ID."}]},"description":"Discards a manually-overridden due date on a landed-cost bill and re-derives it from the bill's effective payment term (its own term, else the linked purchase order's term, else the supplier default). The response's `due_date_source` returns to `derived` (or `none` when no term is resolvable).\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/bills/{bill}/reset-due-date","host":["{{baseUrl}}"],"path":["api","bills",":bill","reset-due-date"],"variable":[{"key":"bill","value":"","description":"The bill ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 321,\n    \"supplier_id\": 88,\n    \"supplier_name\": \"DHL Forwarding\",\n    \"invoice_number\": \"DHL-88213\",\n    \"invoice_date\": \"2026-07-13\",\n    \"due_date\": \"2026-07-28\",\n    \"due_date_source\": \"derived\",\n    \"payment_term_id\": 4,\n    \"payment_term_name\": \"Net 15\",\n    \"payment_term_snapshot\": {\n      \"payment_term_id\": 4,\n      \"name\": \"Net 15\",\n      \"net_days\": 15,\n      \"due_date_type\": \"from_invoice_date\",\n      \"discount_percentage\": 0,\n      \"discount_days\": null,\n      \"captured_at\": \"2026-07-13T12:00:00+00:00\",\n      \"residual_schedule_line\": null\n    },\n    \"term_diverges_from_po\": true,\n    \"discount_due_date\": null,\n    \"discount_amount\": null,\n    \"discount_available\": false,\n    \"payment_due_status\": \"not_due\",\n    \"days_until_due\": 15,\n    \"link_type\": \"purchase_order\",\n    \"link_id\": 707\n  }\n}"}]}]},{"name":"Amortization Schedules","item":[{"name":"Get Amortization Schedule","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/amortization-schedules/{amortizationSchedule}","host":["{{baseUrl}}"],"path":["api","amortization-schedules",":amortizationSchedule"],"variable":[{"key":"amortizationSchedule","value":"7","description":"Amortization schedule ID"}]},"description":"Get a single amortization schedule, including its full list of periods and the allocation configuration that is applied when each period is processed.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAn amortization schedule spreads a cost entry's unallocated amount over a series of periods (for example, an annual customs bond premium spread over four quarterly periods). Schedules are created with `POST /api/cost-entries/:costEntry/amortization-schedule` (documented under Cost Entries).\n\nResponse fields:\n- `start_date` / `end_date` - The date range covered by the schedule; `end_date` is the last period's end date.\n- `total_periods`, `frequency` (`daily`, `weekly`, `monthly`, `quarterly`, `annually`) and `method` (`straight_line`, `front_loaded`, `back_loaded`) - How the amount is distributed over time.\n- `total_amount` / `amount_per_period` - The amount being amortized and the per-period amount (the final period absorbs any rounding difference).\n- `is_fully_processed`, `pending_periods_count`, `processed_periods_count` - Processing progress.\n- `allocation_config` - Where each period's amount is allocated when processed: `allocatable_type` is one of `product`, `brand`, `supplier`, `sales_channel`; `allocatable_id` is the target entity's ID; `proration_strategy` is one of `revenue_based`, `cost_based`, `weight_based`, `volume_based`, `quantity_based`, `specific_line`, `manual`. `allocatable_name` is null on this endpoint; use the status endpoint to resolve the target's display name.\n- `periods` - Every period with its number, date range, amount, and processed state.\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/amortization-schedules/{amortizationSchedule}","host":["{{baseUrl}}"],"path":["api","amortization-schedules",":amortizationSchedule"],"variable":[{"key":"amortizationSchedule","value":"7","description":"Amortization schedule ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 7,\n    \"cost_entry_id\": 42,\n    \"start_date\": \"2026-01-01\",\n    \"end_date\": \"2026-12-31\",\n    \"total_periods\": 4,\n    \"frequency\": \"quarterly\",\n    \"method\": \"straight_line\",\n    \"total_amount\": 2400,\n    \"amount_per_period\": 600,\n    \"is_fully_processed\": false,\n    \"pending_periods_count\": 2,\n    \"processed_periods_count\": 2,\n    \"allocation_config\": {\n      \"allocatable_type\": \"product\",\n      \"allocatable_id\": 123,\n      \"allocatable_name\": null,\n      \"proration_strategy\": \"specific_line\"\n    },\n    \"created_at\": \"2026-01-05T09:30:00.000000Z\",\n    \"updated_at\": \"2026-07-01T02:15:03.000000Z\",\n    \"periods\": [\n      {\n        \"id\": 101,\n        \"amortization_schedule_id\": 7,\n        \"period_number\": 1,\n        \"period_start\": \"2026-01-01\",\n        \"period_end\": \"2026-03-31\",\n        \"amount\": 600,\n        \"is_processed\": true,\n        \"processed_at\": \"2026-04-01T02:15:07.000000Z\",\n        \"created_at\": \"2026-01-05T09:30:00.000000Z\",\n        \"updated_at\": \"2026-04-01T02:15:07.000000Z\"\n      },\n      {\n        \"id\": 102,\n        \"amortization_schedule_id\": 7,\n        \"period_number\": 2,\n        \"period_start\": \"2026-04-01\",\n        \"period_end\": \"2026-06-30\",\n        \"amount\": 600,\n        \"is_processed\": true,\n        \"processed_at\": \"2026-07-01T02:15:03.000000Z\",\n        \"created_at\": \"2026-01-05T09:30:00.000000Z\",\n        \"updated_at\": \"2026-07-01T02:15:03.000000Z\"\n      },\n      {\n        \"id\": 103,\n        \"amortization_schedule_id\": 7,\n        \"period_number\": 3,\n        \"period_start\": \"2026-07-01\",\n        \"period_end\": \"2026-09-30\",\n        \"amount\": 600,\n        \"is_processed\": false,\n        \"processed_at\": null,\n        \"created_at\": \"2026-01-05T09:30:00.000000Z\",\n        \"updated_at\": \"2026-01-05T09:30:00.000000Z\"\n      },\n      {\n        \"id\": 104,\n        \"amortization_schedule_id\": 7,\n        \"period_number\": 4,\n        \"period_start\": \"2026-10-01\",\n        \"period_end\": \"2026-12-31\",\n        \"amount\": 600,\n        \"is_processed\": false,\n        \"processed_at\": null,\n        \"created_at\": \"2026-01-05T09:30:00.000000Z\",\n        \"updated_at\": \"2026-01-05T09:30:00.000000Z\"\n      }\n    ]\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Delete Amortization Schedule","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/amortization-schedules/{amortizationSchedule}","host":["{{baseUrl}}"],"path":["api","amortization-schedules",":amortizationSchedule"],"variable":[{"key":"amortizationSchedule","value":"7","description":"Amortization schedule ID"}]},"description":"Delete an amortization schedule together with all of its periods.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThere is no guard against deleting a schedule that has already-processed periods: any cost allocations that were created by processing this schedule's periods are also deleted, effectively reversing those allocations. The whole operation runs in a single transaction, and the parent cost entry's allocation status is recalculated afterwards (for example back to `pending_allocation` if nothing else is allocated against it).\n\nThe cost entry itself is not deleted - a new schedule can be created for it afterwards with `POST /api/cost-entries/:costEntry/amortization-schedule`.\n\nReturns 200 with a confirmation message (not 204).\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/amortization-schedules/{amortizationSchedule}","host":["{{baseUrl}}"],"path":["api","amortization-schedules",":amortizationSchedule"],"variable":[{"key":"amortizationSchedule","value":"7","description":"Amortization schedule ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Amortization schedule deleted successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Amortization Schedule Status","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/amortization-schedules/{amortizationSchedule}/status","host":["{{baseUrl}}"],"path":["api","amortization-schedules",":amortizationSchedule","status"],"variable":[{"key":"amortizationSchedule","value":"7","description":"Amortization schedule ID"}]},"description":"Get a compact processing-progress summary for an amortization schedule - useful for dashboards and polling without fetching the full period list.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nResponse fields:\n- `total_periods`, `processed_periods`, `pending_periods`, `is_fully_processed` - Progress counters.\n- `total_amount` / `amount_per_period` - The amount being amortized.\n- `next_period` - The next unprocessed period (`period_number`, `period_start`, `period_end`, `amount`, and `is_due`, which is true once the period's end date has passed). Null when every period has been processed.\n- `allocation_config` - The stored allocation target: `allocatable_type` (`product`, `brand`, `supplier`, `sales_channel`), `allocatable_id`, `allocatable_name` (the target's name or SKU), and `proration_strategy`.\n\nSchedules are created with `POST /api/cost-entries/:costEntry/amortization-schedule` (documented under Cost Entries).\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/amortization-schedules/{amortizationSchedule}/status","host":["{{baseUrl}}"],"path":["api","amortization-schedules",":amortizationSchedule","status"],"variable":[{"key":"amortizationSchedule","value":"7","description":"Amortization schedule ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 7,\n    \"cost_entry_id\": 42,\n    \"total_periods\": 4,\n    \"processed_periods\": 2,\n    \"pending_periods\": 2,\n    \"is_fully_processed\": false,\n    \"total_amount\": 2400,\n    \"amount_per_period\": 600,\n    \"next_period\": {\n      \"period_number\": 3,\n      \"period_start\": \"2026-07-01\",\n      \"period_end\": \"2026-09-30\",\n      \"amount\": 600,\n      \"is_due\": false\n    },\n    \"allocation_config\": {\n      \"allocatable_type\": \"product\",\n      \"allocatable_id\": 123,\n      \"allocatable_name\": \"Espresso Machine E61 Group Head\",\n      \"proration_strategy\": \"specific_line\"\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Process Amortization Schedule Due Periods","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/amortization-schedules/{amortizationSchedule}/process","host":["{{baseUrl}}"],"path":["api","amortization-schedules",":amortizationSchedule","process"],"variable":[{"key":"amortizationSchedule","value":"7","description":"Amortization schedule ID"}]},"description":"Process every due period on an amortization schedule. A period is due when it is unprocessed and its end date is on or before `as_of_date` (defaults to today). Processing each period creates the cost allocations defined by the schedule's stored allocation configuration and updates the parent cost entry's allocation status.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThis endpoint runs synchronously - allocations are created before the response returns, and `processed_count` reports how many periods were processed (0 when nothing is due). A daily background task also processes due periods automatically for schedules that have a stored allocation configuration, so this endpoint is mainly for processing ahead of that run or applying a one-off target override.\n\nRequest fields (all optional):\n- `as_of_date` - Process periods due as of this date instead of today (date, e.g. `2026-07-07`).\n- `allocatable_type` + `allocatable_id` - Override the stored allocation target for this run only. `allocatable_type` is one of `product`, `brand`, `supplier`, `sales_channel`; `allocatable_id` is required whenever `allocatable_type` is sent. When omitted, the schedule's stored configuration is used (`used_stored_config: true` in the response).\n- `proration_strategy` - Only used with an override target; one of `revenue_based`, `cost_based`, `weight_based`, `volume_based`, `quantity_based`, `specific_line`, `manual` (defaults to `specific_line`).\n\nReturns 422 when an override target does not exist, or when no override is sent and the schedule has no stored allocation configuration.\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write).","body":{"mode":"raw","raw":"{\n  \"as_of_date\": \"2026-07-07\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/amortization-schedules/{amortizationSchedule}/process","host":["{{baseUrl}}"],"path":["api","amortization-schedules",":amortizationSchedule","process"],"variable":[{"key":"amortizationSchedule","value":"7","description":"Amortization schedule ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"processed_count\": 2,\n    \"used_stored_config\": true\n  },\n  \"message\": \"Processed 2 periods\",\n  \"status\": \"success\"\n}"}]}]},{"name":"Configuration","item":[{"name":"Create Financial Alert","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/financial-alerts","host":["{{baseUrl}}"],"path":["api","financial-alerts"]},"description":"Creates a new financial alert record. Typically, alerts are auto-generated by the process endpoint, but this allows manual creation.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"financial_alert_classification_id\": 2,\n  \"note\": \"Pricing error — needs review\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/financial-alerts","host":["{{baseUrl}}"],"path":["api","financial-alerts"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 16,\n    \"financial_alert_classification_id\": 2,\n    \"note\": \"Pricing error — needs review\"\n  },\n  \"message\": \"Financial alert updated successfully\"\n}"}]},{"name":"Update Financial Alert","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/financial-alerts/{financial_alert}","host":["{{baseUrl}}"],"path":["api","financial-alerts",":financial_alert"],"variable":[{"key":"financial_alert","value":"15","description":"The financial alert ID."}]},"description":"Updates a financial alert's classification and/or note.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"financial_alert_classification_id\": 3,\n  \"note\": \"Investigated — supplier overcharge confirmed\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/financial-alerts/{financial_alert}","host":["{{baseUrl}}"],"path":["api","financial-alerts",":financial_alert"],"variable":[{"key":"financial_alert","value":"15","description":"The financial alert ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 15,\n    \"classification\": \"Supplier Issue\",\n    \"classification_id\": 3,\n    \"note\": \"Investigated — supplier overcharge confirmed\"\n  },\n  \"message\": \"Financial alert updated successfully\"\n}"}]},{"name":"Delete Financial Alert","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/financial-alerts/{financial_alert}","host":["{{baseUrl}}"],"path":["api","financial-alerts",":financial_alert"],"variable":[{"key":"financial_alert","value":"15","description":"The financial alert ID."}]},"description":"Deletes a financial alert record.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/financial-alerts/{financial_alert}","host":["{{baseUrl}}"],"path":["api","financial-alerts",":financial_alert"],"variable":[{"key":"financial_alert","value":"15","description":"The financial alert ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"financial alerts deleted successfully\"\n}"}]},{"name":"Archive Alert","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/financial-alerts/{financialAlert}/archive","host":["{{baseUrl}}"],"path":["api","financial-alerts",":financialAlert","archive"],"variable":[{"key":"financialAlert","value":"15","description":"The financial alert ID."}]},"description":"Archives (resolves) a single financial alert, recording the archiving user and timestamp.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/financial-alerts/{financialAlert}/archive","host":["{{baseUrl}}"],"path":["api","financial-alerts",":financialAlert","archive"],"variable":[{"key":"financialAlert","value":"15","description":"The financial alert ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 15,\n    \"archived_at\": \"2025-04-23T10:00:00.000000Z\",\n    \"archived_by_user\": \"Admin User\",\n    \"is_resolved\": \"true\"\n  },\n  \"message\": \"Financial alert archived successfully\"\n}"}]},{"name":"Unarchive Alert","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/financial-alerts/{financialAlert}/unarchive","host":["{{baseUrl}}"],"path":["api","financial-alerts",":financialAlert","unarchive"],"variable":[{"key":"financialAlert","value":"15","description":"The financial alert ID."}]},"description":"Unarchives (re-opens) a previously archived financial alert.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/financial-alerts/{financialAlert}/unarchive","host":["{{baseUrl}}"],"path":["api","financial-alerts",":financialAlert","unarchive"],"variable":[{"key":"financialAlert","value":"15","description":"The financial alert ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 15,\n    \"archived_at\": null,\n    \"archived_by_user\": null,\n    \"is_resolved\": \"false\"\n  },\n  \"message\": \"Financial alert unarchived successfully\"\n}"}]},{"name":"Bulk Archive Alerts","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/financial-alerts/archive","host":["{{baseUrl}}"],"path":["api","financial-alerts","archive"]},"description":"Archives multiple financial alerts at once.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- ids (required): Array of financial alert IDs to archive","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    15,\n    16,\n    17\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/financial-alerts/archive","host":["{{baseUrl}}"],"path":["api","financial-alerts","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"financial alerts bulk archived successfully\"\n}"}]},{"name":"Bulk Unarchive Alerts","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/financial-alerts/unarchive","host":["{{baseUrl}}"],"path":["api","financial-alerts","unarchive"]},"description":"Unarchives multiple financial alerts at once.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- ids (required): Array of financial alert IDs to unarchive","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    15,\n    16,\n    17\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/financial-alerts/unarchive","host":["{{baseUrl}}"],"path":["api","financial-alerts","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"financial alerts bulk unarchived successfully\"\n}"}]},{"name":"Bulk Update Alerts","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/financial-alerts/update","host":["{{baseUrl}}"],"path":["api","financial-alerts","update"]},"description":"Bulk updates the classification and/or note for multiple financial alerts.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- ids (required): Array of financial alert IDs to update\n- classification (optional): Classification ID to assign\n- note (optional): Note to set on all specified alerts","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    15,\n    16,\n    17\n  ],\n  \"classification\": 2,\n  \"note\": \"Reviewed and classified\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/financial-alerts/update","host":["{{baseUrl}}"],"path":["api","financial-alerts","update"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"financial alerts bulk updated successfully\"\n}"}]},{"name":"Preview Import","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/financial-alerts/import/preview","host":["{{baseUrl}}"],"path":["api","financial-alerts","import","preview"]},"description":"Previews an import file for financial alerts before committing. Returns a summary of what will be imported and any errors.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data\n\nFields:\n- file (required): The CSV/Excel file to preview","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/financial-alerts/import/preview","host":["{{baseUrl}}"],"path":["api","financial-alerts","import","preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_rows\": 50,\n    \"valid_rows\": 48,\n    \"errors\": [\n      {\n        \"row\": 12,\n        \"message\": \"Invalid SKU: SKU-999\"\n      }\n    ],\n    \"preview\": [\n      {\n        \"sku\": \"WIDGET-A\",\n        \"alert_type\": \"LOSS\",\n        \"profit\": -35\n      }\n    ]\n  }\n}"}]},{"name":"Import Alerts","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/financial-alerts/import","host":["{{baseUrl}}"],"path":["api","financial-alerts","import"]},"description":"Imports financial alerts from a CSV/Excel file.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data\n\nFields:\n- file (required): The CSV/Excel file to import","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/financial-alerts/import","host":["{{baseUrl}}"],"path":["api","financial-alerts","import"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Import completed successfully\",\n  \"data\": {\n    \"imported\": 48,\n    \"skipped\": 2\n  }\n}"}]},{"name":"Get Dashboard Data","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/financial-alerts/dashboard","host":["{{baseUrl}}"],"path":["api","financial-alerts","dashboard"]},"description":"Returns a summary of archived (resolved) financial alerts grouped by alert type. Used for the financial alerts dashboard overview.\n\n**Required scope: `accounting:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/financial-alerts/dashboard","host":["{{baseUrl}}"],"path":["api","financial-alerts","dashboard"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"alert_type\": \"LOSS\",\n      \"count\": 24\n    },\n    {\n      \"alert_type\": \"LOW_MARGIN\",\n      \"count\": 37\n    }\n  ]\n}"}]},{"name":"Get Report Data","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/financial-alerts/reports","host":["{{baseUrl}}"],"path":["api","financial-alerts","reports"]},"description":"Returns reporting data for financial alerts including classification breakdown and resolution breakdown. Used for the reports page.\n\n**Required scope: `accounting:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/financial-alerts/reports","host":["{{baseUrl}}"],"path":["api","financial-alerts","reports"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"classification_breakdown\": [\n      {\n        \"count\": 15,\n        \"classification\": \"Pricing Error\"\n      },\n      {\n        \"count\": 9,\n        \"classification\": \"Supplier Issue\"\n      },\n      {\n        \"count\": 3,\n        \"classification\": null\n      }\n    ],\n    \"resolution_breakdown\": [\n      {\n        \"count\": 42,\n        \"resolution\": \"Resolved\"\n      },\n      {\n        \"count\": 18,\n        \"resolution\": \"Unresolved\"\n      }\n    ]\n  }\n}"}]},{"name":"Process Alerts","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/financial-alerts/process","host":["{{baseUrl}}"],"path":["api","financial-alerts","process"]},"description":"Triggers the financial alert processing job, which scans all sales order lines for low margin or loss conditions and creates/deletes alerts accordingly.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/financial-alerts/process","host":["{{baseUrl}}"],"path":["api","financial-alerts","process"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Financial alerts processed successfully\"\n}"}]},{"name":"List Classifications","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/financial-alerts/classifications","host":["{{baseUrl}}"],"path":["api","financial-alerts","classifications"]},"description":"Returns all financial alert classification categories. Classifications are used to tag alerts with a reason or resolution type (e.g., Pricing Error, Supplier Issue).\n\n**Required scope: `accounting:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/financial-alerts/classifications","host":["{{baseUrl}}"],"path":["api","financial-alerts","classifications"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Pricing Error\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Supplier Issue\"\n    },\n    {\n      \"id\": 3,\n      \"name\": \"One-off Promotion\"\n    },\n    {\n      \"id\": 4,\n      \"name\": \"Expected Loss\"\n    }\n  ]\n}"}]},{"name":"Create Classification","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/financial-alerts/classifications","host":["{{baseUrl}}"],"path":["api","financial-alerts","classifications"]},"description":"Creates a new financial alert classification category.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- name (required): Name of the classification","body":{"mode":"raw","raw":"{\n  \"name\": \"Competitor Pricing\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/financial-alerts/classifications","host":["{{baseUrl}}"],"path":["api","financial-alerts","classifications"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Competitor Pricing\"\n  },\n  \"message\": \"financial alert classification created successfully\"\n}"}]},{"name":"Get Classification","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/financial-alerts/classifications/{classification}","host":["{{baseUrl}}"],"path":["api","financial-alerts","classifications",":classification"],"variable":[{"key":"classification","value":"1","description":"The classification ID."}]},"description":"Returns a single financial alert classification by ID.\n\n**Required scope: `accounting:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/financial-alerts/classifications/{classification}","host":["{{baseUrl}}"],"path":["api","financial-alerts","classifications",":classification"],"variable":[{"key":"classification","value":"1","description":"The classification ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Pricing Error\"\n  }\n}"}]},{"name":"Update Classification","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/financial-alerts/classifications/{classification}","host":["{{baseUrl}}"],"path":["api","financial-alerts","classifications",":classification"],"variable":[{"key":"classification","value":"1","description":"The classification ID."}]},"description":"Updates a financial alert classification name.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"name\": \"Pricing / Margin Error\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/financial-alerts/classifications/{classification}","host":["{{baseUrl}}"],"path":["api","financial-alerts","classifications",":classification"],"variable":[{"key":"classification","value":"1","description":"The classification ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Pricing / Margin Error\"\n  }\n}"}]},{"name":"Delete Classification","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/financial-alerts/classifications/{classification}","host":["{{baseUrl}}"],"path":["api","financial-alerts","classifications",":classification"],"variable":[{"key":"classification","value":"1","description":"The classification ID."}]},"description":"Deletes a financial alert classification.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/financial-alerts/classifications/{classification}","host":["{{baseUrl}}"],"path":["api","financial-alerts","classifications",":classification"],"variable":[{"key":"classification","value":"1","description":"The classification ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"financial alert classification deleted successfully\"\n}"}]},{"name":"List Financial Line Types","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/financial-line-types","host":["{{baseUrl}}"],"path":["api","financial-line-types"]},"description":"Returns all financial line types for the organization.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/financial-line-types","host":["{{baseUrl}}"],"path":["api","financial-line-types"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"[\n  {\n    \"id\": 1,\n    \"name\": \"Freight\",\n    \"classification\": \"cost\",\n    \"allocate_to_products\": true,\n    \"proration_strategy\": \"weight_based\",\n    \"nominal_code_id\": null,\n    \"created_at\": \"2024-01-01T00:00:00.000000Z\",\n    \"updated_at\": \"2024-01-01T00:00:00.000000Z\"\n  },\n  {\n    \"id\": 2,\n    \"name\": \"Product Sales\",\n    \"classification\": \"revenue\",\n    \"allocate_to_products\": false,\n    \"proration_strategy\": \"revenue_based\",\n    \"nominal_code_id\": 5,\n    \"created_at\": \"2024-01-01T00:00:00.000000Z\",\n    \"updated_at\": \"2024-02-15T10:00:00.000000Z\"\n  }\n]"}]},{"name":"Create Financial Line Type","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/financial-line-types","host":["{{baseUrl}}"],"path":["api","financial-line-types"]},"description":"Creates a new financial line type.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- name (required, string) — Name of the financial line type\n- classification (required, string) — One of: revenue, cost\n- allocate_to_products (optional, boolean) — Whether to allocate costs/revenue to products\n- proration_strategy (optional, string) — One of: revenue_based, cost_based, weight_based, volume_based, quantity_based, specific_line, manual\n- nominal_code_id (optional, integer|null) — ID of associated nominal code\n\n**Requires permission:** `financial_line_types.create`","body":{"mode":"raw","raw":"{\n  \"name\": \"Freight\",\n  \"classification\": \"cost\",\n  \"allocate_to_products\": true,\n  \"proration_strategy\": \"weight_based\",\n  \"nominal_code_id\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/financial-line-types","host":["{{baseUrl}}"],"path":["api","financial-line-types"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 3,\n  \"name\": \"Freight\",\n  \"classification\": \"cost\",\n  \"allocate_to_products\": true,\n  \"proration_strategy\": \"weight_based\",\n  \"nominal_code_id\": null,\n  \"created_at\": \"2024-06-01T09:00:00.000000Z\",\n  \"updated_at\": \"2024-06-01T09:00:00.000000Z\"\n}"}]},{"name":"Bulk Delete Financial Line Types","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/financial-line-types","host":["{{baseUrl}}"],"path":["api","financial-line-types"]},"description":"Bulk deletes multiple financial line types by their IDs.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- ids (required, array) — Array of financial line type IDs to delete\n\n**Requires permission:** `financial_line_types.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/financial-line-types","host":["{{baseUrl}}"],"path":["api","financial-line-types"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Records deleted successfully.\"\n}"}]},{"name":"Show Financial Line Type","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/financial-line-types/{financial_line_type}","host":["{{baseUrl}}"],"path":["api","financial-line-types",":financial_line_type"],"variable":[{"key":"financial_line_type","value":"1","description":"Financial line type ID"}]},"description":"Returns a single financial line type by ID.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/financial-line-types/{financial_line_type}","host":["{{baseUrl}}"],"path":["api","financial-line-types",":financial_line_type"],"variable":[{"key":"financial_line_type","value":"1","description":"Financial line type ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 1,\n  \"name\": \"Freight\",\n  \"classification\": \"cost\",\n  \"allocate_to_products\": true,\n  \"proration_strategy\": \"weight_based\",\n  \"nominal_code_id\": null,\n  \"created_at\": \"2024-01-01T00:00:00.000000Z\",\n  \"updated_at\": \"2024-01-01T00:00:00.000000Z\"\n}"}]},{"name":"Update Financial Line Type","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/financial-line-types/{financial_line_type}","host":["{{baseUrl}}"],"path":["api","financial-line-types",":financial_line_type"],"variable":[{"key":"financial_line_type","value":"1","description":"Financial line type ID"}]},"description":"Updates an existing financial line type. All fields are optional on update.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields (all optional on update):\n- name (optional, string) — Name of the financial line type\n- classification (optional, string) — One of: revenue, cost\n- allocate_to_products (optional, boolean) — Whether to allocate costs/revenue to products\n- proration_strategy (optional, string) — One of: revenue_based, cost_based, weight_based, volume_based, quantity_based, specific_line, manual\n- nominal_code_id (optional, integer|null) — ID of associated nominal code\n\n**Requires permission:** `financial_line_types.update`","body":{"mode":"raw","raw":"{\n  \"name\": \"International Freight\",\n  \"classification\": \"cost\",\n  \"allocate_to_products\": true,\n  \"proration_strategy\": \"volume_based\",\n  \"nominal_code_id\": 12\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/financial-line-types/{financial_line_type}","host":["{{baseUrl}}"],"path":["api","financial-line-types",":financial_line_type"],"variable":[{"key":"financial_line_type","value":"1","description":"Financial line type ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 1,\n  \"name\": \"International Freight\",\n  \"classification\": \"cost\",\n  \"allocate_to_products\": true,\n  \"proration_strategy\": \"volume_based\",\n  \"nominal_code_id\": 12,\n  \"created_at\": \"2024-01-01T00:00:00.000000Z\",\n  \"updated_at\": \"2024-06-01T10:00:00.000000Z\"\n}"}]},{"name":"Delete Financial Line Type","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/financial-line-types/{financial_line_type}","host":["{{baseUrl}}"],"path":["api","financial-line-types",":financial_line_type"],"variable":[{"key":"financial_line_type","value":"1","description":"Financial line type ID"}]},"description":"Deletes a financial line type. Returns 400 if the type has associated financial lines.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `financial_line_types.delete`"},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/financial-line-types/{financial_line_type}","host":["{{baseUrl}}"],"path":["api","financial-line-types",":financial_line_type"],"variable":[{"key":"financial_line_type","value":"1","description":"Financial line type ID"}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":""}]},{"name":"List Nominal Codes (Dropdown)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/nominal-codes/list","host":["{{baseUrl}}"],"path":["api","nominal-codes","list"]},"description":"Returns a lightweight list of all nominal codes (id, code, name, type), ordered by code. Intended for dropdown/combo-box lookups.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nEvery nominal code record is returned independently — no deduplication. Integration re-syncs (e.g. QuickBooks Online) can legitimately create multiple rows sharing the same code + name; each appears as its own entry with its own id.\n\nPass used_by_transactions=1 to restrict the list to nominal codes referenced by at least one accounting transaction line — used by the Accounting Transactions filter dropdown so unused codes don't appear as dead filter options.\n\nArchived nominal codes are excluded automatically.\n\nThe type field is the account type (Revenue, Expense, Asset, Liability, Equity, Bank, Current) and can be used for client-side filtering (e.g. asset-only dropdowns).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/nominal-codes/list","host":["{{baseUrl}}"],"path":["api","nominal-codes","list"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"code\": \"1200\",\n      \"name\": \"Inventory Asset\",\n      \"type\": \"Current\"\n    },\n    {\n      \"id\": 4,\n      \"code\": \"4000\",\n      \"name\": \"Sales Revenue\",\n      \"type\": \"Revenue\"\n    },\n    {\n      \"id\": 2,\n      \"code\": \"5000\",\n      \"name\": \"Cost of Goods Sold\",\n      \"type\": \"Expense\"\n    },\n    {\n      \"id\": 3,\n      \"code\": \"6000\",\n      \"name\": \"Shipping Expense\",\n      \"type\": \"Expense\"\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Show Nominal Code","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/nominal-codes/{nominal_code}","host":["{{baseUrl}}"],"path":["api","nominal-codes",":nominal_code"],"variable":[{"key":"nominal_code","value":"1","description":"Nominal code ID"}]},"description":"Show a single nominal code.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/nominal-codes/{nominal_code}","host":["{{baseUrl}}"],"path":["api","nominal-codes",":nominal_code"],"variable":[{"key":"nominal_code","value":"1","description":"Nominal code ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"code\": \"4000\",\n    \"name\": \"Sales Revenue\",\n    \"type\": \"Revenue\",\n    \"created_at\": \"2024-01-10T09:00:00.000000Z\",\n    \"updated_at\": \"2024-02-01T11:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Update Nominal Code","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/nominal-codes/{nominal_code}","host":["{{baseUrl}}"],"path":["api","nominal-codes",":nominal_code"],"variable":[{"key":"nominal_code","value":"10","description":"Nominal code ID"}]},"description":"Update an existing nominal code.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nRequest body fields (all optional on update):\n- `code` (string) — unique code; must not conflict with other records\n- `name` (string, max:255)\n- `type` — Allowed values: Revenue, Expense, Asset, Liability, Equity, Bank, Current, Inventory, Fixed, Directcosts, Currliab, Termliab, Sales\n\n**Requires permission:** `nominal_codes.update`","body":{"mode":"raw","raw":"{\n  \"code\": \"7001\",\n  \"name\": \"Digital Marketing Expense\",\n  \"type\": \"Expense\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/nominal-codes/{nominal_code}","host":["{{baseUrl}}"],"path":["api","nominal-codes",":nominal_code"],"variable":[{"key":"nominal_code","value":"10","description":"Nominal code ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 10,\n    \"code\": \"7001\",\n    \"name\": \"Digital Marketing Expense\",\n    \"type\": \"Expense\",\n    \"created_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"updated_at\": \"2024-03-05T14:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"nominal code 7001 was updated successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Delete Nominal Code","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/nominal-codes/{nominal_code}","host":["{{baseUrl}}"],"path":["api","nominal-codes",":nominal_code"],"variable":[{"key":"nominal_code","value":"10","description":"Nominal code ID"}]},"description":"Delete a nominal code. Fails if the code is used in mapping settings or linked to sales order lines.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `nominal_codes.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/nominal-codes/{nominal_code}","host":["{{baseUrl}}"],"path":["api","nominal-codes",":nominal_code"],"variable":[{"key":"nominal_code","value":"10","description":"Nominal code ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"nominal code Digital Marketing Expense was deleted successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Create Nominal Code","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/nominal-codes","host":["{{baseUrl}}"],"path":["api","nominal-codes"]},"description":"Create a new nominal code.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nRequest body fields:\n- `code` (required, string) — unique identifier code for the nominal code\n- `name` (required, string, max:255) — display name\n- `type` (required) — account type. Allowed values: Revenue, Expense, Asset, Liability, Equity, Bank, Current, Inventory, Fixed, Directcosts, Currliab, Termliab, Sales\n\n**Requires permission:** `nominal_codes.create`","body":{"mode":"raw","raw":"{\n  \"code\": \"7000\",\n  \"name\": \"Marketing Expense\",\n  \"type\": \"Expense\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/nominal-codes","host":["{{baseUrl}}"],"path":["api","nominal-codes"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 10,\n    \"code\": \"7000\",\n    \"name\": \"Marketing Expense\",\n    \"type\": \"Expense\",\n    \"created_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"updated_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"nominal code was created successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Delete Nominal Codes","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/nominal-codes","host":["{{baseUrl}}"],"path":["api","nominal-codes"]},"description":"Bulk delete multiple nominal codes by ID.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- `ids` (required) — array of nominal code IDs to delete\n\n**Requires permission:** `nominal_codes.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/nominal-codes","host":["{{baseUrl}}"],"path":["api","nominal-codes"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 nominal codes deleted successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Archive Nominal Code","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/nominal-codes/{nominalCode}/archive","host":["{{baseUrl}}"],"path":["api","nominal-codes",":nominalCode","archive"],"variable":[{"key":"nominalCode","value":"10","description":"Nominal code ID"}]},"description":"Archive a single nominal code.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `nominal_codes.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/nominal-codes/{nominalCode}/archive","host":["{{baseUrl}}"],"path":["api","nominal-codes",":nominalCode","archive"],"variable":[{"key":"nominalCode","value":"10","description":"Nominal code ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 10,\n    \"code\": \"7001\",\n    \"name\": \"Digital Marketing Expense\",\n    \"type\": \"Expense\",\n    \"archived_at\": \"2024-03-10T11:00:00.000000Z\"\n  },\n  \"message\": \"nominal code 7001 was archived successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Unarchive Nominal Code","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/nominal-codes/{nominalCode}/unarchived","host":["{{baseUrl}}"],"path":["api","nominal-codes",":nominalCode","unarchived"],"variable":[{"key":"nominalCode","value":"10","description":"Nominal code ID"}]},"description":"Unarchive a single nominal code.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `nominal_codes.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/nominal-codes/{nominalCode}/unarchived","host":["{{baseUrl}}"],"path":["api","nominal-codes",":nominalCode","unarchived"],"variable":[{"key":"nominalCode","value":"10","description":"Nominal code ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 10,\n    \"code\": \"7001\",\n    \"name\": \"Digital Marketing Expense\",\n    \"type\": \"Expense\",\n    \"archived_at\": null\n  },\n  \"message\": \"nominal code 7001 was unarchived successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Archive Nominal Codes","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/nominal-codes/archive","host":["{{baseUrl}}"],"path":["api","nominal-codes","archive"]},"description":"Bulk archive multiple nominal codes by ID.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- `ids` (required) — array of nominal code IDs to archive\n\n**Requires permission:** `nominal_codes.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    8,\n    9,\n    10\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/nominal-codes/archive","host":["{{baseUrl}}"],"path":["api","nominal-codes","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 nominal codes archived successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Unarchive Nominal Codes","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/nominal-codes/unarchive","host":["{{baseUrl}}"],"path":["api","nominal-codes","unarchive"]},"description":"Bulk unarchive multiple nominal codes by ID.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- `ids` (required) — array of nominal code IDs to unarchive\n\n**Requires permission:** `nominal_codes.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    8,\n    9,\n    10\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/nominal-codes/unarchive","host":["{{baseUrl}}"],"path":["api","nominal-codes","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 nominal codes unarchived successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Check Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/nominal-codes/deletable","host":["{{baseUrl}}"],"path":["api","nominal-codes","deletable"]},"description":"Check which nominal codes can be deleted. A code cannot be deleted if it is linked to sales order lines or used in nominal code mapping settings.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- `ids` (required, array) — nominal code IDs to check; must exist in nominal_codes table\n\n**Requires permission:** `nominal_codes.delete`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    10\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/nominal-codes/deletable","host":["{{baseUrl}}"],"path":["api","nominal-codes","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"code\": \"4000\",\n      \"name\": \"Sales Revenue\",\n      \"deletable\": false,\n      \"reason\": {\n        \"mappingNominalCodes\": \"Nominal code 'Sales Revenue' is used in accounting mapping settings.\"\n      }\n    },\n    {\n      \"id\": 2,\n      \"code\": \"5000\",\n      \"name\": \"Cost of Goods Sold\",\n      \"deletable\": false,\n      \"reason\": {\n        \"salesOrderLines\": \"1 sales order line is currently using nominal code(Cost of Goods Sold).\"\n      }\n    },\n    {\n      \"id\": 10,\n      \"code\": \"7001\",\n      \"name\": \"Digital Marketing Expense\",\n      \"deletable\": true,\n      \"reason\": null\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Check Archivable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/nominal-codes/archivable","host":["{{baseUrl}}"],"path":["api","nominal-codes","archivable"]},"description":"Check which nominal codes can be archived. A code cannot be archived if it is currently used in the nominal code mapping settings.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- `ids` (required, array) — nominal code IDs to check; must exist in nominal_codes table\n\n**Requires permission:** `nominal_codes.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    10\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/nominal-codes/archivable","host":["{{baseUrl}}"],"path":["api","nominal-codes","archivable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"code\": \"4000\",\n      \"name\": \"Sales Revenue\",\n      \"archivable\": false,\n      \"reason\": {\n        \"mappingNominalCodes\": \"Nominal code 'Sales Revenue' is currently used in a mapping.\"\n      }\n    },\n    {\n      \"id\": 10,\n      \"code\": \"7001\",\n      \"name\": \"Digital Marketing Expense\",\n      \"archivable\": true,\n      \"reason\": null\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Preview Import","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/nominal-codes/import/preview","host":["{{baseUrl}}"],"path":["api","nominal-codes","import","preview"]},"description":"Preview a nominal code import file before committing. Returns parsed rows and any validation errors.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data\n- `file` (required) — CSV or Excel file\n\n**Requires permission:** `nominal_codes.import`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/nominal-codes/import/preview","host":["{{baseUrl}}"],"path":["api","nominal-codes","import","preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"rows\": [\n      {\n        \"code\": \"8000\",\n        \"name\": \"Other Income\",\n        \"type\": \"Revenue\",\n        \"valid\": true,\n        \"errors\": []\n      }\n    ],\n    \"total\": 1,\n    \"valid\": 1,\n    \"invalid\": 0\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Import Nominal Codes","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/nominal-codes/import","host":["{{baseUrl}}"],"path":["api","nominal-codes","import"]},"description":"Import nominal codes from a CSV or Excel file.\n\n**Required scope: `accounting:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data\n- `file` (required) — CSV or Excel file\n\n**Requires permission:** `nominal_codes.import`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/nominal-codes/import","host":["{{baseUrl}}"],"path":["api","nominal-codes","import"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"imported\": 3,\n    \"skipped\": 0,\n    \"errors\": []\n  },\n  \"message\": \"Import completed successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"List Payment Types","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/payment-types?archived=0","host":["{{baseUrl}}"],"path":["api","payment-types"],"query":[{"key":"archived","value":"0","description":"Filter by archived status: 0 (active) or 1 (archived). Default: 0","disabled":true}]},"description":"List all payment types. Returns a flat collection (not paginated).\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nQuery parameters:\n- archived: 0 for active types, 1 for archived types. Default: 0\n\nNote: The Amazon and Shopify payment types are system-managed and cannot be deleted."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/payment-types?archived=0","host":["{{baseUrl}}"],"path":["api","payment-types"],"query":[{"key":"archived","value":"0","description":"Filter by archived status: 0 (active) or 1 (archived). Default: 0","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Credit Card\",\n      \"accounting_integration_id\": null,\n      \"created_at\": \"2024-01-01T00:00:00Z\",\n      \"updated_at\": \"2024-01-01T00:00:00Z\",\n      \"archived_at\": null\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Amazon\",\n      \"accounting_integration_id\": null,\n      \"created_at\": \"2024-01-01T00:00:00Z\",\n      \"updated_at\": \"2024-01-01T00:00:00Z\",\n      \"archived_at\": null\n    },\n    {\n      \"id\": 3,\n      \"name\": \"Shopify\",\n      \"accounting_integration_id\": null,\n      \"created_at\": \"2024-01-01T00:00:00Z\",\n      \"updated_at\": \"2024-01-01T00:00:00Z\",\n      \"archived_at\": null\n    }\n  ]\n}"}]},{"name":"Create Payment Type","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/payment-types","host":["{{baseUrl}}"],"path":["api","payment-types"]},"description":"Create a new payment type.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- name (required): Unique payment type name, max 255 characters\n\n**Requires permission:** `payment_types.create`","body":{"mode":"raw","raw":"{\n  \"name\": \"Wire Transfer\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/payment-types","host":["{{baseUrl}}"],"path":["api","payment-types"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"payment type has been successfully created\",\n  \"data\": {\n    \"id\": 10,\n    \"name\": \"Wire Transfer\",\n    \"accounting_integration_id\": null,\n    \"created_at\": \"2024-03-15T14:30:00Z\",\n    \"updated_at\": \"2024-03-15T14:30:00Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Bulk Delete Payment Types","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/payment-types","host":["{{baseUrl}}"],"path":["api","payment-types"]},"description":"Bulk delete multiple payment types by ID.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `payment_types.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/payment-types","host":["{{baseUrl}}"],"path":["api","payment-types"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 payment types deleted successfully\"\n}"}]},{"name":"Get Payment Type","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/payment-types/{payment_type}","host":["{{baseUrl}}"],"path":["api","payment-types",":payment_type"],"variable":[{"key":"payment_type","value":"1","description":"Payment type ID"}]},"description":"Retrieve a single payment type by ID.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/payment-types/{payment_type}","host":["{{baseUrl}}"],"path":["api","payment-types",":payment_type"],"variable":[{"key":"payment_type","value":"1","description":"Payment type ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Credit Card\",\n    \"accounting_integration_id\": null,\n    \"created_at\": \"2024-01-01T00:00:00Z\",\n    \"updated_at\": \"2024-01-01T00:00:00Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Update Payment Type","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/payment-types/{payment_type}","host":["{{baseUrl}}"],"path":["api","payment-types",":payment_type"],"variable":[{"key":"payment_type","value":"10","description":"Payment type ID"}]},"description":"Update an existing payment type.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- name (optional): New payment type name, must be unique, max 255 characters\n\n**Requires permission:** `payment_types.update`","body":{"mode":"raw","raw":"{\n  \"name\": \"Bank Wire Transfer\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/payment-types/{payment_type}","host":["{{baseUrl}}"],"path":["api","payment-types",":payment_type"],"variable":[{"key":"payment_type","value":"10","description":"Payment type ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"payment type Bank Wire Transfer has been successfully updated\",\n  \"data\": {\n    \"id\": 10,\n    \"name\": \"Bank Wire Transfer\",\n    \"accounting_integration_id\": null,\n    \"created_at\": \"2024-03-15T14:30:00Z\",\n    \"updated_at\": \"2024-03-15T15:00:00Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Delete Payment Type","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/payment-types/{payment_type}","host":["{{baseUrl}}"],"path":["api","payment-types",":payment_type"],"variable":[{"key":"payment_type","value":"10","description":"Payment type ID"}]},"description":"Delete a payment type. Amazon and Shopify payment types cannot be deleted (system-managed).\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `payment_types.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/payment-types/{payment_type}","host":["{{baseUrl}}"],"path":["api","payment-types",":payment_type"],"variable":[{"key":"payment_type","value":"10","description":"Payment type ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"payment type Bank Wire Transfer has been successfully deleted\"\n}"}]},{"name":"Check Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/payment-types/deletable","host":["{{baseUrl}}"],"path":["api","payment-types","deletable"]},"description":"Check which payment types can be deleted. Returns deletability status per ID.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody:\n- ids (required): Array of integer payment type IDs to check\n\n**Requires permission:** `payment_types.delete`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    10\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/payment-types/deletable","host":["{{baseUrl}}"],"path":["api","payment-types","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Credit Card\",\n      \"deletable\": true,\n      \"reason\": null\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Amazon\",\n      \"deletable\": false,\n      \"reason\": \"In use by 150 payments\"\n    },\n    {\n      \"id\": 10,\n      \"name\": \"Wire Transfer\",\n      \"deletable\": true,\n      \"reason\": null\n    }\n  ]\n}"}]},{"name":"Update Accounting Mapping","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/payment-types/accounting-mapping","host":["{{baseUrl}}"],"path":["api","payment-types","accounting-mapping"]},"description":"Map each payment method to the accounting account its payments post through.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Authentication:** Requires Bearer token.\n\n**Body fields:**\n- `payment_types` (array, required, min 1) - the mappings to save.\n- `payment_types[].id` (integer, required) - the payment method to map.\n- `payment_types[].accounting_integration_id` (integer or null, required key) - the id of the connected provider's bank account to post to. Send `null` to clear the mapping. The key must be present even when null.\n\nThe account is resolved against the connected accounting provider, so the record type is never sent by the caller. All or nothing: an id that is not one of that provider's accounts rejects the whole request and nothing is saved. Returns 422 on an unknown payment method or account id.\n\n**Requires permission:** `payment_types.update`","body":{"mode":"raw","raw":"{\n  \"payment_types\": [\n    {\n      \"id\": 1,\n      \"accounting_integration_id\": 41\n    },\n    {\n      \"id\": 2,\n      \"accounting_integration_id\": null\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/payment-types/accounting-mapping","host":["{{baseUrl}}"],"path":["api","payment-types","accounting-mapping"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"message\": null,\n  \"data\": null\n}"}]},{"name":"Archive Payment Type","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/payment-types/{paymentType}/archive","host":["{{baseUrl}}"],"path":["api","payment-types",":paymentType","archive"],"variable":[{"key":"paymentType","value":"10","description":"Payment type ID"}]},"description":"Archive a single payment type.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `payment_types.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/payment-types/{paymentType}/archive","host":["{{baseUrl}}"],"path":["api","payment-types",":paymentType","archive"],"variable":[{"key":"paymentType","value":"10","description":"Payment type ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"payment type Wire Transfer has been archived\",\n  \"data\": {\n    \"id\": 10,\n    \"name\": \"Wire Transfer\",\n    \"archived_at\": \"2024-03-16T10:00:00Z\"\n  }\n}"}]},{"name":"Unarchive Payment Type","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/payment-types/{paymentType}/unarchived","host":["{{baseUrl}}"],"path":["api","payment-types",":paymentType","unarchived"],"variable":[{"key":"paymentType","value":"10","description":"Payment type ID"}]},"description":"Unarchive a single payment type.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `payment_types.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/payment-types/{paymentType}/unarchived","host":["{{baseUrl}}"],"path":["api","payment-types",":paymentType","unarchived"],"variable":[{"key":"paymentType","value":"10","description":"Payment type ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"payment type Wire Transfer has been unarchived\",\n  \"data\": {\n    \"id\": 10,\n    \"name\": \"Wire Transfer\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Bulk Archive Payment Types","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/payment-types/archive","host":["{{baseUrl}}"],"path":["api","payment-types","archive"]},"description":"Bulk archive multiple payment types.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `payment_types.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    10,\n    11\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/payment-types/archive","host":["{{baseUrl}}"],"path":["api","payment-types","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 payment types archived successfully\"\n}"}]},{"name":"Bulk Unarchive Payment Types","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/payment-types/unarchive","host":["{{baseUrl}}"],"path":["api","payment-types","unarchive"]},"description":"Bulk unarchive multiple payment types.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `payment_types.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    10,\n    11\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/payment-types/unarchive","host":["{{baseUrl}}"],"path":["api","payment-types","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 payment types unarchived successfully\"\n}"}]},{"name":"List Payment Terms","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/payment-terms?page=1&per_page=10&sort=name&filter[due_date_type]=from_invoice_date&filter[is_default]=false&filter[archived]=all&filter[search]=net30","host":["{{baseUrl}}"],"path":["api","payment-terms"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default: 10)","disabled":true},{"key":"sort","value":"name","description":"Sort field. Prefix with - for descending. Allowed: id, name, net_days, discount_percentage, discount_days, due_date_type, applies_to, is_default, is_default_sales, created_at, updated_at","disabled":true},{"key":"filter[due_date_type]","value":"from_invoice_date","description":"Exact match. Values: immediate, from_invoice_date, end_of_month, end_of_next_month, day_of_month","disabled":true},{"key":"filter[is_default]","value":"false","description":"Exact match. Filter by default payment term flag (true/false)","disabled":true},{"key":"filter[archived]","value":"all","description":"Archive filter. Values: only (archived only), all (include archived), omit for active only","disabled":true},{"key":"filter[search]","value":"net30","description":"Search across id, name, description fields","disabled":true}]},"description":"Returns a paginated list of payment terms.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nSort fields: id, name, net_days, discount_percentage, discount_days, due_date_type, applies_to, is_default, is_default_sales, created_at, updated_at (default: name).\n\nBy default only active (non-archived) terms are returned. Use filter[archived]=all or filter[archived]=only to include archived records.\n\nScope filter: filter[applies_to_scope]=for_sales returns terms selectable on customers and sales orders; for_purchasing returns terms selectable on suppliers and purchase orders. filter[applies_to] matches the exact applies_to value (purchasing, sales, both)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/payment-terms?page=1&per_page=10&sort=name&filter[due_date_type]=from_invoice_date&filter[is_default]=false&filter[archived]=all&filter[search]=net30","host":["{{baseUrl}}"],"path":["api","payment-terms"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default: 10)","disabled":true},{"key":"sort","value":"name","description":"Sort field. Prefix with - for descending. Allowed: id, name, net_days, discount_percentage, discount_days, due_date_type, applies_to, is_default, is_default_sales, created_at, updated_at","disabled":true},{"key":"filter[due_date_type]","value":"from_invoice_date","description":"Exact match. Values: immediate, from_invoice_date, end_of_month, end_of_next_month, day_of_month","disabled":true},{"key":"filter[is_default]","value":"false","description":"Exact match. Filter by default payment term flag (true/false)","disabled":true},{"key":"filter[archived]","value":"all","description":"Archive filter. Values: only (archived only), all (include archived), omit for active only","disabled":true},{"key":"filter[search]","value":"net30","description":"Search across id, name, description fields","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Net 30\",\n      \"net_days\": 30,\n      \"discount_percentage\": 2,\n      \"discount_days\": 10,\n      \"due_date_type\": \"from_invoice_date\",\n      \"due_date_type_label\": \"Days After Invoice Date\",\n      \"applies_to\": \"both\",\n      \"applies_to_label\": \"Both\",\n      \"description\": \"2% discount if paid within 10 days, net due in 30 days\",\n      \"is_default\": true,\n      \"is_default_sales\": true,\n      \"is_consignment\": false,\n      \"consignment_settlement_frequency\": null,\n      \"treat_as_purchase\": false,\n      \"has_discount\": true,\n      \"generated_description\": \"2/10 Net 30\",\n      \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2024-01-15T10:00:00.000000Z\",\n      \"archived_at\": null\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Net 60\",\n      \"net_days\": 60,\n      \"discount_percentage\": 0,\n      \"discount_days\": null,\n      \"due_date_type\": \"from_invoice_date\",\n      \"due_date_type_label\": \"Days After Invoice Date\",\n      \"applies_to\": \"purchasing\",\n      \"applies_to_label\": \"Purchasing\",\n      \"description\": null,\n      \"is_default\": false,\n      \"is_default_sales\": false,\n      \"is_consignment\": false,\n      \"consignment_settlement_frequency\": null,\n      \"treat_as_purchase\": false,\n      \"has_discount\": false,\n      \"generated_description\": \"Net 60\",\n      \"created_at\": \"2024-01-16T09:30:00.000000Z\",\n      \"updated_at\": \"2024-01-16T09:30:00.000000Z\",\n      \"archived_at\": null\n    }\n  ],\n  \"first_page_url\": \"https://sku2.test/api/payment-terms?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://sku2.test/api/payment-terms?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://sku2.test/api/payment-terms\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 2,\n  \"total\": 2\n}"}]},{"name":"Create Payment Term","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/payment-terms","host":["{{baseUrl}}"],"path":["api","payment-terms"]},"description":"Creates a new payment term.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- name (required, string, max:255, unique)\n- net_days (required, integer, 0-365)\n- discount_percentage (optional, numeric, 0-100)\n- discount_days (optional, integer >= 0; must be less than net_days for types: from_invoice_date, end_of_month, day_of_month)\n- due_date_type (required): immediate | from_invoice_date | end_of_month | end_of_next_month | day_of_month\n- description (optional, string, max:500)\n- is_default (optional, boolean)\n- applies_to (optional): purchasing | sales | both (default: both). Which side of the business can select the term — purchasing (suppliers, purchase orders, bills), sales (customers, sales orders), or both. Terms using purchasing-only features (a vendor deposit schedule, consignment, or treat_as_purchase) must keep applies_to = purchasing.\n- is_default_sales (optional, boolean): marks the term as the account-wide default for sales orders, used when a customer has no default term of their own. Only one term can hold the flag — setting it clears it from any other term.\n\nVendor Deposit Schedule (optional, array):\n- vendor_deposit_schedule.*.label (optional, string)\n- vendor_deposit_schedule.*.amount_mode (optional): percent | fixed\n- vendor_deposit_schedule.*.percentage (numeric, 0-100)\n- vendor_deposit_schedule.*.fixed_amount (optional, numeric)\n- vendor_deposit_schedule.*.settlement_type (optional): vendor_deposit | final_invoice_balance. A schedule may carry several final_invoice_balance lines — they become the goods invoice's installment tranches, each dated by its own trigger + offset, and the bill's due date tracks the earliest tranche still owed\n- vendor_deposit_schedule.*.trigger_event (required): manual | po_approved | po_shipped | po_fully_shipped | po_received | po_fully_received | logistics_tracking_field | invoice_date. `invoice_date` (\"Bill Date\") is accepted only on a final_invoice_balance line, and never together with per_shipment: true or a shipment_date_basis — a bill on such a term is due its own bill date plus offset_days, with no shipment link or purchase-order event needed, e.g. `{\"label\": \"Balance net 30 from bill date\", \"settlement_type\": \"final_invoice_balance\", \"trigger_event\": \"invoice_date\", \"offset_days\": 30}`. Violations return 422 with \"Milestone N: Bill Date applies only to Final Goods Invoice milestones.\", \"Milestone N cannot repeat per shipment when it dates from the bill.\" or \"Milestone N cannot set a shipment date basis when it dates from the bill.\" (N is the line's 1-based position)\n- vendor_deposit_schedule.*.offset_days (optional, integer, nullable)\n- vendor_deposit_schedule.*.per_shipment (optional, boolean): repeat this milestone per shipment (prorated). Requires a per-shipment-capable trigger_event and a shipment_date_basis. Also allowed on the final_invoice_balance line — each shipment's goods invoice then derives its own due date from its linked shipment (projected from the shipment ETA until received)\n- vendor_deposit_schedule.*.shipment_date_basis (optional): dispatched | planned_etd | actual_dispatched | received_at\n- vendor_deposit_schedule.*.is_ownership_transfer (optional, boolean) — only one line per schedule may be true\n- vendor_deposit_schedule.*.logistics_tracking_field_id (optional, integer, exists:logistics_tracking_fields,id) — REQUIRED when trigger_event is logistics_tracking_field\n\n**Requires permission:** `payment_terms.create`\n\nConsignment fields: set is_consignment to true to mark the term as consignment (supplier-owned stock billed as it sells). consignment_settlement_frequency controls how often consigned sales are settled (on_demand, weekly, biweekly, monthly, quarterly). treat_as_purchase overrides consignment so goods received on this term are capitalized as owned inventory immediately at receipt (substance-over-form).","body":{"mode":"raw","raw":"{\n  \"name\": \"Net 45\",\n  \"net_days\": 45,\n  \"discount_percentage\": 1.5,\n  \"discount_days\": 10,\n  \"due_date_type\": \"from_invoice_date\",\n  \"description\": \"1.5% discount if paid within 10 days, net due in 45 days\",\n  \"is_default\": false,\n  \"applies_to\": \"both\",\n  \"is_default_sales\": false,\n  \"is_consignment\": false,\n  \"consignment_settlement_frequency\": null,\n  \"treat_as_purchase\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/payment-terms","host":["{{baseUrl}}"],"path":["api","payment-terms"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Net 45\",\n    \"net_days\": 45,\n    \"discount_percentage\": 1.5,\n    \"discount_days\": 10,\n    \"due_date_type\": \"from_invoice_date\",\n    \"due_date_type_label\": \"Days After Invoice Date\",\n    \"applies_to\": \"both\",\n    \"applies_to_label\": \"Both\",\n    \"description\": \"1.5% discount if paid within 10 days, net due in 45 days\",\n    \"is_default\": false,\n    \"is_default_sales\": false,\n    \"is_consignment\": false,\n    \"consignment_settlement_frequency\": null,\n    \"treat_as_purchase\": false,\n    \"has_discount\": true,\n    \"generated_description\": \"1.5/10 Net 45\",\n    \"created_at\": \"2024-03-01T12:00:00.000000Z\",\n    \"updated_at\": \"2024-03-01T12:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"payment term created successfully\"\n}"}]},{"name":"Bulk Delete Payment Terms","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/payment-terms","host":["{{baseUrl}}"],"path":["api","payment-terms"]},"description":"Bulk deletes multiple payment terms by IDs.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `payment_terms.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/payment-terms","host":["{{baseUrl}}"],"path":["api","payment-terms"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 payment terms deleted successfully\"\n}"}]},{"name":"Show Payment Term","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/payment-terms/{id}","host":["{{baseUrl}}"],"path":["api","payment-terms",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns a single payment term by ID. Includes due_date_type_options in the response for form use.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/payment-terms/{id}","host":["{{baseUrl}}"],"path":["api","payment-terms",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Net 30\",\n    \"net_days\": 30,\n    \"discount_percentage\": 2,\n    \"discount_days\": 10,\n    \"due_date_type\": \"from_invoice_date\",\n    \"due_date_type_label\": \"Days After Invoice Date\",\n    \"applies_to\": \"both\",\n    \"applies_to_label\": \"Both\",\n    \"description\": \"2% discount if paid within 10 days, net due in 30 days\",\n    \"is_default\": true,\n    \"is_default_sales\": true,\n    \"is_consignment\": false,\n    \"consignment_settlement_frequency\": null,\n    \"treat_as_purchase\": false,\n    \"has_discount\": true,\n    \"generated_description\": \"2/10 Net 30\",\n    \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"archived_at\": null,\n    \"due_date_type_options\": [\n      {\n        \"value\": \"immediate\",\n        \"label\": \"Due Immediately\",\n        \"description\": \"Payment is due immediately upon receipt of invoice.\",\n        \"netDaysLabel\": \"Net Days (not used)\",\n        \"usesNetDays\": false\n      },\n      {\n        \"value\": \"from_invoice_date\",\n        \"label\": \"Days After Invoice Date\",\n        \"description\": \"Due date = Invoice Date + Net Days.\",\n        \"netDaysLabel\": \"Net Days\",\n        \"usesNetDays\": true\n      },\n      {\n        \"value\": \"end_of_month\",\n        \"label\": \"End of Invoice Month + Days\",\n        \"description\": \"Due date = End of invoice month + Net Days.\",\n        \"netDaysLabel\": \"Additional Days After Month End\",\n        \"usesNetDays\": true\n      },\n      {\n        \"value\": \"end_of_next_month\",\n        \"label\": \"End of Next Month\",\n        \"description\": \"Due at the end of the month following the invoice date.\",\n        \"netDaysLabel\": \"Net Days (not used)\",\n        \"usesNetDays\": false\n      },\n      {\n        \"value\": \"day_of_month\",\n        \"label\": \"Specific Day of Month\",\n        \"description\": \"Due on a specific day of the next month.\",\n        \"netDaysLabel\": \"Day of Month (1-31)\",\n        \"usesNetDays\": true\n      }\n    ]\n  }\n}"}]},{"name":"Update Payment Term","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/payment-terms/{id}","host":["{{baseUrl}}"],"path":["api","payment-terms",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Updates an existing payment term by ID.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nAll fields same as Create, including the optional vendor_deposit_schedule array (with per-line logistics_tracking_field_id required when trigger_event is logistics_tracking_field). The `invoice_date` (Bill Date) trigger follows the same rules as on Create — only on a final_invoice_balance line, never with per_shipment or a shipment_date_basis; violations return 422 with \"Milestone N: Bill Date applies only to Final Goods Invoice milestones.\", \"Milestone N cannot repeat per shipment when it dates from the bill.\" or \"Milestone N cannot set a shipment date basis when it dates from the bill.\" Name uniqueness ignores the current record.\n\nConsignment fields: set is_consignment to true to mark the term as consignment (supplier-owned stock billed as it sells). consignment_settlement_frequency controls how often consigned sales are settled (on_demand, weekly, biweekly, monthly, quarterly). treat_as_purchase overrides consignment so goods received on this term are capitalized as owned inventory immediately at receipt (substance-over-form).","body":{"mode":"raw","raw":"{\n  \"name\": \"Net 45 Updated\",\n  \"net_days\": 45,\n  \"discount_percentage\": 2,\n  \"discount_days\": 15,\n  \"due_date_type\": \"from_invoice_date\",\n  \"description\": \"Updated payment terms\",\n  \"is_default\": false,\n  \"applies_to\": \"both\",\n  \"is_default_sales\": false,\n  \"is_consignment\": false,\n  \"consignment_settlement_frequency\": null,\n  \"treat_as_purchase\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/payment-terms/{id}","host":["{{baseUrl}}"],"path":["api","payment-terms",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Net 45 Updated\",\n    \"net_days\": 45,\n    \"discount_percentage\": 2,\n    \"discount_days\": 15,\n    \"due_date_type\": \"from_invoice_date\",\n    \"due_date_type_label\": \"Days After Invoice Date\",\n    \"applies_to\": \"both\",\n    \"applies_to_label\": \"Both\",\n    \"description\": \"Updated payment terms\",\n    \"is_default\": false,\n    \"is_default_sales\": false,\n    \"is_consignment\": false,\n    \"consignment_settlement_frequency\": null,\n    \"treat_as_purchase\": false,\n    \"has_discount\": true,\n    \"generated_description\": \"2/15 Net 45\",\n    \"created_at\": \"2024-03-01T12:00:00.000000Z\",\n    \"updated_at\": \"2024-03-01T14:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"payment term Net 45 Updated updated successfully\"\n}"}]},{"name":"Delete Payment Term","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/payment-terms/{id}","host":["{{baseUrl}}"],"path":["api","payment-terms",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Deletes a payment term by ID. Returns an error if the term is linked to purchase orders or suppliers.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/payment-terms/{id}","host":["{{baseUrl}}"],"path":["api","payment-terms",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Net 45 Updated deleted successfully\"\n}"}]},{"name":"Archive Payment Term","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/payment-terms/{id}/archive","host":["{{baseUrl}}"],"path":["api","payment-terms",":id","archive"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Archives a payment term. Returns a warning if already archived.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/payment-terms/{id}/archive","host":["{{baseUrl}}"],"path":["api","payment-terms",":id","archive"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Net 30\",\n    \"archived_at\": \"2024-03-01T15:00:00.000000Z\"\n  },\n  \"message\": \"payment term Net 30 archived successfully\"\n}"}]},{"name":"Unarchive Payment Term","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/payment-terms/{id}/unarchived","host":["{{baseUrl}}"],"path":["api","payment-terms",":id","unarchived"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Unarchives a payment term. Returns a warning if not currently archived.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/payment-terms/{id}/unarchived","host":["{{baseUrl}}"],"path":["api","payment-terms",":id","unarchived"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Net 30\",\n    \"archived_at\": null\n  },\n  \"message\": \"payment term Net 30 unarchived successfully\"\n}"}]},{"name":"Bulk Archive Payment Terms","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/payment-terms/archive","host":["{{baseUrl}}"],"path":["api","payment-terms","archive"]},"description":"Bulk archives multiple payment terms by IDs.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `payment_terms.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/payment-terms/archive","host":["{{baseUrl}}"],"path":["api","payment-terms","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 payment terms archived successfully\"\n}"}]},{"name":"Bulk Unarchive Payment Terms","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/payment-terms/unarchive","host":["{{baseUrl}}"],"path":["api","payment-terms","unarchive"]},"description":"Bulk unarchives multiple payment terms by IDs.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `payment_terms.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/payment-terms/unarchive","host":["{{baseUrl}}"],"path":["api","payment-terms","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 payment terms unarchived successfully\"\n}"}]},{"name":"Check Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/payment-terms/deletable","host":["{{baseUrl}}"],"path":["api","payment-terms","deletable"]},"description":"Checks which payment terms can be deleted (not linked to any purchase orders or suppliers).\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `payment_terms.delete`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/payment-terms/deletable","host":["{{baseUrl}}"],"path":["api","payment-terms","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Net 30\",\n      \"deletable\": false,\n      \"reason\": {\n        \"purchase_orders\": \"This payment term is currently used by 5 purchase orders.\"\n      }\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Net 60\",\n      \"deletable\": true,\n      \"reason\": null\n    },\n    {\n      \"id\": 3,\n      \"name\": \"Net 45\",\n      \"deletable\": true,\n      \"reason\": null\n    }\n  ]\n}"}]},{"name":"Create Incoterm","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/incoterms","host":["{{baseUrl}}"],"path":["api","incoterms"]},"description":"Manage the Incoterms 2020 reference table. Standard ICC rows carry structured trade-term metadata used to drive buyer/seller responsibility panels on purchase and sales orders.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nFields:\n- `code` (required, string, max 10, unique) — e.g. `DAP`, `FOB`. Locked for standard ICC rows.\n- `name` (string) — human label, e.g. `Delivered at Place`.\n- `description` (string)\n- `transport_mode` (enum) — `any` | `sea_inland`.\n- `family` (enum) — `E` | `F` | `C` | `D` (ICC term family).\n- `seller_pays_main_carriage` (boolean)\n- `export_cleared_by` (enum) — `seller` | `buyer`.\n- `import_cleared_by` (enum) — `seller` | `buyer`.\n- `seller_insurance` (enum) — `none` | `clause_a` | `clause_c`.\n- `risk_transfer_point` (string) — plain-English point where risk passes.\n- `sort_order` (integer)\n- `is_deprecated` (boolean) — flags retired rules (e.g. DAT).\n\n`is_standard` is server-maintained and NOT accepted in the request body.\n\nAuthentication: Requires Bearer token. Permission: `incoterms.create` / `incoterms.update`.","body":{"mode":"raw","raw":"{\n  \"code\": \"DAP\",\n  \"name\": \"Delivered at Place\",\n  \"description\": \"Seller delivers goods to the named destination. Buyer handles import customs clearance and duties.\",\n  \"transport_mode\": \"any\",\n  \"family\": \"D\",\n  \"seller_pays_main_carriage\": true,\n  \"export_cleared_by\": \"seller\",\n  \"import_cleared_by\": \"buyer\",\n  \"seller_insurance\": \"none\",\n  \"risk_transfer_point\": \"At the named place of destination, when goods are ready for unloading.\",\n  \"sort_order\": 12,\n  \"is_deprecated\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/incoterms","host":["{{baseUrl}}"],"path":["api","incoterms"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"incoterm created successfully.\",\n  \"data\": {\n    \"code\": \"DAP\",\n    \"id\": 12,\n    \"name\": \"Delivered at Place\",\n    \"description\": \"Seller delivers goods to the named destination. Buyer handles import customs clearance and duties.\",\n    \"transport_mode\": \"any\",\n    \"family\": \"D\",\n    \"seller_pays_main_carriage\": true,\n    \"export_cleared_by\": \"seller\",\n    \"import_cleared_by\": \"buyer\",\n    \"seller_insurance\": \"none\",\n    \"risk_transfer_point\": \"At the named place of destination, when goods are ready for unloading.\",\n    \"is_standard\": true,\n    \"is_deprecated\": false,\n    \"created_at\": \"2026-07-13T10:30:00.000000Z\",\n    \"updated_at\": \"2026-07-13T10:30:00.000000Z\",\n    \"archived_at\": null,\n    \"sort_order\": 4\n  }\n}"}]},{"name":"Bulk Delete Incoterms","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/incoterms","host":["{{baseUrl}}"],"path":["api","incoterms"]},"description":"Deletes multiple incoterms in a single request.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nRequest body:\n- ids (array, required): Array of incoterm IDs to delete\n\n**Requires permission:** `incoterms.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/incoterms","host":["{{baseUrl}}"],"path":["api","incoterms"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 incoterms deleted successfully.\"\n}"}]},{"name":"Get Incoterm","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/incoterms/{incoterm}","host":["{{baseUrl}}"],"path":["api","incoterms",":incoterm"],"variable":[{"key":"incoterm","value":"1","description":"Incoterm ID"}]},"description":"Returns the details of a single incoterm.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/incoterms/{incoterm}","host":["{{baseUrl}}"],"path":["api","incoterms",":incoterm"],"variable":[{"key":"incoterm","value":"1","description":"Incoterm ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"code\": \"EXW\",\n    \"id\": 1,\n    \"name\": \"Ex Works\",\n    \"description\": \"Ex Works — seller makes goods available at their premises.\",\n    \"transport_mode\": \"any\",\n    \"family\": \"E\",\n    \"seller_pays_main_carriage\": true,\n    \"export_cleared_by\": \"seller\",\n    \"import_cleared_by\": \"buyer\",\n    \"seller_insurance\": \"none\",\n    \"risk_transfer_point\": \"At the named place of destination, when goods are ready for unloading.\",\n    \"is_standard\": true,\n    \"is_deprecated\": false,\n    \"created_at\": \"2026-07-13T10:30:00.000000Z\",\n    \"updated_at\": \"2026-07-13T10:30:00.000000Z\",\n    \"archived_at\": null,\n    \"sort_order\": 4\n  }\n}"}]},{"name":"Update Incoterm","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/incoterms/{incoterm}","host":["{{baseUrl}}"],"path":["api","incoterms",":incoterm"],"variable":[{"key":"incoterm","value":"1","description":"Incoterm ID"}]},"description":"Manage the Incoterms 2020 reference table. Standard ICC rows carry structured trade-term metadata used to drive buyer/seller responsibility panels on purchase and sales orders.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nFields:\n- `code` (required, string, max 10, unique) — e.g. `DAP`, `FOB`. Locked for standard ICC rows.\n- `name` (string) — human label, e.g. `Delivered at Place`.\n- `description` (string)\n- `transport_mode` (enum) — `any` | `sea_inland`.\n- `family` (enum) — `E` | `F` | `C` | `D` (ICC term family).\n- `seller_pays_main_carriage` (boolean)\n- `export_cleared_by` (enum) — `seller` | `buyer`.\n- `import_cleared_by` (enum) — `seller` | `buyer`.\n- `seller_insurance` (enum) — `none` | `clause_a` | `clause_c`.\n- `risk_transfer_point` (string) — plain-English point where risk passes.\n- `sort_order` (integer)\n- `is_deprecated` (boolean) — flags retired rules (e.g. DAT).\n\n`is_standard` is server-maintained and NOT accepted in the request body.\n\nAuthentication: Requires Bearer token. Permission: `incoterms.create` / `incoterms.update`.","body":{"mode":"raw","raw":"{\n  \"code\": \"EXW\",\n  \"name\": \"Ex Works\",\n  \"description\": \"Ex Works — seller makes goods available at their premises.\",\n  \"transport_mode\": \"any\",\n  \"family\": \"E\",\n  \"seller_pays_main_carriage\": false,\n  \"export_cleared_by\": \"buyer\",\n  \"import_cleared_by\": \"buyer\",\n  \"seller_insurance\": \"none\",\n  \"risk_transfer_point\": \"At the seller's premises, once goods are placed at the buyer's disposal.\",\n  \"sort_order\": 1,\n  \"is_deprecated\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/incoterms/{incoterm}","host":["{{baseUrl}}"],"path":["api","incoterms",":incoterm"],"variable":[{"key":"incoterm","value":"1","description":"Incoterm ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"incoterm EXW updated successfully.\",\n  \"data\": {\n    \"code\": \"EXW\",\n    \"id\": 1,\n    \"name\": \"Ex Works\",\n    \"description\": \"Ex Works — seller makes goods available at their premises.\",\n    \"transport_mode\": \"any\",\n    \"family\": \"E\",\n    \"seller_pays_main_carriage\": true,\n    \"export_cleared_by\": \"seller\",\n    \"import_cleared_by\": \"buyer\",\n    \"seller_insurance\": \"none\",\n    \"risk_transfer_point\": \"At the named place of destination, when goods are ready for unloading.\",\n    \"is_standard\": true,\n    \"is_deprecated\": false,\n    \"created_at\": \"2026-07-13T10:30:00.000000Z\",\n    \"updated_at\": \"2026-07-13T10:30:00.000000Z\",\n    \"archived_at\": null,\n    \"sort_order\": 4\n  }\n}"}]},{"name":"Delete Incoterm","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/incoterms/{incoterm}","host":["{{baseUrl}}"],"path":["api","incoterms",":incoterm"],"variable":[{"key":"incoterm","value":"1","description":"Incoterm ID"}]},"description":"Deletes the specified incoterm. Will fail if the incoterm is linked to existing purchase orders or other records.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\n**Requires permission:** `incoterms.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/incoterms/{incoterm}","host":["{{baseUrl}}"],"path":["api","incoterms",":incoterm"],"variable":[{"key":"incoterm","value":"1","description":"Incoterm ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"incoterm EXW deleted successfully.\"\n}"}]},{"name":"Archive Incoterm","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/incoterms/{incoterm}/archive","host":["{{baseUrl}}"],"path":["api","incoterms",":incoterm","archive"],"variable":[{"key":"incoterm","value":"1","description":"Incoterm ID"}]},"description":"Archives the specified incoterm by setting its archived_at timestamp.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\n**Requires permission:** `incoterms.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/incoterms/{incoterm}/archive","host":["{{baseUrl}}"],"path":["api","incoterms",":incoterm","archive"],"variable":[{"key":"incoterm","value":"1","description":"Incoterm ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"incoterm EXW archived successfully.\",\n  \"data\": {\n    \"id\": 1,\n    \"code\": \"EXW\",\n    \"archived_at\": \"2024-01-15T10:30:00.000000Z\"\n  }\n}"}]},{"name":"Unarchive Incoterm","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/incoterms/{incoterm}/unarchived","host":["{{baseUrl}}"],"path":["api","incoterms",":incoterm","unarchived"],"variable":[{"key":"incoterm","value":"1","description":"Incoterm ID"}]},"description":"Restores (unarchives) the specified incoterm by clearing its archived_at timestamp.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\n**Requires permission:** `incoterms.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/incoterms/{incoterm}/unarchived","host":["{{baseUrl}}"],"path":["api","incoterms",":incoterm","unarchived"],"variable":[{"key":"incoterm","value":"1","description":"Incoterm ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"incoterm EXW unarchived successfully.\",\n  \"data\": {\n    \"id\": 1,\n    \"code\": \"EXW\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Check Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/incoterms/deletable","host":["{{baseUrl}}"],"path":["api","incoterms","deletable"]},"description":"Checks whether the specified incoterms can be deleted. Returns deletability status and reason if an incoterm is in use.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nRequest body:\n- ids (array, required, min:1): Array of incoterm IDs to check\n\n**Requires permission:** `incoterms.delete`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/incoterms/deletable","host":["{{baseUrl}}"],"path":["api","incoterms","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"code\": \"EXW\",\n      \"deletable\": false,\n      \"reason\": \"Linked to existing purchase orders\"\n    },\n    {\n      \"id\": 2,\n      \"code\": \"FOB\",\n      \"deletable\": true,\n      \"reason\": null\n    },\n    {\n      \"id\": 3,\n      \"code\": \"CIF\",\n      \"deletable\": true,\n      \"reason\": null\n    }\n  ]\n}"}]},{"name":"Bulk Archive Incoterms","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/incoterms/archive","host":["{{baseUrl}}"],"path":["api","incoterms","archive"]},"description":"Archives multiple incoterms.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nRequest body:\n- ids (array, required): Array of incoterm IDs to archive\n\n**Requires permission:** `incoterms.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/incoterms/archive","host":["{{baseUrl}}"],"path":["api","incoterms","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 incoterms archived successfully.\"\n}"}]},{"name":"Bulk Unarchive Incoterms","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/incoterms/unarchive","host":["{{baseUrl}}"],"path":["api","incoterms","unarchive"]},"description":"Restores (unarchives) multiple incoterms.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nRequest body:\n- ids (array, required): Array of incoterm IDs to unarchive\n\n**Requires permission:** `incoterms.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/incoterms/unarchive","host":["{{baseUrl}}"],"path":["api","incoterms","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 incoterms unarchived successfully.\"\n}"}]}]},{"name":"Cost Categories","item":[{"name":"List Cost Categories","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-categories","host":["{{baseUrl}}"],"path":["api","cost-categories"]},"description":"List all cost categories, ordered by name.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach category includes its `default_nominal_code_id` and, when set, an embedded `default_nominal_code` object ({id, code, name}). The default nominal code is the account a landed-cost bill line assigned to this category posts its debit to by default (an explicit nominal code on the line still wins; if the category has no default, posting falls back to the nominal code mapping settings).\n\nQuery parameters:\n- `selectable` (boolean) — exclude system categories\n- `is_active` (boolean) — filter by active/archived\n- `with_usage_counts` (boolean) — include usage counts\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-categories","host":["{{baseUrl}}"],"path":["api","cost-categories"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Accrued Purchases\",\n      \"is_system\": true,\n      \"is_active\": true,\n      \"default_nominal_code_id\": null,\n      \"default_nominal_code\": null,\n      \"created_at\": \"2024-01-10T09:00:00.000000Z\",\n      \"updated_at\": \"2024-01-10T09:00:00.000000Z\"\n    },\n    {\n      \"id\": 5,\n      \"name\": \"Freight\",\n      \"is_system\": false,\n      \"is_active\": true,\n      \"default_nominal_code_id\": 3,\n      \"default_nominal_code\": {\n        \"id\": 3,\n        \"code\": \"6000\",\n        \"name\": \"Shipping Expense\"\n      },\n      \"created_at\": \"2024-03-01T10:00:00.000000Z\",\n      \"updated_at\": \"2024-03-05T14:00:00.000000Z\"\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Create Cost Category","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-categories","host":["{{baseUrl}}"],"path":["api","cost-categories"]},"description":"Create a new cost category.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body fields:\n- `name` (required, string, max:255) — unique category name\n- `default_nominal_code_id` (optional, integer, nullable) — the nominal code a landed-cost bill line assigned to this category posts its debit to by default. Must exist in `nominal_codes`. Leave null to fall back to the nominal code mapping settings at posting time.","body":{"mode":"raw","raw":"{\n  \"name\": \"Freight\",\n  \"default_nominal_code_id\": 3\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-categories","host":["{{baseUrl}}"],"path":["api","cost-categories"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Freight\",\n    \"is_system\": false,\n    \"is_active\": true,\n    \"default_nominal_code_id\": 3,\n    \"default_nominal_code\": {\n      \"id\": 3,\n      \"code\": \"6000\",\n      \"name\": \"Shipping Expense\"\n    },\n    \"created_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"updated_at\": \"2024-03-05T14:00:00.000000Z\"\n  },\n  \"message\": \"cost category was created successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"List Cost Categories (Paginated)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-categories/list","host":["{{baseUrl}}"],"path":["api","cost-categories","list"]},"description":"Paginated, server-side-filtered list of cost categories. Supports search (name / id / keyword aliases), active/system/selectable filters, and sorting, returning a standard paginated envelope. Each item includes usage counts and, when set, the embedded default nominal code.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nUse this endpoint when building a browsable, filterable table of cost categories. For a full unpaginated list (e.g. to populate a dropdown) use `GET /api/cost-categories` instead.\n\nQuery parameters:\n- `filter[search]` — search term (partial match on name; scope with search_columns)\n- `search_columns` — restrict search to: name, id, keyword\n- `filter[is_active]` — 1 active / 0 archived\n- `filter[is_system]` — 1 system / 0 user-defined\n- `filter[selectable]` — 1 to exclude system categories\n- `sort` — id, name, is_system, is_active, created_at, updated_at (prefix - for DESC)\n- `page`, `per_page` — pagination (per_page default 10)\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-categories/list","host":["{{baseUrl}}"],"path":["api","cost-categories","list"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"name\": \"Freight\",\n      \"is_system\": true,\n      \"is_active\": true,\n      \"default_nominal_code_id\": 5,\n      \"default_nominal_code\": {\n        \"id\": 5,\n        \"code\": \"5000\",\n        \"name\": \"Cost of Goods Sold\"\n      },\n      \"usage_counts\": {\n        \"bill_lines\": 8,\n        \"breakdown_items\": 2,\n        \"financial_line_types\": 1,\n        \"total\": 11\n      },\n      \"created_at\": \"2024-01-10T09:00:00.000000Z\",\n      \"updated_at\": \"2024-03-02T14:20:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 8,\n  \"from\": 1,\n  \"to\": 8,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/cost-categories/list?page=1\",\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/cost-categories/list?page=1\",\n  \"next_page_url\": null,\n  \"prev_page_url\": null,\n  \"path\": \"{{protocol}}{{domain}}/api/cost-categories/list\"\n}"}]},{"name":"Get Cost Category","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-categories/{cost_category}","host":["{{baseUrl}}"],"path":["api","cost-categories",":cost_category"],"variable":[{"key":"cost_category","value":"5","description":"Cost category ID"}]},"description":"Show a single cost category with usage counts and its embedded `default_nominal_code` (when set).\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-categories/{cost_category}","host":["{{baseUrl}}"],"path":["api","cost-categories",":cost_category"],"variable":[{"key":"cost_category","value":"5","description":"Cost category ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Freight\",\n    \"is_system\": false,\n    \"is_active\": true,\n    \"default_nominal_code_id\": 3,\n    \"default_nominal_code\": {\n      \"id\": 3,\n      \"code\": \"6000\",\n      \"name\": \"Shipping Expense\"\n    },\n    \"usage_counts\": {\n      \"bill_lines\": 12,\n      \"breakdown_items\": 3,\n      \"financial_line_types\": 1,\n      \"total\": 16\n    },\n    \"created_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"updated_at\": \"2024-03-05T14:00:00.000000Z\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Update Cost Category","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-categories/{cost_category}","host":["{{baseUrl}}"],"path":["api","cost-categories",":cost_category"],"variable":[{"key":"cost_category","value":"5","description":"Cost category ID"}]},"description":"Update an existing cost category.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body fields:\n- `name` (required, string, max:255) — unique category name (ignores this record for the uniqueness check)\n- `default_nominal_code_id` (optional, integer, nullable) — default posting nominal code; must exist in `nominal_codes`. Send null to clear it and fall back to the nominal code mapping settings.\n\nNote: the name of a **system** cost category cannot be changed (returns 422).","body":{"mode":"raw","raw":"{\n  \"name\": \"Freight & Handling\",\n  \"default_nominal_code_id\": 3\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/cost-categories/{cost_category}","host":["{{baseUrl}}"],"path":["api","cost-categories",":cost_category"],"variable":[{"key":"cost_category","value":"5","description":"Cost category ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Freight\",\n    \"is_system\": false,\n    \"is_active\": true,\n    \"default_nominal_code_id\": 3,\n    \"default_nominal_code\": {\n      \"id\": 3,\n      \"code\": \"6000\",\n      \"name\": \"Shipping Expense\"\n    },\n    \"created_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"updated_at\": \"2024-03-05T14:00:00.000000Z\"\n  },\n  \"message\": \"cost category was updated successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Delete Cost Category","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/cost-categories/{cost_category}","host":["{{baseUrl}}"],"path":["api","cost-categories",":cost_category"],"variable":[{"key":"cost_category","value":"5","description":"Cost category ID"}]},"description":"Delete a cost category. Fails if the category is a system category, or if it is in use (bill lines / breakdown items / financial line types) unless `?force=true` is passed.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/cost-categories/{cost_category}","host":["{{baseUrl}}"],"path":["api","cost-categories",":cost_category"],"variable":[{"key":"cost_category","value":"5","description":"Cost category ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Cost category deleted.\",\n  \"status\": \"success\"\n}"}]},{"name":"Archive Cost Category","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/cost-categories/{cost_category}/archive","host":["{{baseUrl}}"],"path":["api","cost-categories",":cost_category","archive"],"variable":[{"key":"cost_category","value":"5","description":"Cost category ID"}]},"description":"Archive a cost category (soft-archive via is_active=false). System categories cannot be archived (returns 422).\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-categories/{cost_category}/archive","host":["{{baseUrl}}"],"path":["api","cost-categories",":cost_category","archive"],"variable":[{"key":"cost_category","value":"5","description":"Cost category ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Freight\",\n    \"is_system\": false,\n    \"is_active\": false,\n    \"default_nominal_code_id\": 3,\n    \"default_nominal_code\": {\n      \"id\": 3,\n      \"code\": \"6000\",\n      \"name\": \"Shipping Expense\"\n    },\n    \"created_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"updated_at\": \"2024-03-05T14:00:00.000000Z\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Unarchive Cost Category","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/cost-categories/{cost_category}/unarchive","host":["{{baseUrl}}"],"path":["api","cost-categories",":cost_category","unarchive"],"variable":[{"key":"cost_category","value":"5","description":"Cost category ID"}]},"description":"Unarchive a cost category (is_active=true).\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-categories/{cost_category}/unarchive","host":["{{baseUrl}}"],"path":["api","cost-categories",":cost_category","unarchive"],"variable":[{"key":"cost_category","value":"5","description":"Cost category ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Freight\",\n    \"is_system\": false,\n    \"is_active\": true,\n    \"default_nominal_code_id\": 3,\n    \"default_nominal_code\": {\n      \"id\": 3,\n      \"code\": \"6000\",\n      \"name\": \"Shipping Expense\"\n    },\n    \"created_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"updated_at\": \"2024-03-05T14:00:00.000000Z\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Merge Cost Category","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-categories/{cost_category}/merge","host":["{{baseUrl}}"],"path":["api","cost-categories",":cost_category","merge"],"variable":[{"key":"cost_category","value":"5","description":"Source cost category ID (merged away and deleted)"}]},"description":"Merge a cost category into a target. Re-points all references (bill lines, breakdown items, financial line types) from the source to the target, then deletes the source. Returns the target category.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body fields:\n- `target_id` (required, integer) — the cost category to merge into; must exist and differ from the source.","body":{"mode":"raw","raw":"{\n  \"target_id\": 6\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-categories/{cost_category}/merge","host":["{{baseUrl}}"],"path":["api","cost-categories",":cost_category","merge"],"variable":[{"key":"cost_category","value":"5","description":"Source cost category ID (merged away and deleted)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Freight\",\n    \"is_system\": false,\n    \"is_active\": true,\n    \"default_nominal_code_id\": 3,\n    \"default_nominal_code\": {\n      \"id\": 3,\n      \"code\": \"6000\",\n      \"name\": \"Shipping Expense\"\n    },\n    \"created_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"updated_at\": \"2024-03-05T14:00:00.000000Z\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Fuzzy Match Cost Category","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-categories/fuzzy-match?name=Freght","host":["{{baseUrl}}"],"path":["api","cost-categories","fuzzy-match"],"query":[{"key":"name","value":"Freght","description":"Required. The name to fuzzy-match against existing categories. Returns the closest match, or null if none is close enough.","disabled":true}]},"description":"Suggest a fuzzy-matched category for the given name — used by the create form to warn \"Did you mean Freight?\" before a near-duplicate is created. Returns null in `data` when no close match is found.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-categories/fuzzy-match?name=Freght","host":["{{baseUrl}}"],"path":["api","cost-categories","fuzzy-match"],"query":[{"key":"name","value":"Freght","description":"Required. The name to fuzzy-match against existing categories. Returns the closest match, or null if none is close enough.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Freight\",\n    \"is_system\": false,\n    \"is_active\": true,\n    \"default_nominal_code_id\": 3,\n    \"default_nominal_code\": {\n      \"id\": 3,\n      \"code\": \"6000\",\n      \"name\": \"Shipping Expense\"\n    },\n    \"created_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"updated_at\": \"2024-03-05T14:00:00.000000Z\"\n  },\n  \"status\": \"success\"\n}"}]}]},{"name":"Payment Terms","item":[{"name":"Preview Schedule Impact","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/payment-terms/{payment_term}/schedule-impact-preview","host":["{{baseUrl}}"],"path":["api","payment-terms",":payment_term","schedule-impact-preview"],"variable":[{"key":"payment_term","value":"1","description":"Payment term ID"}]},"description":"Dry-run a proposed vendor-deposit schedule against every non-terminal vendor deposit already created from this payment term, BEFORE saving the edit. Accepts the same `vendor_deposit_schedule` array as the update endpoint (`payment_schedule` alias also accepted; requires `payment_terms.update` permission).\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nPer affected deposit the outcome is:\n- `will_realign` — draft/pending deposits that Review & Resync can realign after saving\n- `shortfall` — settled deposits whose recorded amount is now below the new expectation\n- `overage` — settled deposits whose recorded amount now exceeds the new expectation\n- `unchanged` — settled deposits whose amount still matches\n\nSettled deposits are never mutated — shortfalls/overages surface on the vendor-deposit cash-flow report.","body":{"mode":"raw","raw":"{\n  \"vendor_deposit_schedule\": [\n    {\n      \"label\": \"Deposit\",\n      \"percentage\": 30,\n      \"trigger_event\": \"po_approved\",\n      \"offset_days\": 0\n    },\n    {\n      \"label\": \"Balance\",\n      \"percentage\": 70,\n      \"trigger_event\": \"po_fully_received\",\n      \"offset_days\": 30\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/payment-terms/{payment_term}/schedule-impact-preview","host":["{{baseUrl}}"],"path":["api","payment-terms",":payment_term","schedule-impact-preview"],"variable":[{"key":"payment_term","value":"1","description":"Payment term ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_revision\": 1,\n  \"next_revision\": 2,\n  \"affected_deposits\": [\n    {\n      \"vendor_deposit_id\": 12,\n      \"deposit_number\": \"VD-00012\",\n      \"deposit_status\": \"draft\",\n      \"purchase_order_number\": \"PO-00034\",\n      \"currency_code\": \"USD\",\n      \"line_index\": 1,\n      \"recorded_amount\": 200,\n      \"current_expected\": 200,\n      \"proposed_expected\": 300,\n      \"outcome\": \"will_realign\",\n      \"delta\": 100\n    },\n    {\n      \"vendor_deposit_id\": 9,\n      \"deposit_number\": \"VD-00009\",\n      \"deposit_status\": \"paid\",\n      \"purchase_order_number\": \"PO-00031\",\n      \"currency_code\": \"USD\",\n      \"line_index\": 1,\n      \"recorded_amount\": 200,\n      \"current_expected\": 200,\n      \"proposed_expected\": 300,\n      \"outcome\": \"shortfall\",\n      \"delta\": -100\n    }\n  ]\n}"}]}]},{"name":"Ledger (Accounting v2)","item":[{"name":"Get Dashboard Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/dashboard","host":["{{baseUrl}}"],"path":["api","ledger","dashboard"]},"description":"The Accounting v2 section landing summary — the whole dashboard in one call. Composed from the existing managers/repositories so it never diverges from the tab it summarizes.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Sections:**\n- `generation_health` — the derived backlog signal (`state`: healthy | behind | failing | paused) with its label + Vuetify color, plus the raw inputs: kill-switch `enabled`, `environment_override`, outbox `pending`/`processing`/`failed` (`failed` counts unresolved failures only — those no newer attempt has superseded), the drain `last_processed_at` heartbeat, `stale_after_minutes` (5), and the next scheduled runs. `behind` requires BOTH queued work AND a stale heartbeat — a large-but-draining queue stays healthy.\n- `kpis` — four headline counts: `pending_sync` (pending_sync + update_pending + syncing + retrying), `needs_attention` (needs_attention + failed + conflict + removed_in_provider), `synced`, and `outbox_backlog` (outbox pending + processing + unresolved-failed source documents not yet turned into entries — matches the Outbox listing's `backlog` filter row for row).\n- `status_breakdown` — per-display-status counts in enum order, each with label + color (click a segment to open the All tab filtered on that `display_status`). Statuses with no entries are included with count 0.\n- `needs_attention` — the parked `total`, the `group_count`, and the top 5 error-cause `groups`.\n- `connections` — the accounting connections view-model (provider, health, lock date, rate budget, last inbound, open ops).\n- `reconciliation` — `balance_mismatches`: current entries whose line sums disagree with their header totals.\n- `recent_entries` — the 5 most recent entries (same shape as the entries table)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/dashboard","host":["{{baseUrl}}"],"path":["api","ledger","dashboard"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"dashboard\": {\n      \"generation_health\": {\n        \"state\": \"behind\",\n        \"label\": \"Behind\",\n        \"color\": \"warning\",\n        \"enabled\": true,\n        \"environment_override\": false,\n        \"pending\": 42,\n        \"processing\": 0,\n        \"failed\": 0,\n        \"last_processed_at\": \"2026-06-12T02:21:00+00:00\",\n        \"stale_after_minutes\": 5,\n        \"schedule\": {\n          \"next_generation_run_at\": \"2026-06-12T20:21:00+00:00\",\n          \"next_reconcile_run_at\": \"2026-06-13T00:00:00+00:00\"\n        }\n      },\n      \"kpis\": {\n        \"pending_sync\": 38,\n        \"needs_attention\": 14,\n        \"synced\": 1240,\n        \"outbox_backlog\": 42\n      },\n      \"status_breakdown\": [\n        {\n          \"key\": \"excluded\",\n          \"label\": \"Excluded\",\n          \"color\": \"grey\",\n          \"count\": 12\n        },\n        {\n          \"key\": \"pending_sync\",\n          \"label\": \"Pending sync\",\n          \"color\": \"info\",\n          \"count\": 28\n        },\n        {\n          \"key\": \"syncing\",\n          \"label\": \"Syncing\",\n          \"color\": \"info\",\n          \"count\": 4\n        },\n        {\n          \"key\": \"synced\",\n          \"label\": \"Synced\",\n          \"color\": \"success\",\n          \"count\": 1240\n        },\n        {\n          \"key\": \"update_pending\",\n          \"label\": \"Update pending\",\n          \"color\": \"warning\",\n          \"count\": 5\n        },\n        {\n          \"key\": \"retrying\",\n          \"label\": \"Retrying\",\n          \"color\": \"warning\",\n          \"count\": 1\n        },\n        {\n          \"key\": \"needs_attention\",\n          \"label\": \"Needs attention\",\n          \"color\": \"error\",\n          \"count\": 9\n        },\n        {\n          \"key\": \"failed\",\n          \"label\": \"Failed\",\n          \"color\": \"error\",\n          \"count\": 3\n        },\n        {\n          \"key\": \"removed_in_provider\",\n          \"label\": \"Removed in provider\",\n          \"color\": \"error\",\n          \"count\": 1\n        },\n        {\n          \"key\": \"conflict\",\n          \"label\": \"Conflict\",\n          \"color\": \"error\",\n          \"count\": 1\n        },\n        {\n          \"key\": \"voided\",\n          \"label\": \"Synced, Voided\",\n          \"color\": \"warning\",\n          \"count\": 2\n        }\n      ],\n      \"needs_attention\": {\n        \"total\": 14,\n        \"group_count\": 2,\n        \"groups\": [\n          {\n            \"cause_key\": \"user_action_required:d2b5c3a1e4f6a7b8c9d0e1f2a3b4c5d6\",\n            \"label\": \"Account # Sales Revenue is inactive in QuickBooks Online.\",\n            \"error_class\": \"user_action_required\",\n            \"count\": 11\n          },\n          {\n            \"cause_key\": \"auth_connection:9f1c0b2a3d4e5f60718293a4b5c6d7e8\",\n            \"label\": \"The Xero connection needs to be re-authorized.\",\n            \"error_class\": \"auth_connection\",\n            \"count\": 3\n          }\n        ]\n      },\n      \"connections\": [\n        {\n          \"id\": 3,\n          \"provider\": \"qbo\",\n          \"name\": \"QuickBooks Online\",\n          \"health\": {\n            \"sync_status\": \"active\",\n            \"paused\": false\n          },\n          \"lock_date\": \"2026-04-30\",\n          \"rate_budget_remaining\": null,\n          \"last_inbound_event_at\": \"2026-06-10T18:42:11+00:00\",\n          \"open_operations\": {\n            \"pending\": 4,\n            \"queued\": 2,\n            \"in_flight\": 1\n          }\n        }\n      ],\n      \"reconciliation\": {\n        \"balance_mismatches\": 0\n      },\n      \"recent_entries\": [\n        {\n          \"id\": 1842,\n          \"uuid\": \"9c2f1b7e-4a6d-4f0e-9b1a-2d8c5e7f3a10\",\n          \"type\": {\n            \"value\": \"sales_order_invoice\",\n            \"label\": \"Sales Order Invoice\"\n          },\n          \"status\": \"posted\",\n          \"display_status\": {\n            \"key\": \"synced\",\n            \"label\": \"Synced\",\n            \"color\": \"success\",\n            \"reason\": null\n          },\n          \"effective_at\": \"2026-06-05T14:30:00+00:00\",\n          \"posted_at\": \"2026-06-05T14:35:12+00:00\",\n          \"currency_code\": \"USD\",\n          \"total_debit\": \"249.9900\",\n          \"total_credit\": \"249.9900\",\n          \"reference\": \"SO-10293\",\n          \"reference_suffix\": null,\n          \"description\": \"Sales order invoice for SO-10293\",\n          \"source\": {\n            \"type\": \"App\\\\Models\\\\SalesOrder\",\n            \"id\": 10293,\n            \"label\": \"SO-10293\",\n            \"link_type_label\": \"Sales Order\",\n            \"link_route\": \"/orders/sales-orders/10293\",\n            \"link_legacy_url\": null,\n            \"url\": \"/orders/sales-orders/10293\"\n          },\n          \"batch_id\": null,\n          \"sync_override\": null,\n          \"integrity_flag\": null,\n          \"reversal_of_id\": null,\n          \"replaced_by_id\": null,\n          \"created_at\": \"2026-06-05T14:30:02+00:00\",\n          \"provider_link\": null\n        }\n      ]\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"List Outbox Failures","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/outbox/failures","host":["{{baseUrl}}"],"path":["api","ledger","outbox","failures"]},"description":"Paginated terminally-failed `ledger_outbox` rows for the dashboard failures drill-down, newest failure first (`processed_at` carries the failure timestamp for failed rows).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Pagination:** `per_page` (default 10, clamped to 100), `page`. Standard Laravel pagination envelope.\n\n**Each row:**\n- `source` — the source-document cross-link resolved from source_type/source_id (NOT hydrated — failed rows often point at since-deleted documents, so `label`/`link_route` may be null; `link_type_label` always renders).\n- `event` — the failed source event (created | updated | deleted) with a headline label.\n- `attempts` — how many drain attempts were exhausted before the row terminated.\n- `last_error` — the preserved error text (capped at 60 KB).\n- `failed_at` / `available_at` / `created_at` — ISO-8601 timestamps."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/outbox/failures","host":["{{baseUrl}}"],"path":["api","ledger","outbox","failures"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 9182,\n      \"source\": {\n        \"type\": \"App\\\\Models\\\\Bill\",\n        \"id\": 4412,\n        \"label\": null,\n        \"link_type_label\": \"Bill\",\n        \"link_route\": null,\n        \"link_legacy_url\": null,\n        \"url\": null\n      },\n      \"event\": {\n        \"value\": \"updated\",\n        \"label\": \"Updated\"\n      },\n      \"attempts\": 5,\n      \"last_error\": \"QuickBooks Online rejected the request: Account 'Sales Revenue' is inactive.\",\n      \"available_at\": \"2026-06-12T02:18:00+00:00\",\n      \"failed_at\": \"2026-06-12T02:21:00+00:00\",\n      \"created_at\": \"2026-06-12T01:40:00+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/ledger/outbox/failures?page=1\",\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/ledger/outbox/failures?page=1\",\n  \"next_page_url\": null,\n  \"prev_page_url\": null,\n  \"path\": \"{{protocol}}{{domain}}/api/ledger/outbox/failures\",\n  \"links\": []\n}"}]},{"name":"Retry Outbox Failures","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/outbox/failures/retry","host":["{{baseUrl}}"],"path":["api","ledger","outbox","failures","retry"]},"description":"Re-queue terminally-failed outbox rows for another drain pass: reset them to pending, clear the attempt counter (a fresh max-attempts budget — otherwise the next failure would immediately re-terminate them), make them claimable now, and kick the drain.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token. Requires the `accounting.sync` permission.\n\n**Body:** `{ \"ids\": [9182, 9183] }` retries the given failed rows; omit `ids` or pass `[]` to retry EVERY terminally-failed row.\n\n**Response:** `data.requeued` — the number of rows re-queued (0 if there were no failures to retry).","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    9182,\n    9183\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/outbox/failures/retry","host":["{{baseUrl}}"],"path":["api","ledger","outbox","failures","retry"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"requeued\": 2\n  },\n  \"message\": \"2 failed rows re-queued — generation will reprocess them shortly.\",\n  \"status\": \"success\"\n}"}]},{"name":"List Journal Entries","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries","host":["{{baseUrl}}"],"path":["api","ledger","entries"]},"description":"Paginated journal entries listing. Discarded regeneration drafts are excluded; reversed entries stay visible so the reversal chain is browsable.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token (Personal Access Token).\n\n**Filters (Spatie QueryBuilder):**\n- `filter[type]` — exact entry type (AccountingTransactionTypeEnum, ~30 values, e.g. sales_order_invoice, fba_shipments, bill)\n- `filter[status]` — exact lifecycle status: draft, posted, reversed\n- `filter[display_status]` — exact, comma-multi supported. Values: excluded, pending_sync, syncing, synced, update_pending, retrying, needs_attention, failed, removed_in_provider, conflict, voided\n- `filter[sync_routing]` — Routing/cadence axis (individual | daily | monthly)\n- `filter[source_type]` — exact source morph class\n- `filter[batch_id]` — exact batch ID\n- `filter[date_from]` / `filter[date_to]` — m/d/Y on effective_at (invalid dates silently ignored)\n- `filter[connection]` — connection ID; entries holding a sync link on that connection\n- `filter[search]` — partial match on reference, description, amount (total_debit), id, the resolved counterparty name and the synced provider document id (remote_id — e.g. a Xero invoice ID); scope with `search_columns` (e.g. `search_columns=remote_id`) and force exact match with `search_strict_columns`\n\n**Sorts:** id, effective_at, reference, total_debit, created_at, type, counterparty, due_date, display_status, sync_routing (default: -effective_at). `type` sorts the stored entry-type enum; `counterparty` sorts the resolved supplier/customer/marketplace/provider name across polymorphic sources; `due_date` sorts the source purchase invoice's due date (other sources sort NULL).\n\n**Pagination:** per_page default 10, clamped to max 100.\n\nEach row carries a `display_status` object (key/label/color/reason). A batch member has no standalone status — its `display_status` mirrors its batch aggregate's live status at read time (Pending Sync / Syncing / Synced / Failed …); there is deliberately no `batched` status value. `reason` is the chip-tooltip hint: `batch_member` for batch members, comma-joined eligibility exclusion reasons (or `no_accounting_connection`) for excluded entries, null otherwise. Detail-only sections (lines, sync_links, operations, reversal_chain) are NOT included on the index — use the show endpoint.\n\n**Status is null for historical entries:** the `display_status` and `routing` objects each report null key/label/color when an entry has left the live books — it is discarded (superseded by a regenerated replacement: `replaced_by_id` set + `discarded_at`) or a reversed original (`status = reversed`). Such rows can never sync (the batch rollup, reports and live lists all exclude them), so a frozen `synced` status would mislead; the supersession marker carries the why instead. A live reversal contra entry (`reversal_of_id` set, still posted) is NOT historical and keeps its status.\n\n**Advanced filters (`filter_groups`):** base64-encoded JSON tree of AND/OR groups, applied via GroupedFilterSupport (same stack as Purchase Orders). Decoded shape:\n```\n{\n  \"conjunction\": \"and\",\n  \"children\": [\n    {\"type\": \"condition\", \"condition\": {\"column\": \"reference\", \"operator\": \"contains\", \"value\": \"INV\"}},\n    {\"type\": \"group\", \"group\": {\"conjunction\": \"or\", \"children\": [...]}}\n  ]\n}\n```\n**Filterable columns:** `reference`, `description`, `entry_type`, `entry_status`, `entry_display_status`, `currency_code`, `counterparty` (text: is, is_not, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, is_one_of); `id`, `total_debit`, `total_credit`, `entry_batch_id` (numeric: is, is_not, is_one_of, greater_than, less_than, greater_than_or_equal, less_than_or_equal, between, is_empty, is_not_empty); `nominal_code_id` (text, relationship — matches entries posting to the nominal code on any line); `effective_at` (date), `posted_at`, `created_at` (datetime: is, is_not, before, after, on_or_before, on_or_after, between, today, yesterday, past_week, past_month, …). `counterparty` has no stored column — it filters on the resolved supplier/customer/marketplace name via the same correlated subquery used for search and sort.\n\nUnregistered columns in the tree are rejected with HTTP 400 (InvalidFilterQuery).\n\nEach entry includes `provider_link` — the accounting-provider document it is synced to (the Xero/QBO column): { provider, remote_type, remote_id, link_type_label, label, link_route (internal detail page, or null when none exists yet), remote_url (provider deep link) }. It is null for entries not yet synced to a provider.\n\n**Per-lens columns (`lens` query param):** the scoped Transactions tabs (Invoices / Journals / Purchase Orders) are thin wrappers over this one endpoint and request extra columns via `?lens=`. The All tab, dashboard, detail and export paths send no lens, so the response keeps the shared columns only with zero extra queries. When a lens is present each row gains a derive-on-read block:\n- `lens=invoices` adds `counterparty` ({ name, id, link_type_label, link_route }; a CRM party has `link_type_label` \"Supplier\"/\"Customer\" + numeric `id` + `link_route`; an AR source (sales order / sales credit) with no CRM customer falls back to a marketplace customer — `name` \"{Integration} Customer\" e.g. \"Amazon Customer\", `link_type_label` \"Customer\", `id` + `link_route` null; a settlement source (Amazon financial-event group) falls back to a bare marketplace label — `name` the integration name e.g. \"Amazon\", `link_type_label` \"Marketplace\", `id` + `link_route` null; a ShipMyOrders provider invoice (a 3PL fulfilment-provider bill, no CRM party) falls back to a fixed provider label — `name` \"SMO\", `link_type_label` \"Provider\", `id` + `link_route` null; null/absent when the source resolves no party or was deleted), `due_date` (YYYY-MM-DD date-only, no timezone conversion; null unless the source carries one — only purchase invoices do), and `tax_total` (scale-4 string summed across the entry's lines, or null when no line carries tax).\n- `lens=journals` adds `debit_account` and `credit_account` — each is either { split: false, nominal_code_id, code, name, link_route } for a side posting to one account, or { split: true, count } when the side fans out across several codes, or null when the side has no lines — plus `batch` ({ id, label, status, period_start, period_end, link_route } or null for unbatched entries).\n- `lens=purchase-orders` adds `counterparty` (the supplier, same shape as above) and `stage` ({ ordered: true, received: none|partial|full, invoiced: none|partial|full }) derived from the source purchase order's receipt_status / invoice_status.\n\nEach row carries `line_count` — the number of GL-impact journal lines on the entry (cheap `withCount` subquery) — powering the entries table's inline expandable \"Lines\" column. Fetch the lines themselves via **Get Journal Entry Lines** (`/api/ledger/entries/{entry}/lines`).\n\n**`can_sync_now`** (boolean) — whether a manual \"Sync Now\" can push this entry right now. The per-transaction-type Sync toggle, granularity opt-out and the per-type start date only gate *automatic/scheduled* sync; a manual push bypasses them, so `can_sync_now` stays `true` for a benign \"Auto-off\" entry. It is `false` only when a *structural* blocker applies (connection inactive, entry is a batch member / open batch, internal-only, or before the accounting provider's conversion/start date) — those block even a manual push. Drives the UI split between a grey \"Auto-off · push anytime\" chip (`display_status=excluded` + `can_sync_now=true`) and an amber \"Blocked\" chip (`display_status=excluded` + `can_sync_now=false`).\n\n**provider_status** (response field) — the provider's own transaction status on the synced document (primary sync link remote_status); null for unsynced entries. Backs the Provider Status column + filter."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/entries","host":["{{baseUrl}}"],"path":["api","ledger","entries"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1842,\n      \"uuid\": \"9c2f1b7e-4a6d-4f0e-9b1a-2d8c5e7f3a10\",\n      \"type\": {\n        \"value\": \"sales_order_invoice\",\n        \"label\": \"Sales Order Invoice\"\n      },\n      \"status\": \"posted\",\n      \"display_status\": {\n        \"key\": \"synced\",\n        \"label\": \"Synced\",\n        \"color\": \"success\",\n        \"reason\": null\n      },\n      \"can_sync_now\": true,\n      \"locked_update_pending\": false,\n      \"locked_amend_blocked\": false,\n      \"routing\": {\n        \"key\": \"individual\",\n        \"label\": \"Individual\",\n        \"color\": \"grey\",\n        \"batch_id\": null,\n        \"batch_route\": null\n      },\n      \"effective_at\": \"2026-06-05T14:30:00+00:00\",\n      \"posted_at\": \"2026-06-05T14:35:12+00:00\",\n      \"currency_code\": \"USD\",\n      \"total_debit\": \"249.9900\",\n      \"total_credit\": \"249.9900\",\n      \"document_total\": \"249.9900\",\n      \"document_number\": \"INV-1842\",\n      \"lineage_root_id\": 1842,\n      \"document_number_is_inherited\": false,\n      \"document_number_is_provider_assigned\": false,\n      \"reference\": \"SO-10293\",\n      \"reference_suffix\": null,\n      \"description\": \"Sales order invoice for SO-10293\",\n      \"source\": {\n        \"type\": \"App\\\\Models\\\\SalesOrder\",\n        \"id\": 10293,\n        \"label\": \"SO-10293\",\n        \"link_type_label\": \"Sales Order\",\n        \"link_route\": \"/orders/sales-orders/10293\",\n        \"link_legacy_url\": null,\n        \"url\": \"/orders/sales-orders/10293\"\n      },\n      \"batch_id\": null,\n      \"sync_override\": null,\n      \"integrity_flag\": null,\n      \"reversal_of_id\": null,\n      \"replaced_by_id\": null,\n      \"created_at\": \"2026-06-05T14:30:02+00:00\",\n      \"provider_link\": {\n        \"provider\": \"Xero\",\n        \"remote_type\": \"xero_invoice\",\n        \"remote_id\": \"a1b2c3d4-0000-0000-0000-000000000000\",\n        \"link_type_label\": \"Invoice\",\n        \"label\": \"INV-1042\",\n        \"link_route\": \"/integrations/xero/invoices/55\",\n        \"remote_url\": \"https://go.xero.com/AccountsReceivable/View.aspx?InvoiceID=a1b2c3d4-0000-0000-0000-000000000000\"\n      },\n      \"provider_status\": \"AUTHORISED\"\n    },\n    {\n      \"id\": 1841,\n      \"uuid\": \"5e8d2c1a-9f3b-4e7d-8a60-1b4c6d9e2f55\",\n      \"type\": {\n        \"value\": \"fba_shipments\",\n        \"label\": \"FBA Shipments\"\n      },\n      \"status\": \"draft\",\n      \"display_status\": {\n        \"key\": \"pending_sync\",\n        \"label\": \"Pending Sync\",\n        \"color\": \"info\",\n        \"reason\": \"batch_member\"\n      },\n      \"can_sync_now\": false,\n      \"locked_update_pending\": false,\n      \"locked_amend_blocked\": false,\n      \"routing\": {\n        \"key\": \"daily\",\n        \"label\": \"Daily\",\n        \"color\": \"purple\",\n        \"batch_id\": 57,\n        \"batch_route\": \"/ledger/batches/57\"\n      },\n      \"effective_at\": \"2026-06-04T09:00:00+00:00\",\n      \"posted_at\": null,\n      \"currency_code\": \"USD\",\n      \"total_debit\": \"1240.5000\",\n      \"total_credit\": \"1240.5000\",\n      \"document_total\": \"1240.5000\",\n      \"document_number\": \"JE-1841\",\n      \"lineage_root_id\": 1841,\n      \"document_number_is_inherited\": false,\n      \"document_number_is_provider_assigned\": false,\n      \"reference\": \"FBA-SHIP-2026-06-04\",\n      \"reference_suffix\": null,\n      \"description\": \"FBA shipments for 06/04/2026\",\n      \"source\": {\n        \"type\": \"Modules\\\\Amazon\\\\Entities\\\\AmazonLedger\",\n        \"id\": 88451,\n        \"label\": \"#88451\",\n        \"link_type_label\": \"Amazon Ledger\",\n        \"link_route\": null,\n        \"link_legacy_url\": null,\n        \"url\": null\n      },\n      \"batch_id\": 57,\n      \"sync_override\": null,\n      \"integrity_flag\": null,\n      \"reversal_of_id\": null,\n      \"replaced_by_id\": null,\n      \"created_at\": \"2026-06-04T09:02:41+00:00\",\n      \"provider_link\": null,\n      \"provider_status\": \"AUTHORISED\"\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"https://sku-alt.test/api/ledger/entries?page=1\",\n  \"from\": 1,\n  \"last_page\": 93,\n  \"last_page_url\": \"https://sku-alt.test/api/ledger/entries?page=93\",\n  \"links\": [\n    {\n      \"url\": null,\n      \"label\": \"&laquo; Previous\",\n      \"active\": false\n    },\n    {\n      \"url\": \"https://sku-alt.test/api/ledger/entries?page=1\",\n      \"label\": \"1\",\n      \"active\": true\n    },\n    {\n      \"url\": \"https://sku-alt.test/api/ledger/entries?page=2\",\n      \"label\": \"Next &raquo;\",\n      \"active\": false\n    }\n  ],\n  \"next_page_url\": \"https://sku-alt.test/api/ledger/entries?page=2\",\n  \"path\": \"https://sku-alt.test/api/ledger/entries\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 10,\n  \"total\": 924\n}"}]},{"name":"Get Journal Entry Aggregates","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/aggregates?aggregates[total_debit]=sum","host":["{{baseUrl}}"],"path":["api","ledger","entries","aggregates"],"query":[{"key":"aggregates[total_debit]","value":"sum","description":"Map of column => calculation. Repeat for each column to aggregate. Calculations: sum, avg, min, max, range, count_all, count_values, count_unique, count_empty, count_not_empty.","disabled":true}]},"description":"Compute summary aggregations over the full filtered journal entries list in a single request.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAccepts the same filter and search parameters as List Journal Entries, plus an `aggregates` map of column => calculation. Discarded and reversed entries are excluded by default, matching List Journal Entries.\n\n**Calculations:** sum, avg, min, max, range (max - min), count_all (matching entries), count_values / count_not_empty (entries with a value), count_unique (distinct values), count_empty (entries without a value).\n\n**Aggregatable columns:** id, total_debit, line_count.\n\nUnknown columns are silently ignored. A `null` value means no matching entries carried a value for that column. An unsupported calculation returns a 422 validation error.\n\nAuthentication: Requires Bearer token. Supply `ids[]` to constrain the calculation to specific records; omit it to cover the whole filtered set."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/entries/aggregates?aggregates[total_debit]=sum","host":["{{baseUrl}}"],"path":["api","ledger","entries","aggregates"],"query":[{"key":"aggregates[total_debit]","value":"sum","description":"Map of column => calculation. Repeat for each column to aggregate. Calculations: sum, avg, min, max, range, count_all, count_values, count_unique, count_empty, count_not_empty.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_debit\": {\n      \"calc\": \"sum\",\n      \"value\": 45210.87\n    },\n    \"line_count\": {\n      \"calc\": \"sum\",\n      \"value\": 1284\n    }\n  }\n}"}]},{"name":"List Needs-Attention Groups","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/needs-attention","host":["{{baseUrl}}"],"path":["api","ledger","entries","needs-attention"]},"description":"The needs-attention inbox feed: non-discarded entries whose display status needs a human (needs_attention, failed, conflict, removed_in_provider — capped at the latest 1000), grouped server-side by error cause so the UI can offer fix-once-retry-all.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Grouping:** each group keys on the latest sync operation's `error_class` plus a normalized cause message (numeric IDs masked to `#` so per-record noise collapses into one cause, truncated to 160 chars). Entries with no operation error group under `status:<display_status>`. Groups are sorted by count descending.\n\n**Response shape (per group):**\n- `cause_key` — stable grouping key (error_class[:md5-of-message] or status:&lt;display_status>)\n- `label` — human-readable cause (normalized message, headline-cased error class, or display-status label)\n- `error_class` — retryable_transient | auth_connection | auto_remediable | user_action_required | terminal_internal | conflict, or null\n- `count` — total entries in the group\n- `entry_ids` — up to 25 sample entry IDs\n- `latest_error_payload` — the raw error payload of the group's latest operation, or null\n\n**Period scope:** pass `period=YYYY-MM` to restrict the feed to entries dated in that accounting month, so a per-month count and this feed describe the same set. Batch aggregate entries are excluded — an aggregate is a roll-up of its member entries and is managed through the batch it belongs to, not as a document row."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/entries/needs-attention","host":["{{baseUrl}}"],"path":["api","ledger","entries","needs-attention"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"groups\": [\n      {\n        \"cause_key\": \"user_action_required:d2b5c3a1e4f6a7b8c9d0e1f2a3b4c5d6\",\n        \"label\": \"Account # Sales Revenue is inactive in QuickBooks Online.\",\n        \"error_class\": \"user_action_required\",\n        \"count\": 14,\n        \"entry_ids\": [\n          1842,\n          1836,\n          1831,\n          1828,\n          1815,\n          1809,\n          1804,\n          1799,\n          1795,\n          1788,\n          1781,\n          1774,\n          1769,\n          1762\n        ],\n        \"latest_error_payload\": {\n          \"message\": \"Account 4000 Sales Revenue is inactive in QuickBooks Online.\",\n          \"provider_code\": \"6240\"\n        }\n      },\n      {\n        \"cause_key\": \"auth_connection\",\n        \"label\": \"Auth Connection\",\n        \"error_class\": \"auth_connection\",\n        \"count\": 3,\n        \"entry_ids\": [\n          1822,\n          1817,\n          1801\n        ],\n        \"latest_error_payload\": {\n          \"error\": \"Token refresh failed: invalid_grant\"\n        }\n      },\n      {\n        \"cause_key\": \"status:removed_in_provider\",\n        \"label\": \"Removed in provider\",\n        \"error_class\": null,\n        \"count\": 1,\n        \"entry_ids\": [\n          1755\n        ],\n        \"latest_error_payload\": null\n      }\n    ]\n  }\n}"}]},{"name":"List Stale Pre-Cutover Entries","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/stale-precutover","host":["{{baseUrl}}"],"path":["api","ledger","entries","stale-precutover"]},"description":"Imported pre-cutover journal entries whose SOURCE document was edited after the entry was imported. Automatic generation deliberately never rewrites frozen pre-cutover history, so these entries' figures no longer match their documents — this feed makes that drift visible so it can be fixed deliberately with Rebuild Journal Entry (single) or Bulk Entry Action with action `rebuild`.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Response fields:**\n- `count` — total stale entries across all source types\n- `capped` — true when `entries` holds only the first 200 rows. To rebuild everything in one call, use Bulk Entry Action with `action: \"rebuild\"` and `scope: \"all_stale\"` — it targets the full stale set, not just this preview.\n- `entries` — up to 200 rows, most-recently-drifted first. Each row carries the entry identity (`id`, `document_number`, `reference`, `type`, `display_status`), its amounts (`total_debit`, `total_credit`, `currency_code`), its accounting date (`effective_at`), the source document link (`source`), and the drift facts: `source_updated_at` (when the document last changed) vs `imported_at` (when the entry was imported).\n\nReturns `count: 0` with an empty list for tenants without an accounting cutover (no frozen history exists)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/entries/stale-precutover","host":["{{baseUrl}}"],"path":["api","ledger","entries","stale-precutover"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"count\": 3,\n    \"capped\": false,\n    \"entries\": [\n      {\n        \"id\": 8114,\n        \"reference\": \"BILL-1042\",\n        \"reference_suffix\": 0,\n        \"document_number\": \"BILL-8114\",\n        \"lineage_root_id\": 8114,\n        \"document_number_is_inherited\": false,\n        \"document_number_is_provider_assigned\": false,\n        \"type\": {\n          \"value\": \"bill\",\n          \"label\": \"Bill\"\n        },\n        \"display_status\": {\n          \"key\": \"not_synced\",\n          \"label\": \"Not Synced\",\n          \"color\": \"grey\"\n        },\n        \"currency_code\": \"USD\",\n        \"total_debit\": \"1250.0000\",\n        \"total_credit\": \"1250.0000\",\n        \"effective_at\": \"2025-11-14T00:00:00+00:00\",\n        \"source\": {\n          \"type\": \"App\\\\Models\\\\Bill\",\n          \"id\": 1042,\n          \"label\": \"BILL-1042\",\n          \"link_type_label\": \"Bill\",\n          \"link_route\": \"/purchases/bills/1042\",\n          \"link_legacy_url\": null,\n          \"url\": \"/purchases/bills/1042\"\n        },\n        \"source_updated_at\": \"2026-07-08T15:21:44+00:00\",\n        \"imported_at\": \"2026-06-11T03:12:09+00:00\"\n      }\n    ]\n  },\n  \"status\": \"success\"\n}"}]},{"name":"List Source Types","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/source-types","host":["{{baseUrl}}"],"path":["api","ledger","entries","source-types"]},"description":"Distinct source-document origins present on live (non-discarded, non-batch-aggregate) journal entries — powers the entries-list \"Source\" quick filter so it offers only origins the tenant actually has (e.g. \"Amazon Settlement\", \"Sales Order\", \"Bill\"). Lets users isolate settlement-derived bills/invoices that have no batch.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Usage:** each `value` is the backend morph class to pass back as `filter[source_type]=<value>` on `GET /api/ledger/entries`; `label` is the friendly name to display. Options are sorted by label.\n\n**No query parameters.**"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/entries/source-types","host":["{{baseUrl}}"],"path":["api","ledger","entries","source-types"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"source_types\": [\n      {\n        \"value\": \"Modules\\\\Amazon\\\\Entities\\\\AmazonFinancialEventGroup\",\n        \"label\": \"Amazon Settlement\"\n      },\n      {\n        \"value\": \"App\\\\Models\\\\Bill\",\n        \"label\": \"Bill\"\n      },\n      {\n        \"value\": \"App\\\\Models\\\\PurchaseOrder\",\n        \"label\": \"Purchase Order\"\n      },\n      {\n        \"value\": \"App\\\\Models\\\\SalesOrder\",\n        \"label\": \"Sales Order\"\n      },\n      {\n        \"value\": \"App\\\\Models\\\\SalesOrderFulfillment\",\n        \"label\": \"Sales Order Fulfillment\"\n      }\n    ]\n  }\n}"}]},{"name":"List Provider Statuses","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/provider-statuses","host":["{{baseUrl}}"],"path":["api","ledger","entries","provider-statuses"]},"description":"Distinct provider transaction statuses (sync link remote_status — Xero DRAFT/AUTHORISED/PAID/VOIDED, QBO Paid/Open, …) present for the tenant, as {value, label} options for the entries table's Provider Status filter (filter[provider_status]=...). Values are provider-native raw statuses; labels are Title Case.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRequires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/entries/provider-statuses","host":["{{baseUrl}}"],"path":["api","ledger","entries","provider-statuses"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"provider_statuses\": [\n      {\n        \"value\": \"AUTHORISED\",\n        \"label\": \"Authorised\"\n      },\n      {\n        \"value\": \"DRAFT\",\n        \"label\": \"Draft\"\n      },\n      {\n        \"value\": \"PAID\",\n        \"label\": \"Paid\"\n      },\n      {\n        \"value\": \"VOIDED\",\n        \"label\": \"Voided\"\n      }\n    ]\n  }\n}"}]},{"name":"Get Journal Entry","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"Journal entry detail: everything from the index row plus the detail-only sections —\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n- `lines` — the GL impact rows (direction, amount, quantity, tax_amount, nominal_code, tax_rate, source_line, role)\n- `invoice_view` — Xero-style invoice document view, present only for invoice-style entry types (sales_order_invoice, batch_sales_order_invoice, purchase_order_invoice, sales_credit, vendor_credit, bill) that resolve at least one product line; `null` for manual journals and quantity-free entries. One row per source line (`item`, `description`, `quantity`, `unit_price`, `discount_amount`, `account`, `tax_rate`, `tax_amount`, `amount`) with a `subtotal` / `discount_total` / `tax_total` / `total` / `currency_code` footer. Money + quantity fields are 4dp strings; `total` = `subtotal` + `tax_total`. Tax and quantity are read from the primary (revenue / COGS / expense) leg only, so they are never double-counted against the auto-booked control leg.\n\n- `sync_links` — provider links with connection identity, remote document type/ID, the provider deep link (`remote_url`, Xero go.xero.com / QBO web-app URL patterns), link_state (unlinked, linked_current, linked_stale, linked_conflict, remote_gone), remote_status and last_remote_seen_at\n- `operations` — the latest 20 sync operations (state, operation create|update|void|delete|link, direction push|pull, error_class, error_payload, attempts, connection) each with its append-only `transitions` audit trail\n- `reversal_chain` — summaries of the linked reversal_of / reversed_by / replaced_by / replaces entries\n\n**counterparty** — the supplier (AP) or customer (AR) the source document is against, as a cross-linkable reference: `{ name, id, link_type_label, link_route }`. Present on the detail path (and the Invoices / Purchase Orders list lenses) whenever the eager-loaded source resolves a party. Four shapes: (1) a linked CRM party — `name` + numeric `id` + `link_type_label` \"Supplier\"/\"Customer\" + `link_route` `/crm/suppliers/{id}` or `/crm/customers/{id}`; (2) a **marketplace-customer fallback** for AR sources (sales order / sales credit) with no CRM customer — `name` is \"{Integration} Customer\" (e.g. \"Amazon Customer\", \"Shopify Customer\"), `link_type_label` \"Customer\", with `id` and `link_route` `null` (no detail page to link to); (3) a **marketplace fallback** for a settlement source (Amazon financial-event group, whose sale + fee legs all net against the marketplace) — `name` is the bare integration name (e.g. \"Amazon\"), `link_type_label` \"Marketplace\", with `id` and `link_route` `null`; (4) a **provider fallback** for a ShipMyOrders provider invoice (a 3PL fulfilment-provider bill carrying no CRM party and no integration instance) — `name` is the fixed label \"SMO\", `link_type_label` \"Provider\", with `id` and `link_route` `null`; (5) the field is **absent entirely** only for sources that resolve none of the above (a manual journal with no source, a native SKU.io order missing its customer, a deleted source) — match it with `if (entry.counterparty)`.\n\n**Sync axes are null for historical entries:** the `sync`, `progress` and `routing` objects each report null key/label/color when an entry has left the live books — it is discarded (superseded by a regenerated replacement: `replaced_by_id` set + `discarded_at`) or a reversed original (`status = reversed`). Such rows can never sync (the batch rollup, reports and live lists all exclude them), so a frozen `enabled`/`synced` axis would mislead; the supersession marker carries the why instead. A live reversal contra entry (`reversal_of_id` set, still posted) is NOT historical and keeps its axes.\n\n**Authentication:** Requires Bearer token.\n\nReturns 404 when the entry does not exist (route is numeric-constrained).\n\nEach line also carries `role` — its economic role within a document-style entry (`primary` product leg, `control` A/R or A/P leg, `tax` leg, or `discount` leg), recovered from the mapped nominal code. The GL Impact table shows the invoiced quantity (and copied line description) on `primary` legs only: v2 copies the quantity onto the control leg too, so a role-blind column would repeat it on Accounts Payable / Receivable and read as a double-count. `role` is `null` on paths that don't load lines.\n\nEach line also carries `pushed_to_provider`: `false` marks the control line (A/R on an Invoice/Credit Note, A/P on a Bill) that the connected provider auto-books itself — SKU omits it from the push, so the GL Impact table dims it. `true` = the line is pushed. `null` when no accounting provider is connected. The entry's `sync_provider` ('Xero' / 'QuickBooks', or `null`) names that provider for the hint.\n\n**`can_sync_now`** (boolean) — whether a manual \"Sync Now\" can push this entry right now. The per-transaction-type Sync toggle, granularity opt-out and the per-type start date only gate *automatic/scheduled* sync; a manual push bypasses them, so `can_sync_now` stays `true` for a benign \"Auto-off\" entry. It is `false` only when a *structural* blocker applies (connection inactive, entry is a batch member / open batch, internal-only, or before the accounting provider's conversion/start date) — those block even a manual push. Drives the UI split between a grey \"Auto-off · push anytime\" chip (`display_status=excluded` + `can_sync_now=true`) and an amber \"Blocked\" chip (`display_status=excluded` + `can_sync_now=false`).\n\n**provider_payment** — for invoice-style entries (sales/purchase invoices, bills) linked to an accounting provider, the paid status + payment metadata: normalized `payment_state` (paid / partially_paid / unpaid / voided), the provider invoice lifecycle `provider_status` (Xero DRAFT/AUTHORISED/PAID/VOIDED), amounts (total/paid/due/credited), the `payments` applied, and provider-dependent `metadata`. Xero is fully populated; QBO is header-only and flagged `data_availability: limited`. `null` for non-invoice or unlinked entries.\n\n**`chain`** — the full reverse+replace lineage for the source document (every version, its reversal contras and the current head), oldest→newest, for a chain-of-events view. Each step: { id, document_number, reference, reference_suffix, status (draft|posted|reversed), role (original|reversal|replacement), is_current, is_viewed, reversal_of_id, replaced_by_id, effective_at, provider_link ({ remote_type, remote_id, remote_url, link_state, remote_status, badge } or null) }. `[]` when the entry has never been reversed or replaced.\n\nAlso returns `sync_override_provenance` (who/what/when set the sync override: `source` = `user` | `legacy_cutover` | `system`, `set_at`, `set_by`; null when no override) and `is_settlement_sourced` (boolean — a marketplace settlement posting, exempt from per-channel sync toggles).\n\n`provider_payment.unreflected_credit_notes` — credit notes the accounting provider has applied to this bill that are not yet reflected in SKU as vendor-credit allocations. Empty when everything reconciles. Each item carries `credit_note_id`, `credit_note_number`, `applied_amount` (applied at the provider), `reflected_amount` (covered by SKU allocations) and `shortfall` (the remaining unreflected amount).\n\n**Balance:** `line_totals` is the entry's own legs summed, and `is_out_of_balance` is true when those sums disagree with the stored `total_debit`/`total_credit`, or with each other. An out-of-balance entry is excluded from a clean month-end close until it is repaired."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1842,\n    \"uuid\": \"9c2f1b7e-4a6d-4f0e-9b1a-2d8c5e7f3a10\",\n    \"type\": {\n      \"value\": \"sales_order_invoice\",\n      \"label\": \"Sales Order Invoice\"\n    },\n    \"status\": \"posted\",\n    \"display_status\": {\n      \"key\": \"needs_attention\",\n      \"label\": \"Needs attention\",\n      \"color\": \"error\",\n      \"reason\": null\n    },\n    \"can_sync_now\": true,\n    \"locked_update_pending\": false,\n    \"locked_amend_blocked\": false,\n    \"sync\": {\n      \"key\": \"enabled\",\n      \"label\": \"Enabled\",\n      \"color\": \"success\",\n      \"reason\": null\n    },\n    \"progress\": {\n      \"key\": \"needs_attention\",\n      \"label\": \"Needs attention\",\n      \"color\": \"error\",\n      \"via_batch\": false\n    },\n    \"routing\": {\n      \"key\": \"individual\",\n      \"label\": \"Individual\",\n      \"color\": \"grey\",\n      \"batch_id\": null,\n      \"batch_route\": null\n    },\n    \"effective_at\": \"2026-06-05T14:30:00+00:00\",\n    \"posted_at\": \"2026-06-05T14:35:12+00:00\",\n    \"currency_code\": \"USD\",\n    \"total_debit\": \"249.9900\",\n    \"total_credit\": \"249.9900\",\n    \"document_total\": \"249.9900\",\n    \"document_number\": \"INV-1842\",\n    \"lineage_root_id\": 1842,\n    \"document_number_is_inherited\": false,\n    \"document_number_is_provider_assigned\": false,\n    \"reference\": \"SO-10293\",\n    \"reference_suffix\": null,\n    \"description\": \"Sales order invoice for SO-10293\",\n    \"source\": {\n      \"type\": \"App\\\\Models\\\\SalesOrder\",\n      \"id\": 10293,\n      \"label\": \"SO-10293\",\n      \"link_type_label\": \"Sales Order\",\n      \"link_route\": \"/orders/sales-orders/10293\",\n      \"link_legacy_url\": null,\n      \"url\": \"/orders/sales-orders/10293\"\n    },\n    \"counterparty\": {\n      \"name\": \"Acme Retail LLC\",\n      \"id\": 5821,\n      \"link_type_label\": \"Customer\",\n      \"link_route\": \"/crm/customers/5821\"\n    },\n    \"batch_id\": null,\n    \"sync_override\": null,\n    \"sync_override_provenance\": {\n      \"source\": \"user\",\n      \"set_at\": \"2026-07-16T14:00:00+00:00\",\n      \"set_by\": {\n        \"id\": 12,\n        \"name\": \"Julia T\"\n      }\n    },\n    \"is_settlement_sourced\": false,\n    \"sync_provider\": \"Xero\",\n    \"integrity_flag\": null,\n    \"reversal_of_id\": null,\n    \"replaced_by_id\": null,\n    \"created_at\": \"2026-06-05T14:30:02+00:00\",\n    \"invoice_view\": {\n      \"lines\": [\n        {\n          \"item\": \"WIDGET-BLU\",\n          \"description\": \"Blue Widget\",\n          \"quantity\": \"3.0000\",\n          \"unit_price\": \"76.5133\",\n          \"discount_amount\": null,\n          \"account\": {\n            \"nominal_code_id\": 40,\n            \"code\": \"4000\",\n            \"name\": \"Sales Revenue\",\n            \"link_route\": \"/ledger/accounts/40\"\n          },\n          \"tax_rate\": {\n            \"id\": 2,\n            \"name\": \"Tax on Sales (8.875%)\",\n            \"rate\": 8.875\n          },\n          \"tax_amount\": \"20.4500\",\n          \"amount\": \"229.5400\",\n          \"source_line\": {\n            \"type\": \"App\\\\Models\\\\SalesOrderLine\",\n            \"id\": 30412\n          }\n        }\n      ],\n      \"subtotal\": \"229.5400\",\n      \"discount_total\": \"0.0000\",\n      \"tax_total\": \"20.4500\",\n      \"total\": \"249.9900\",\n      \"currency_code\": \"USD\"\n    },\n    \"provider_payment\": {\n      \"provider\": \"xero\",\n      \"provider_label\": \"Xero\",\n      \"document_type\": \"invoice\",\n      \"provider_status\": \"PAID\",\n      \"provider_status_label\": \"Paid\",\n      \"provider_status_color\": \"success\",\n      \"payment_state\": \"paid\",\n      \"currency_code\": \"USD\",\n      \"total\": \"100.0000\",\n      \"amount_paid\": \"100.0000\",\n      \"amount_due\": \"0.0000\",\n      \"amount_credited\": \"0.0000\",\n      \"sub_total\": \"90.0000\",\n      \"tax_total\": \"10.0000\",\n      \"due_date\": \"2026-01-31T00:00:00+00:00\",\n      \"fully_paid_on\": \"2026-12-02T00:00:00+00:00\",\n      \"document_date\": \"2026-12-01T00:00:00+00:00\",\n      \"provider_url\": \"https://go.xero.com/AccountsReceivable/View.aspx?InvoiceID=6b1e...INV\",\n      \"payments\": [\n        {\n          \"date\": \"2026-12-02T00:00:00+00:00\",\n          \"amount\": \"100.0000\",\n          \"reference\": \"CHK-1042\",\n          \"type\": \"ACCRECPAYMENT\",\n          \"status\": \"AUTHORISED\",\n          \"is_reconciled\": true,\n          \"bank_account\": \"Business Bank Account\",\n          \"provider_payment_id\": \"b1c2d3e4-5678-90ab-cdef-1234567890ab\",\n          \"provider_url\": \"https://go.xero.com/Bank/ViewTransaction.aspx?bankTransactionID=b1c2d3e4-5678-90ab-cdef-1234567890ab\",\n          \"link_route\": \"/integrations/xero/payments/42\",\n          \"link_type_label\": \"Xero Payment\",\n          \"link_id\": 42\n        }\n      ],\n      \"metadata\": [\n        {\n          \"label\": \"Currency Rate\",\n          \"value\": \"1.000000\"\n        },\n        {\n          \"label\": \"Sent To Contact\",\n          \"value\": \"Yes\"\n        }\n      ],\n      \"data_availability\": \"full\",\n      \"availability_message\": null,\n      \"last_synced_at\": \"2026-12-02T04:15:00+00:00\",\n      \"unreflected_credit_notes\": []\n    },\n    \"lines\": [\n      {\n        \"id\": 5210,\n        \"direction\": \"debit\",\n        \"amount\": \"249.9900\",\n        \"quantity\": null,\n        \"tax_amount\": \"20.4500\",\n        \"description\": \"Accounts receivable\",\n        \"nominal_code\": {\n          \"id\": 12,\n          \"code\": \"1100\",\n          \"name\": \"Accounts Receivable\"\n        },\n        \"tax_rate\": null,\n        \"source_line\": null,\n        \"role\": \"control\",\n        \"pushed_to_provider\": false\n      },\n      {\n        \"id\": 5211,\n        \"direction\": \"credit\",\n        \"amount\": \"229.5400\",\n        \"quantity\": \"3.0000\",\n        \"tax_amount\": null,\n        \"description\": \"Product sales\",\n        \"nominal_code\": {\n          \"id\": 40,\n          \"code\": \"4000\",\n          \"name\": \"Sales Revenue\"\n        },\n        \"tax_rate\": {\n          \"id\": 2,\n          \"name\": \"Tax on Sales (8.875%)\"\n        },\n        \"source_line\": {\n          \"type\": \"App\\\\Models\\\\SalesOrderLine\",\n          \"id\": 30412\n        },\n        \"role\": \"primary\",\n        \"pushed_to_provider\": true\n      },\n      {\n        \"id\": 5212,\n        \"direction\": \"credit\",\n        \"amount\": \"20.4500\",\n        \"quantity\": null,\n        \"tax_amount\": null,\n        \"description\": \"Sales tax payable\",\n        \"nominal_code\": {\n          \"id\": 45,\n          \"code\": \"2200\",\n          \"name\": \"Sales Tax Payable\"\n        },\n        \"tax_rate\": {\n          \"id\": 2,\n          \"name\": \"Tax on Sales (8.875%)\"\n        },\n        \"source_line\": null,\n        \"role\": \"tax\",\n        \"pushed_to_provider\": true\n      }\n    ],\n    \"sync_links\": [\n      {\n        \"id\": 311,\n        \"provider\": \"qbo\",\n        \"connection\": {\n          \"id\": 3,\n          \"name\": \"QuickBooks Online\"\n        },\n        \"remote_type\": \"Invoice\",\n        \"remote_id\": \"4412\",\n        \"remote_url\": \"https://app.qbo.intuit.com/app/invoice?txnId=4412\",\n        \"link_state\": \"linked_stale\",\n        \"remote_status\": \"Open\",\n        \"last_remote_seen_at\": \"2026-06-08T03:12:00+00:00\"\n      }\n    ],\n    \"operations\": [\n      {\n        \"id\": 9907,\n        \"state\": \"needs_action\",\n        \"operation\": \"update\",\n        \"direction\": \"push\",\n        \"error_class\": \"user_action_required\",\n        \"retry_disposition\": \"needs_action\",\n        \"error_payload\": {\n          \"message\": \"Duplicate Document Number Error : You must specify a different number. This number has already been used.\",\n          \"user_title\": \"A document with this reference already exists in QuickBooks\",\n          \"user_message\": \"QuickBooks rejected this as a duplicate of a document that already exists. Review whether it is the same document before re-syncing.\",\n          \"provider_code\": \"6240\",\n          \"retry_disposition\": \"needs_action\"\n        },\n        \"attempts\": 1,\n        \"connection\": {\n          \"id\": 3,\n          \"name\": \"QuickBooks Online\"\n        },\n        \"created_at\": \"2026-06-08T03:10:00+00:00\",\n        \"started_at\": \"2026-06-08T03:10:05+00:00\",\n        \"finished_at\": \"2026-06-08T03:10:06+00:00\",\n        \"transitions\": [\n          {\n            \"from\": null,\n            \"to\": \"pending\",\n            \"reason\": \"enqueued\",\n            \"created_at\": \"2026-06-08T03:10:00+00:00\"\n          },\n          {\n            \"from\": \"pending\",\n            \"to\": \"in_flight\",\n            \"reason\": \"dispatched\",\n            \"created_at\": \"2026-06-08T03:10:05+00:00\"\n          },\n          {\n            \"from\": \"in_flight\",\n            \"to\": \"needs_action\",\n            \"reason\": \"provider_validation: Account 4000 Sales Revenue is inactive\",\n            \"created_at\": \"2026-06-08T03:10:06+00:00\"\n          }\n        ]\n      }\n    ],\n    \"reversal_chain\": {\n      \"reversal_of\": null,\n      \"reversed_by\": null,\n      \"replaced_by\": null,\n      \"replaces\": {\n        \"id\": 1790,\n        \"document_number\": \"JE-1790\",\n        \"reference\": \"SO-10293\",\n        \"status\": \"reversed\"\n      }\n    },\n    \"provider_link\": null,\n    \"chain\": [\n      {\n        \"id\": 1839,\n        \"document_number\": \"VC-1839\",\n        \"reference\": 1668,\n        \"reference_suffix\": 0,\n        \"status\": \"reversed\",\n        \"role\": \"original\",\n        \"is_current\": false,\n        \"is_viewed\": true,\n        \"reversal_of_id\": null,\n        \"replaced_by_id\": 1842,\n        \"effective_at\": \"2026-02-28T08:00:00+00:00\",\n        \"provider_link\": {\n          \"remote_type\": \"xero_credit_note\",\n          \"remote_id\": \"2643cbf7-ea33-460d-9d30-301fac99f609\",\n          \"remote_url\": \"https://go.xero.com/AccountsPayable/ViewCreditNote.aspx?creditNoteID=2643cbf7-ea33-460d-9d30-301fac99f609\",\n          \"link_state\": \"remote_gone\",\n          \"remote_status\": \"VOIDED\",\n          \"badge\": \"Voided\"\n        }\n      },\n      {\n        \"id\": 1841,\n        \"document_number\": \"VC-1841\",\n        \"reference\": 1668,\n        \"reference_suffix\": 0,\n        \"status\": \"posted\",\n        \"role\": \"reversal\",\n        \"is_current\": false,\n        \"is_viewed\": false,\n        \"reversal_of_id\": 1839,\n        \"replaced_by_id\": null,\n        \"effective_at\": \"2026-02-28T08:00:00+00:00\",\n        \"provider_link\": null\n      },\n      {\n        \"id\": 1842,\n        \"document_number\": \"VC-1842\",\n        \"reference\": \"0001668/R1\",\n        \"reference_suffix\": 1,\n        \"status\": \"draft\",\n        \"role\": \"replacement\",\n        \"is_current\": true,\n        \"is_viewed\": false,\n        \"reversal_of_id\": null,\n        \"replaced_by_id\": null,\n        \"effective_at\": \"2026-02-28T08:00:00+00:00\",\n        \"provider_link\": null\n      }\n    ],\n    \"line_totals\": {\n      \"debit\": \"480.0000\",\n      \"credit\": \"480.0000\"\n    },\n    \"is_out_of_balance\": true\n  }\n}"}]},{"name":"Get Journal Entry Lines","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/lines","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","lines"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"The entry's GL-impact journal lines — a lightweight cousin of **Get Journal Entry** that returns only the `lines` array (no sync_links, operations or reversal_chain). Drives the entries table's inline \"Lines\" row expansion.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nLines are ordered GL-impact-first: debits before credits, each ordered by nominal code.\n\nEach line includes:\n- `direction` — `debit` or `credit`\n- `amount` — the extended (line total) amount as a string\n- `quantity`, `tax_amount` — strings, or null\n- `nominal_code` — `{ id, code, name }` of the account the line posts to\n- `is_overridden` — true when a durable override remapped this line's account\n- `nominal_code_original` — the account it WOULD have derived to (non-null only when overridden), drives the `<derived> → <override>` display\n- `tax_rate` — `{ id, name }` or null\n- `source_line` — `{ type, id }` of the originating document line, or null\n- `role` — economic role within a document-style entry: `primary` (product leg), `control` (A/R or A/P leg), `tax`, or `discount`, recovered from the mapped nominal code. The GL Impact table shows the invoiced quantity on `primary` legs only (v2 copies the quantity onto the control leg, so a role-blind column would double-count it); `null` on paths that don't load lines\n- `pushed_to_provider` — `false` marks the control line (A/R on an Invoice/Credit Note, A/P on a Bill) that the connected provider auto-books itself, so SKU omits it from the push and the GL Impact table dims it; `true` = pushed; `null` when no accounting provider is connected\n\n**Authentication:** Requires Bearer token.\n\nReturns 404 when the entry does not exist (route is numeric-constrained)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/lines","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","lines"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5501,\n      \"direction\": \"debit\",\n      \"amount\": \"1200.00\",\n      \"quantity\": \"10\",\n      \"tax_amount\": \"0.00\",\n      \"description\": \"Inventory received — Widget A\",\n      \"nominal_code\": {\n        \"id\": 31,\n        \"code\": \"500\",\n        \"name\": \"Cost of Goods Sold\"\n      },\n      \"is_overridden\": false,\n      \"nominal_code_original\": null,\n      \"tax_rate\": {\n        \"id\": 4,\n        \"name\": \"No VAT\"\n      },\n      \"source_line\": {\n        \"type\": \"App\\\\Models\\\\PurchaseInvoiceLine\",\n        \"id\": 8842\n      },\n      \"role\": \"primary\",\n      \"pushed_to_provider\": true\n    },\n    {\n      \"id\": 5502,\n      \"direction\": \"credit\",\n      \"amount\": \"1200.00\",\n      \"quantity\": null,\n      \"tax_amount\": \"0.00\",\n      \"description\": \"Accounts Payable — Acme Supplies\",\n      \"nominal_code\": {\n        \"id\": 80,\n        \"code\": \"800\",\n        \"name\": \"Accounts Payable\"\n      },\n      \"is_overridden\": false,\n      \"nominal_code_original\": null,\n      \"tax_rate\": null,\n      \"source_line\": null,\n      \"role\": \"control\",\n      \"pushed_to_provider\": false\n    }\n  ]\n}"}]},{"name":"Get Journal Entry Attachment Sync","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/attachment-sync","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","attachment-sync"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"The sync state of each source-document file attachment across every connection. When the entry's source document (purchase invoice, vendor credit, or vendor deposit) has attachments, this returns one row per (attachment × connection) describing whether the file was uploaded to the matching Xero/QBO document.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nReturns an empty `data` array when the source has no attachments, no attachment concept, or nothing has been attempted yet.\n\nEach row includes:\n- `source_attachment_id` — the SKU attachment row id\n- `file_name` — the source file's display name\n- `connection_id`, `provider` — the accounting connection and its integration name (e.g. `Xero`, `QuickBooks Online`)\n- `remote_type`, `remote_id` — the provider document the file is attached to (e.g. `qbo_vendor_credit`)\n- `remote_attachment_id` — the Xero AttachmentID / QBO Attachable.Id (null while failed/pending)\n- `status` — `synced`, `failed`, or `pending`\n- `last_error` — failure reason (null on success)\n- `synced_at` — ISO-8601 UTC timestamp of the last successful upload (null otherwise)\n\nAuthentication: requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/attachment-sync","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","attachment-sync"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5012,\n      \"source_attachment_id\": 8841,\n      \"file_name\": \"supplier-invoice.pdf\",\n      \"connection_id\": 12,\n      \"provider\": \"Xero\",\n      \"remote_type\": \"xero_bill\",\n      \"remote_id\": \"b1f0e2c4-6fce-4330-bb3b-8306278c6fd8\",\n      \"remote_attachment_id\": \"a77c1e90-2b1f-4d0a-9d1e-9a2b3c4d5e6f\",\n      \"status\": \"synced\",\n      \"last_error\": null,\n      \"synced_at\": \"2026-07-02T14:12:05+00:00\"\n    },\n    {\n      \"id\": 5013,\n      \"source_attachment_id\": 8842,\n      \"file_name\": \"delivery-note.pdf\",\n      \"connection_id\": 12,\n      \"provider\": \"Xero\",\n      \"remote_type\": \"xero_bill\",\n      \"remote_id\": \"b1f0e2c4-6fce-4330-bb3b-8306278c6fd8\",\n      \"remote_attachment_id\": null,\n      \"status\": \"failed\",\n      \"last_error\": \"Skipped: file exceeds the provider attachment size limit of 25MB.\",\n      \"synced_at\": null\n    }\n  ]\n}"}]},{"name":"Rebuild Journal Entry","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/rebuild","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","rebuild"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"Rebuild the entry's SOURCE document INLINE (single-entry action): the user clicked Rebuild on the entry detail page and expects the regenerated entry to be current the instant the request returns, not after the async drain catches up. The rebuild writes the same `updated` outbox row a real source mutation rides, then processes JUST that row synchronously through the shared outbox processor (build → persistSet → batch-assign → sync-select) — so posted-immutability (reversal + replacement) holds exactly as on the async path.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nTwo cases fall back to the queue instead of running inline (the message says which):\n- **Batch-aggregate entries** (source = a LedgerBatch) ride the batch state machine (markDirty + a background job) — identical effect to POST /api/ledger/batches/{batch}/rebuild.\n- **Generation disabled** (the `ledger.generation_enabled` kill-switch is off): the `updated` row is parked for whenever the drain next runs.\n\n**Authentication:** Requires Bearer token.\n\n**Request body:** none.\n\n**Response:** 200 with a `message`. When the rebuild ran inline the message is \"Entry rebuilt from its source document.\"; on the queued fallback it is \"Rebuild queued — the entry will regenerate from its source document shortly.\" (App\\Response normalizes every 2xx to 200.) Returns 404 when the entry does not exist.\n\nPermission: requires `accounting.sync` (granular permissions; admins bypass; unenforced when the tenant's granular_permissions feature flag is off). Returns 403 without it."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/rebuild","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","rebuild"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [],\n  \"message\": \"Entry rebuilt from its source document.\"\n}"}]},{"name":"Refresh Journal Entry","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/refresh","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","refresh"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"Refresh this entry's amounts from its SOURCE document IN PLACE — the light counterpart to Rebuild. Where Rebuild corrects a posted entry by writing a reversal + a replacement, Refresh re-derives the entry's lines and totals and writes them onto the SAME entry: its id, status and accounting-provider link are all kept, so a posted, already-synced entry whose local figure drifted is corrected WITHOUT creating a new entry and WITHOUT touching the linked Xero / QuickBooks document. After the rewrite the sync state is re-graded — a still-editable remote is offered an update, while a settled/locked or voided remote is left as-is (nothing is pushed).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Request body:** none.\n\n**Response:** 200 with `data.refresh` ({ refreshed, reason, period_locked }) and `data.entry` (the refreshed entry object). `refreshed` is false with a `reason` when there is nothing to do: `already_current` (the entry already matches its source), `batched` (the entry rolls into a batch aggregate — rebuild the batch instead), `generation_disabled`, `source_no_longer_produces_entry` (the source stopped producing this entry — use Rebuild to reverse it), or `period_locked` (the entry, or the date its source now derives, falls in a locked accounting period; a locked period is read-only, so move the lock date back to refresh it). `period_locked` is true exactly when the refresh was refused for that reason. `message` summarises the outcome.\n\nPermission: requires `accounting.sync` (granular permissions; admins bypass; unenforced when the tenant's granular_permissions feature flag is off). Returns 403 without it; 404 when the entry does not exist."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/refresh","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","refresh"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"refresh\": {\n      \"refreshed\": true,\n      \"reason\": null,\n      \"period_locked\": false\n    },\n    \"entry\": {\n      \"id\": 1842,\n      \"uuid\": \"9c2f1b7e-4a6d-4f0e-9b1a-2d8c5e7f3a10\",\n      \"type\": {\n        \"value\": \"purchase_order_invoice\",\n        \"label\": \"Purchase Order Invoice\"\n      },\n      \"status\": \"posted\",\n      \"display_status\": {\n        \"key\": \"locked\",\n        \"label\": \"Synced, Locked\",\n        \"color\": \"info\",\n        \"reason\": null\n      },\n      \"can_sync_now\": false,\n      \"effective_at\": \"2026-06-05T14:30:00+00:00\",\n      \"posted_at\": \"2026-06-05T14:35:12+00:00\",\n      \"currency_code\": \"USD\",\n      \"total_debit\": \"4631.0400\",\n      \"total_credit\": \"4631.0400\",\n      \"reference\": \"BILL-10293\",\n      \"sync_links\": [\n        {\n          \"id\": 55,\n          \"provider\": \"xero\",\n          \"remote_type\": \"xero_bill\",\n          \"remote_id\": \"54bccb09-ffa1-4309-8caf-4f78a09b493a\",\n          \"link_state\": \"remote_locked\",\n          \"remote_status\": \"PAID\"\n        }\n      ],\n      \"operations\": []\n    }\n  },\n  \"message\": \"Refreshed from its source document — the provider link was kept.\"\n}"}]},{"name":"Resync Entry to Provider","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/sync","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","sync"],"variable":[{"key":"entry","value":"8114","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"Push this SINGLE entry to the connected accounting provider SYNCHRONOUSLY (InlineLedgerSyncRunner): the provider round-trip runs inline within the request, so the response already reflects the real Synced / Failed outcome — there is no interim \"queued\" state to poll. This powers the entry detail page's \"Resync Now\" button and its live \"talking to the provider\" animation. The bulk equivalent — `POST /api/ledger/entries/bulk` with action `sync_now` — stays an async tracked job.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Request body:** none.\n\n**Locked documents:** when the provider document is paid/settled (locked), amounts and dates cannot be pushed. Resync instead pushes only the fields the provider still allows editing on a locked document (the reference/number) and reports `amended: true`. The entry keeps its \"Synced, Locked\" status; if the reference change covered all the local drift, the pending-update flag clears. To push amount or date changes, remove the payment or credit note on the provider document first, then Resync. **Fully-locked documents:** some provider document types (for example a credit note with a payment or allocation applied) are locked so hard that even the reference cannot be pushed. For those, Resync mints no operation and reports `locked_blocked: true`, and the entry carries a `locked_amend_blocked` flag; nothing changes on the provider until the payment or allocation is removed there.\n\n**Behaviour:** mints the entry's manual push op (manual eligibility bypasses the soft per-type Sync toggle but NOT structural blockers — connection inactive, batch member / open batch, internal-only, or before the provider start date) and drives it to the provider in-process. A no-op (still reports `synced` when already linked-current) when the entry's content hash matches the last sync, or `eligible: false` when nothing can be pushed.\n\n**First-push regeneration:** when the entry is imported pre-cutover history that has NEVER been synced (no link on any connection), it is first rebuilt with the current posting rules so the provider's first copy reflects current logic rather than the migrated import. The rebuild may replace the entry with a successor — `data.sync.entry_id` is the entry the push actually ran against and `data.entry` is that (possibly new) entry.\n\n**Response:** 200 with `data.sync` ({ eligible, synced, failed, amended, locked_blocked, error, regenerated, entry_id }) and `data.entry` (the refreshed ledger entry object, which includes a `locked_amend_blocked` flag). `error` carries the provider rejection message when `failed` is true. `locked_blocked` is true when the provider document is fully locked and not even the reference could be pushed. `message` summarises the outcome.\n\nPermission: requires `accounting.sync` (granular permissions; admins bypass; unenforced when the tenant's granular_permissions feature flag is off). Returns 403 without it; 404 when the entry does not exist."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/sync","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","sync"],"variable":[{"key":"entry","value":"8114","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"sync\": {\n      \"eligible\": true,\n      \"synced\": true,\n      \"failed\": false,\n      \"amended\": false,\n      \"error\": null,\n      \"regenerated\": false,\n      \"entry_id\": 8114\n    },\n    \"entry\": {\n      \"id\": 8114,\n      \"type\": {\n        \"value\": \"bill\",\n        \"label\": \"Bill\"\n      },\n      \"status\": \"posted\",\n      \"display_status\": {\n        \"key\": \"synced\",\n        \"label\": \"Synced\",\n        \"color\": \"success\",\n        \"reason\": null\n      },\n      \"can_sync_now\": true,\n      \"locked_update_pending\": false,\n      \"locked_amend_blocked\": false,\n      \"counterparty_name\": \"Amazon\",\n      \"currency_code\": \"USD\",\n      \"total_debit\": \"842.6700\",\n      \"total_credit\": \"842.6700\",\n      \"document_number\": \"BILL-8114\",\n      \"lineage_root_id\": 8114,\n      \"document_number_is_inherited\": false,\n      \"document_number_is_provider_assigned\": false,\n      \"reference\": \"5cKTkaXNDpXqmNv0MzaOHAMppp6CsZOYXrgMbDv4UDg\",\n      \"sync_provider\": \"Xero\",\n      \"sync_links\": [\n        {\n          \"id\": 5521,\n          \"provider\": \"Xero\",\n          \"remote_type\": \"bill\",\n          \"remote_id\": \"57406763-6b49-4be8-98b5-16750c106f6e\",\n          \"link_state\": \"linked_current\",\n          \"remote_status\": \"AUTHORISED\",\n          \"last_remote_seen_at\": \"2026-06-30T01:41:05+00:00\"\n        }\n      ],\n      \"operations\": [\n        {\n          \"id\": 90211,\n          \"state\": \"succeeded\",\n          \"operation\": \"create\",\n          \"direction\": \"push\",\n          \"error_class\": null,\n          \"error_payload\": null,\n          \"attempts\": 1,\n          \"finished_at\": \"2026-06-30T01:41:05+00:00\"\n        }\n      ]\n    }\n  },\n  \"message\": \"Pushed to the provider — this entry is up to date.\"\n}"}]},{"name":"Preview Bill Settlement With Vendor Credits","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/settlement-preview","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","settlement-preview"],"variable":[{"key":"entry","value":"9609","description":"Ledger journal entry ID (numeric) — must be a bill (purchase invoice) entry. Route-model bound — returns 404 if the entry does not exist."}]},"description":"Preview the plan to settle a bill (a purchase-invoice entry) using the vendor credits already allocated against it — the read side of the \"sync & mark paid\" flow. Reports whether the entry is a settleable bill, whether the bill itself is synced to the accounting provider, and the state of each applied vendor credit: `missing_transaction` (its accounting transaction has not been generated yet), `unsynced` (generated but not pushed to the provider), or `synced` (its credit note is pushed and its allocation applied). Use it to show exactly what will happen before committing.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Request body:** none.\n\n**Response:** 200 with `data.settlement`: `is_settleable_bill`, `bill_synced`, `already_settled`, `needs_action`, `credit_count`, `allocated_total`, `outstanding_balance`, `currency_code`, and `credits[]` (each carries `vendor_credit_id`, `allocation_id`, `vendor_credit_number`, `vendor_credit_route`, `allocation_amount`, `state`, `ledger_entry_id`, `ledger_entry_route`). When the entry is not a bill with allocated vendor credits, `is_settleable_bill` is false and `credits` is empty. Returns 404 when the entry does not exist."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/settlement-preview","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","settlement-preview"],"variable":[{"key":"entry","value":"9609","description":"Ledger journal entry ID (numeric) — must be a bill (purchase invoice) entry. Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"settlement\": {\n      \"is_settleable_bill\": true,\n      \"bill_synced\": true,\n      \"already_settled\": false,\n      \"needs_action\": true,\n      \"credit_count\": 4,\n      \"allocated_total\": \"2084.52\",\n      \"outstanding_balance\": \"2084.52\",\n      \"currency_code\": \"USD\",\n      \"credits\": [\n        {\n          \"vendor_credit_id\": 312,\n          \"allocation_id\": 907,\n          \"vendor_credit_number\": \"VC-000312\",\n          \"vendor_credit_route\": \"/v2/vendor-credits/312\",\n          \"allocation_amount\": \"500.00\",\n          \"state\": \"unsynced\",\n          \"ledger_entry_id\": 9720,\n          \"ledger_entry_route\": \"/ledger/entries/9720\"\n        },\n        {\n          \"vendor_credit_id\": 313,\n          \"allocation_id\": 908,\n          \"vendor_credit_number\": \"VC-000313\",\n          \"vendor_credit_route\": \"/v2/vendor-credits/313\",\n          \"allocation_amount\": \"600.00\",\n          \"state\": \"missing_transaction\",\n          \"ledger_entry_id\": null,\n          \"ledger_entry_route\": null\n        },\n        {\n          \"vendor_credit_id\": 314,\n          \"allocation_id\": 909,\n          \"vendor_credit_number\": \"VC-000314\",\n          \"vendor_credit_route\": \"/v2/vendor-credits/314\",\n          \"allocation_amount\": \"484.52\",\n          \"state\": \"unsynced\",\n          \"ledger_entry_id\": 9721,\n          \"ledger_entry_route\": \"/ledger/entries/9721\"\n        },\n        {\n          \"vendor_credit_id\": 315,\n          \"allocation_id\": 910,\n          \"vendor_credit_number\": \"VC-000315\",\n          \"vendor_credit_route\": \"/v2/vendor-credits/315\",\n          \"allocation_amount\": \"500.00\",\n          \"state\": \"synced\",\n          \"ledger_entry_id\": 9722,\n          \"ledger_entry_route\": \"/ledger/entries/9722\"\n        }\n      ]\n    }\n  },\n  \"message\": \"This bill has 4 vendor credits allocated — 3 still need to be applied on the provider to mark it paid.\"\n}"}]},{"name":"Settle Bill With Vendor Credits","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/sync-and-settle","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","sync-and-settle"],"variable":[{"key":"entry","value":"9609","description":"Ledger journal entry ID (numeric) — must be a bill (purchase invoice) entry. Route-model bound — returns 404 if the entry does not exist."}]},"description":"Settle a bill (a purchase-invoice entry) using the vendor credits allocated against it so the connected accounting provider marks the bill PAID — the missing half of two-way sync when the allocations exist here but were never pushed. Dispatches a tracked background job that (1) syncs the bill so its remote document exists, (2) for each applied vendor credit generates its accounting transactions if missing, syncs its credit note, then pushes the allocation (Xero credit-note allocation / QuickBooks zero-dollar bill payment), and (3) re-reads the bill so its link re-grades to Synced, Locked (paid). Idempotent — re-running skips work already done, so a partial failure is safe to retry.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.sync` permission (403 without it).\n\n**Request body:** none.\n\n**Response:** 200 with `data.tracked_job_log_id` (poll for progress) and `data.settlement` (the pre-run snapshot, same shape as Preview Bill Settlement). When the bill is already settled by its credits, returns 200 with just `data.settlement` and no job. Returns 422 when the entry is not a bill with vendor credits allocated. Returns 404 when the entry does not exist."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/sync-and-settle","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","sync-and-settle"],"variable":[{"key":"entry","value":"9609","description":"Ledger journal entry ID (numeric) — must be a bill (purchase invoice) entry. Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 44821,\n    \"settlement\": {\n      \"is_settleable_bill\": true,\n      \"bill_synced\": true,\n      \"already_settled\": false,\n      \"needs_action\": true,\n      \"credit_count\": 4,\n      \"allocated_total\": \"2084.52\",\n      \"outstanding_balance\": \"2084.52\",\n      \"currency_code\": \"USD\",\n      \"credits\": [\n        {\n          \"vendor_credit_id\": 312,\n          \"allocation_id\": 907,\n          \"vendor_credit_number\": \"VC-000312\",\n          \"vendor_credit_route\": \"/v2/vendor-credits/312\",\n          \"allocation_amount\": \"500.00\",\n          \"state\": \"unsynced\",\n          \"ledger_entry_id\": 9720,\n          \"ledger_entry_route\": \"/ledger/entries/9720\"\n        },\n        {\n          \"vendor_credit_id\": 313,\n          \"allocation_id\": 908,\n          \"vendor_credit_number\": \"VC-000313\",\n          \"vendor_credit_route\": \"/v2/vendor-credits/313\",\n          \"allocation_amount\": \"600.00\",\n          \"state\": \"missing_transaction\",\n          \"ledger_entry_id\": null,\n          \"ledger_entry_route\": null\n        },\n        {\n          \"vendor_credit_id\": 314,\n          \"allocation_id\": 909,\n          \"vendor_credit_number\": \"VC-000314\",\n          \"vendor_credit_route\": \"/v2/vendor-credits/314\",\n          \"allocation_amount\": \"484.52\",\n          \"state\": \"unsynced\",\n          \"ledger_entry_id\": 9721,\n          \"ledger_entry_route\": \"/ledger/entries/9721\"\n        },\n        {\n          \"vendor_credit_id\": 315,\n          \"allocation_id\": 910,\n          \"vendor_credit_number\": \"VC-000315\",\n          \"vendor_credit_route\": \"/v2/vendor-credits/315\",\n          \"allocation_amount\": \"500.00\",\n          \"state\": \"synced\",\n          \"ledger_entry_id\": 9722,\n          \"ledger_entry_route\": \"/ledger/entries/9722\"\n        }\n      ]\n    }\n  },\n  \"message\": \"Settling the bill with its vendor credits — track progress in the job tracker.\"\n}"}]},{"name":"Clear Sync Errors","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/clear-errors","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","clear-errors"],"variable":[{"key":"entry","value":"8114","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"Dismiss THIS single entry's parked sync errors (needs_action / failed_retryable) SYNCHRONOUSLY by superseding its open sync operations across every connection, then return the refreshed entry so the detail page drops the error banner without a page refresh. This powers the entry detail page's \"Clear errors\" button.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nIt clears the error WITHOUT auto-retrying — the user runs Resync Now afterwards to re-attempt the push. The bulk equivalent — `POST /api/ledger/entries/bulk` with action `clear_errors` — stays an async tracked job for multi-entry selections.\n\n**Authentication:** Requires Bearer token.\n\n**Request body:** none.\n\n**Behaviour:** transitions the entry's open operations (except InFlight) to `Superseded` per connection, then recomputes and refreshes the entry's display status. A no-op (`cleared: false`) when the entry has no open sync errors.\n\n**Response:** 200 with `data.cleared` (bool — whether any open ops were superseded) and `data.entry` (the refreshed ledger entry object, whose `display_status` no longer reports needs_attention). `message` summarises the outcome.\n\nPermission: requires `accounting.sync` (granular permissions; admins bypass; unenforced when the tenant's granular_permissions feature flag is off). Returns 403 without it; 404 when the entry does not exist."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/clear-errors","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","clear-errors"],"variable":[{"key":"entry","value":"8114","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"cleared\": true,\n    \"entry\": {\n      \"id\": 8114,\n      \"type\": {\n        \"value\": \"bill\",\n        \"label\": \"Bill\"\n      },\n      \"status\": \"posted\",\n      \"display_status\": {\n        \"key\": \"not_synced\",\n        \"label\": \"Not Synced\",\n        \"color\": \"grey\",\n        \"reason\": null\n      },\n      \"can_sync_now\": true,\n      \"locked_update_pending\": false,\n      \"locked_amend_blocked\": false,\n      \"counterparty_name\": \"Amazon\",\n      \"currency_code\": \"USD\",\n      \"total_debit\": \"842.6700\",\n      \"total_credit\": \"842.6700\",\n      \"document_number\": \"BILL-8114\",\n      \"lineage_root_id\": 8114,\n      \"document_number_is_inherited\": false,\n      \"document_number_is_provider_assigned\": false,\n      \"reference\": \"5cKTkaXNDpXqmNv0MzaOHAMppp6CsZOYXrgMbDv4UDg\",\n      \"sync_provider\": \"Xero\",\n      \"operations\": [\n        {\n          \"id\": 90244,\n          \"state\": \"superseded\",\n          \"operation\": \"create\",\n          \"direction\": \"push\",\n          \"error_class\": null,\n          \"error_payload\": null,\n          \"attempts\": 2,\n          \"finished_at\": \"2026-06-30T01:41:05+00:00\"\n        }\n      ]\n    }\n  },\n  \"message\": \"Sync errors cleared — run Sync now to re-attempt.\"\n}"}]},{"name":"Recreate Accounting Transaction","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/recreate","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","recreate"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"\"Recreate document\" for an entry whose remote document was VOIDED or DELETED in the accounting provider (display_status `voided` — \"Synced, Voided\" — or `removed_in_provider` — \"Synced, Deleted\"). Once a provider document is voided/deleted there is nothing left to update on it, so this reverse+replaces the entry into a fresh UNLINKED draft replacement and mints it from the same source, even though the source itself never changed.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nThe dead provider link is RETAINED on the reversed original as the historical record; the replacement starts with NO provider link and is chained via replaced_by_id, so the detail view shows a \"Replaces …\" link. This action does NOT push and does NOT touch the dead document — the user syncs the fresh draft to push a brand-new document on demand.\n\n**Authentication:** Requires Bearer token.\n\n**Request body:** none.\n\n**Response:** 200 with `data.recreate` ({ recreated, status, reason, replacement_id }) and `data.entry` (the now-reversed original). `replacement_id` is the id of the fresh unlinked draft. `recreated` is false with reason `not_voided` (nothing voided to replace), `generation_disabled`, or `source_not_accounting_ready` (the source no longer produces an entry). Returns 404 when the entry does not exist.\n\nPermission: requires `accounting.sync`."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/recreate","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","recreate"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"recreate\": {\n      \"recreated\": true,\n      \"status\": \"voided\",\n      \"reason\": null,\n      \"replacement_id\": 1843\n    },\n    \"entry\": {\n      \"id\": 1842,\n      \"uuid\": \"9c2f1b7e-4a6d-4f0e-9b1a-2d8c5e7f3a10\",\n      \"type\": {\n        \"value\": \"vendor_credit\",\n        \"label\": \"Vendor Credit\"\n      },\n      \"status\": \"reversed\",\n      \"display_status\": {\n        \"key\": \"voided\",\n        \"label\": \"Synced, Voided\",\n        \"color\": \"warning\",\n        \"reason\": null\n      },\n      \"can_sync_now\": false,\n      \"locked_update_pending\": false,\n      \"locked_amend_blocked\": false,\n      \"effective_at\": \"2026-02-28T08:00:00+00:00\",\n      \"posted_at\": \"2026-02-28T08:05:00+00:00\",\n      \"currency_code\": \"USD\",\n      \"total_debit\": \"600.0000\",\n      \"total_credit\": \"600.0000\",\n      \"document_number\": \"VC-11036\",\n      \"lineage_root_id\": 1842,\n      \"document_number_is_inherited\": false,\n      \"document_number_is_provider_assigned\": false,\n      \"reference\": \"0001668/R1\",\n      \"replaced_by_id\": 1843,\n      \"sync_links\": [\n        {\n          \"id\": 819,\n          \"provider\": \"xero\",\n          \"connection\": {\n            \"id\": 30,\n            \"name\": \"Xero\"\n          },\n          \"remote_type\": \"xero_credit_note\",\n          \"remote_id\": \"2643cbf7-ea33-460d-9d30-301fac99f609\",\n          \"remote_url\": \"https://go.xero.com/AccountsPayable/ViewCreditNote.aspx?creditNoteID=2643cbf7-ea33-460d-9d30-301fac99f609\",\n          \"link_state\": \"remote_gone\",\n          \"remote_status\": \"VOIDED\",\n          \"last_remote_seen_at\": \"2026-07-13T22:40:00+00:00\"\n        }\n      ],\n      \"operations\": []\n    }\n  },\n  \"message\": \"Created a fresh draft that replaces this entry — the voided document stays on the original for the record. Sync the new draft when you are ready to push a brand-new document.\"\n}"}]},{"name":"List Relink Candidates","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/relink-candidates","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","relink-candidates"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"Provider documents this voided or deleted entry could be linked to INSTEAD of recreating one — the confidence-ranked \"link to an existing document\" picker. Use this when the accounting document was voided in the provider and a replacement already exists there (e.g. a bill was voided and re-raised): rather than minting a duplicate, adopt the document that is already in the provider.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nReads the provider-document cache for the connection of the entry's voided link and returns the unlinked documents of the same kind, ranked best-first. Each candidate carries per-signal match flags — `amount_match`, `date_match`, `reference_match`, and `confident` (amount AND date within tolerance) — so the top result is usually the correct replacement. Documents already linked to another entry, or themselves voided/deleted, are excluded. Documents dismissed on the reconcile screen are still offered — flagged `dismissed: true` and ranked below equally-confident undismissed candidates; linking one clears its dismissal. Returns an empty list when the entry has no voided/deleted link to recover.\n\n**Authentication:** Requires Bearer token.\n\n**Query parameters:** `search` (optional) — free-text narrowing.\n\n**Response:** 200 with `data` as an array of candidate documents ordered most-confident first."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/relink-candidates","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","relink-candidates"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"connection_id\": 30,\n      \"remote_type\": \"xero_bill\",\n      \"remote_id\": \"54f0b7b3-9537-447a-bda2-897f7da26e9f\",\n      \"reference\": \"PO-0327: INVCBUSA-2000001.1\",\n      \"total\": \"49063.4000\",\n      \"currency\": \"USD\",\n      \"document_date\": \"2025-09-22\",\n      \"status\": \"AUTHORISED\",\n      \"amount_match\": true,\n      \"date_match\": true,\n      \"reference_match\": true,\n      \"confident\": true,\n      \"dismissed\": false\n    },\n    {\n      \"connection_id\": 30,\n      \"remote_type\": \"xero_bill\",\n      \"remote_id\": \"11c2a7d0-882e-4a19-9f6b-73c0d9a1e2ff\",\n      \"reference\": \"PO-0299\",\n      \"total\": \"1204.0000\",\n      \"currency\": \"USD\",\n      \"document_date\": \"2025-09-03\",\n      \"status\": \"AUTHORISED\",\n      \"amount_match\": false,\n      \"date_match\": false,\n      \"reference_match\": false,\n      \"confident\": false,\n      \"dismissed\": true\n    }\n  ]\n}"}]},{"name":"List Document Link Candidates","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/link-candidates","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","link-candidates"],"variable":[{"key":"entry","value":"10927","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"Provider documents an unmatched ledger entry could be linked to for the first time — the confidence-ranked picker for pointing a never-linked SKU entry at the document it already corresponds to in the provider (e.g. a bill that was imported into the provider some other way), instead of syncing a duplicate. This is the sibling of List Relink Candidates, which recovers a voided/deleted link; this one is for an entry that has never been linked.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nReads the provider-document cache for the tenant's accounting connection and returns the unlinked documents ranked best-first. Each candidate carries per-signal match flags — `amount_match`, `date_match`, `reference_match`, and `confident` (amount AND date within tolerance). Documents already linked to another entry are excluded. Documents dismissed on the reconcile screen are still offered — flagged `dismissed: true` and ranked below equally-confident undismissed candidates; linking one clears its dismissal. Returns an empty list when the entry is already linked. Link the chosen document with POST /api/ledger/reconciliation/link.\n\n**Authentication:** Requires Bearer token.\n\n**Query parameters:** `search` (optional) — free-text narrowing.\n\n**Response:** 200 with `data` as an array of candidate documents ordered most-confident first."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/link-candidates","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","link-candidates"],"variable":[{"key":"entry","value":"10927","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"connection_id\": 30,\n      \"remote_type\": \"xero_bill\",\n      \"remote_id\": \"85414bb6-6f88-4c0f-8c53-53e7c1f7128c\",\n      \"reference\": \"FBA181D492B4: 55641\",\n      \"total\": \"3950.0000\",\n      \"currency\": \"USD\",\n      \"document_date\": \"2024-05-23\",\n      \"status\": \"PAID\",\n      \"amount_match\": true,\n      \"date_match\": true,\n      \"reference_match\": true,\n      \"confident\": true,\n      \"dismissed\": false\n    },\n    {\n      \"connection_id\": 30,\n      \"remote_type\": \"xero_bill\",\n      \"remote_id\": \"11c2a7d0-882e-4a19-9f6b-73c0d9a1e2ff\",\n      \"reference\": \"PO-0299\",\n      \"total\": \"1204.0000\",\n      \"currency\": \"USD\",\n      \"document_date\": \"2025-09-03\",\n      \"status\": \"AUTHORISED\",\n      \"amount_match\": false,\n      \"date_match\": false,\n      \"reference_match\": false,\n      \"confident\": false,\n      \"dismissed\": true\n    }\n  ]\n}"}]},{"name":"Link Entry to Existing Document","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/relink","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","relink"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"Re-link a voided or deleted entry to an EXISTING provider document — the replacement already re-created in the accounting provider — instead of minting a fresh one. Repoints the entry's voided link onto the chosen document in place and records it so the NEXT sync pushes an update to that existing document, never a duplicate create.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nUse the candidate returned by `List Relink Candidates` to supply `remote_type` and `remote_id`. The action is guarded: the entry must still be voided/deleted, it must have a voided link on the given connection, and the target document must not already be linked to another entry.\n\nLinking a document also clears any reconcile-screen dismissal recorded against it.\n\n**Authentication:** Requires Bearer token.\n\n**Request body:**\n- `connection_id` (integer, required) — the accounting connection the document belongs to.\n- `remote_type` (string, required) — the document kind, e.g. `xero_bill`, `xero_invoice`, `qbo_bill`.\n- `remote_id` (string, required) — the provider document identifier to adopt.\n- `remote_status` (string, optional) — the document's current provider status (e.g. `AUTHORISED`), recorded on the link.\n\n**Response:** 200 with `data.relink` ({ relinked, status, reason }) and `data.entry` (the refreshed entry). When `relinked` is false, `reason` is one of `not_voided`, `no_parked_link`, or `already_linked_elsewhere`. Returns 404 when the entry does not exist, 422 when the body is invalid.\n\nRequires the `accounting.sync` permission.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 30,\n  \"remote_type\": \"xero_bill\",\n  \"remote_id\": \"54f0b7b3-9537-447a-bda2-897f7da26e9f\",\n  \"remote_status\": \"AUTHORISED\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/relink","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","relink"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"relink\": {\n      \"relinked\": true,\n      \"status\": \"update_pending\",\n      \"reason\": null\n    },\n    \"entry\": {\n      \"id\": 1842,\n      \"uuid\": \"9c2f1b7e-4a6d-4f0e-9b1a-2d8c5e7f3a10\",\n      \"type\": {\n        \"value\": \"purchase_order_invoice\",\n        \"label\": \"Purchase Order Invoice\"\n      },\n      \"status\": \"posted\",\n      \"display_status\": {\n        \"key\": \"update_pending\",\n        \"label\": \"Synced, Pending Update\",\n        \"color\": \"warning\",\n        \"reason\": null\n      },\n      \"can_sync_now\": true,\n      \"currency_code\": \"USD\",\n      \"total_debit\": \"49063.4000\",\n      \"total_credit\": \"49063.4000\",\n      \"document_number\": \"BILL-1842\",\n      \"lineage_root_id\": 1842,\n      \"document_number_is_inherited\": false,\n      \"document_number_is_provider_assigned\": false,\n      \"reference\": \"PO-0327: INVCBUSA-2000001\",\n      \"sync_links\": [\n        {\n          \"remote_type\": \"xero_bill\",\n          \"remote_id\": \"54f0b7b3-9537-447a-bda2-897f7da26e9f\",\n          \"link_state\": \"linked_stale\",\n          \"remote_status\": \"AUTHORISED\"\n        }\n      ],\n      \"operations\": []\n    }\n  },\n  \"message\": \"Linked to the existing provider document — it will update on the next sync.\"\n}"}]},{"name":"Fetch Latest From Provider","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/fetch-remote","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","fetch-remote"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"The inbound mirror of \"Sync Now\" (— two-way sync, on demand): instead of PUSHING local changes out, it re-reads this entry's remote document(s) FROM the accounting provider (Xero / QuickBooks) and re-grades the sync link RIGHT NOW. For each supported, still-linked connection it fetches the current remote truth via the same InboundChangeProcessor funnel the webhook + nightly sweep use — picking up drift (→ linked_conflict), a void/delete (→ remote_gone), a settled/paid lock (→ remote_locked), or a previously settled lock that has re-opened for edits — the paying payment / credit note was reversed at the provider, e.g. Xero PAID → AUTHORISED — which un-parks the link (→ linked_stale) so it is ready to resync — and refreshes `last_remote_seen_at`. A 404 at the provider degrades to a synthetic-deleted snapshot so the link still parks remote_gone rather than silently staying Synced.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRuns **synchronously** (the response reflects the re-read); the bulk equivalent is the `fetch_remote` action on POST /api/ledger/entries/bulk, which runs the same per-entry work as a tracked job.\n\n**Deferral:** if a push for this entry is in flight, the funnel writes NOTHING (touching the link would erase the drift signals reconciliation needs) and the response carries `data.fetch.deferred: true` with a \"try again once the in-flight sync settles\" message — still HTTP 200.\n\n**Authentication:** Requires Bearer token + `accounting.sync` permission (403 without it).\n\n**Request body:** none.\n\n**Response:** 200 with `data.fetch` ({ checked, not_linked, deferred, primary, outcomes }) and `data.entry` (the refreshed ledger entry object so the new link state renders without a second round-trip). `data.fetch.primary` is the most-notable settled outcome across the re-read links by severity (`remote_gone` > `remote_locked` > `conflict` > `remote_reopened` > `current`), or null when nothing was re-read. `message` explains the outcome. Returns **502 Bad Gateway** when the provider read itself fails (e.g. expired auth / network) — no link state is changed. Returns 404 when the entry does not exist.\n\nWhen the provider has settled the bill with a credit note that SKU has not mirrored as a vendor-credit allocation, the response reports `data.fetch.unreflected_credit = true` and lists the affected notes in `data.fetch.unreflected_credit_notes` (each with `credit_note_number`, `applied_amount` and `shortfall`), and `message` explains that a matching vendor credit must be created or allocated to reconcile the bill."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/fetch-remote","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","fetch-remote"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"fetch\": {\n      \"checked\": 1,\n      \"not_linked\": false,\n      \"deferred\": false,\n      \"primary\": \"current\",\n      \"outcomes\": {\n        \"current\": 1\n      },\n      \"unreflected_credit\": false,\n      \"unreflected_credit_notes\": []\n    },\n    \"entry\": {\n      \"id\": 1842,\n      \"type\": {\n        \"value\": \"bill\",\n        \"label\": \"Bill\"\n      },\n      \"status\": \"posted\",\n      \"display_status\": {\n        \"key\": \"synced\",\n        \"label\": \"Synced\",\n        \"reason\": null\n      },\n      \"document_number\": \"BILL-1842\",\n      \"lineage_root_id\": 1842,\n      \"document_number_is_inherited\": false,\n      \"document_number_is_provider_assigned\": false,\n      \"reference\": \"5cKTkaXNDpXqmNv0MzaOHAMppp6CsZOYXrgMbDv4UDg\",\n      \"currency_code\": \"USD\",\n      \"total_debit\": \"1305.86\",\n      \"total_credit\": \"1305.86\",\n      \"document_total\": \"842.67\",\n      \"sync_provider\": \"Xero\",\n      \"sync_links\": [\n        {\n          \"connection_id\": 7,\n          \"remote_type\": \"xero_bill\",\n          \"remote_id\": \"57406763-6b49-4be8-98b5-16750c106f6e\",\n          \"link_state\": \"linked_current\",\n          \"last_remote_seen_at\": \"2026-06-30T01:41:05Z\"\n        }\n      ]\n    }\n  },\n  \"message\": \"Refreshed from the provider — the entry is up to date.\"\n}"}]},{"name":"Get Entry Sync Inspection","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/sync-inspection","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","sync-inspection"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"Explain why a journal entry is (or is not) syncing to the accounting provider. Read-only — never writes.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Response fields:**\n- `eligible` (boolean) — the entry will sync automatically as it stands\n- `enableable` (boolean) — the entry won't auto-sync, but a manual sync (or a `force_enabled` override) will push it: its only blockers are settings opt-outs\n- `can_sync_now` (boolean) — `eligible OR enableable`; whether a manual sync would succeed right now\n- `reasons` (array of strings) — why automatic sync is blocked (empty when eligible). Possible codes: `override_disabled`, `type_disabled`, `channel_disabled`, `granularity_off`, `before_type_sync_start_date`, `before_provider_sync_start_date`, `batch_member`, `batch_open`, `internal_only`, `connection_inactive`, `no_accounting_connection`\n- `structural_reasons` (array of strings) — the subset that blocks even a manual sync (empty when the entry is eligible or enableable)\n- `sync_override` (string|null) — the entry's durable override (`force_enabled` / `force_disabled`), if any\n- `sync_override_provenance` (object|null) — who or what set the override: `source` (`user`, `legacy_cutover`, or `system`), `set_at` (ISO 8601), `set_by` (`{id, name}` of the user, or null)\n\nNotes: `channel_disabled` blocks scheduled sync only — a manual sync still pushes the entry. Structural codes (`connection_inactive`, `batch_member`, `batch_open`, `internal_only`, `before_provider_sync_start_date`) cannot be bypassed.\n\nReturns 404 when the entry does not exist."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/sync-inspection","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","sync-inspection"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"entry_id\": 1842,\n    \"eligible\": false,\n    \"enableable\": true,\n    \"can_sync_now\": true,\n    \"reasons\": [\n      \"channel_disabled\",\n      \"override_disabled\"\n    ],\n    \"structural_reasons\": [],\n    \"sync_override\": \"force_disabled\",\n    \"sync_override_provenance\": {\n      \"source\": \"legacy_cutover\",\n      \"set_at\": \"2026-07-16T14:00:00+00:00\",\n      \"set_by\": null\n    }\n  },\n  \"message\": null\n}"}]},{"name":"Set Sync Override","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/sync-override","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","sync-override"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"Set or clear the entry's durable sync override (Layer A). This endpoint is THE sanctioned writer of `ledger_journal_entries.sync_override` — the override is durable user intent, and regeneration never touches the column, so it survives rebuilds (fixes the legacy is_sync_enabled clobber bug).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Request body fields:**\n- `override` (required-present, nullable) — one of:\n  - `\"force_enabled\"` — force the entry eligible for sync\n  - `\"force_disabled\"` — force the entry excluded from sync\n  - `null` — clear the override back to derived eligibility\n\nThe key MUST be present in the body (HTTP 422 if omitted); send `null` to clear.\n\nAfter the write, sync work and display status are re-evaluated. The response echoes the entry's id, the saved sync_override and the recomputed display_status. Returns 404 when the entry does not exist.\n\nPermission: requires `accounting.sync` (granular permissions; admins bypass; unenforced when the tenant's granular_permissions feature flag is off). Returns 403 without it.\n\nEvery write records provenance (who set the override and when), returned as `sync_override_provenance` on the entry; clearing the override clears the provenance with it.","body":{"mode":"raw","raw":"{\n  \"override\": \"force_disabled\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/sync-override","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","sync-override"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1842,\n    \"sync_override\": \"force_disabled\",\n    \"display_status\": \"excluded\",\n    \"can_sync_now\": true,\n    \"locked_update_pending\": false,\n    \"locked_amend_blocked\": false\n  },\n  \"message\": \"Sync override updated.\"\n}"}]},{"name":"Set Reference Override","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/reference-override","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","reference-override"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"Set or clear the entry's durable **reference override**. The override is the custom reference shown in SKU and SENT TO THE ACCOUNTING PROVIDER (Xero/QBO) as the reference / invoice number, in place of the auto-derived `reference`. Like `sync_override` it is durable user intent — carried across draft regeneration and posted reversal+replacement — and the original `reference` is always retained for traceability.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Request body fields:**\n- `reference` (required-present, nullable string, max 191) — the custom reference. Send an empty string or `null` to clear the override back to the derived reference.\n\nThe key MUST be present (HTTP 422 if omitted). The response echoes the entry id, the derived `reference`, the saved `reference_override`, and `display_reference` (= override ?? reference, what the UI shows and what syncs). Returns 404 when the entry does not exist.\n\nPermission: requires `accounting.sync`.","body":{"mode":"raw","raw":"{\n  \"reference\": \"PO-0001.55\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/reference-override","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","reference-override"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1842,\n    \"document_number\": \"JE-1842\",\n    \"reference\": \"PO-0001\",\n    \"reference_override\": \"PO-0001.55\",\n    \"display_reference\": \"PO-0001.55\"\n  },\n  \"message\": \"Reference updated.\"\n}"}]},{"name":"Set Nominal Override","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/nominal-overrides","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","nominal-overrides"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"Pin a durable manual **nominal-code (GL account) override** on the entry: \"when this entry would post to `from_nominal_code_id`, post to `to_nominal_code_id` instead.\" The override is keyed by the entry identity and **re-applied during derivation on every rebuild**, so it survives regeneration the way `sync_override` does — while amounts, dates and lines keep tracking the source document (it pins ONLY the account, NOT a whole-entry lock).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nApplying it rebuilds the entry through the normal pipeline: a **draft** regenerates with the remapped account; a **posted** entry **reverses + re-posts** a correction (the accounting-correct path). Re-editing an already-overridden line keys off the line's ORIGINALLY-DERIVED account, so send `from_nominal_code_id = nominal_code_id_original ?? nominal_code_id`.\n\n**Authentication:** Requires Bearer token.\n\n**Request body fields:**\n- `from_nominal_code_id` (required, integer, exists:nominal_codes) — the originally-derived account to remap.\n- `to_nominal_code_id` (required, integer, exists:nominal_codes, different from `from`) — the account to post to.\n\nTo revert, use DELETE on this endpoint instead. Returns 404 when the entry does not exist.\n\nPermission: requires `accounting.sync`.","body":{"mode":"raw","raw":"{\n  \"from_nominal_code_id\": 10,\n  \"to_nominal_code_id\": 20\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/nominal-overrides","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","nominal-overrides"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [],\n  \"message\": \"Account override saved — the entry will regenerate with the new account shortly.\"\n}"}]},{"name":"Remove Nominal Override","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/nominal-overrides","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","nominal-overrides"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]},"description":"Remove the nominal-code override for one originally-derived account (`from_nominal_code_id`) so the line reverts to its source-derived account on the next rebuild.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Request body fields:**\n- `from_nominal_code_id` (required, integer) — the derived account whose override is being removed.\n\nReturns 404 when the entry does not exist. Permission: requires `accounting.sync`."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/ledger/entries/{entry}/nominal-overrides","host":["{{baseUrl}}"],"path":["api","ledger","entries",":entry","nominal-overrides"],"variable":[{"key":"entry","value":"1842","description":"Ledger journal entry ID (numeric). Route-model bound — returns 404 if the entry does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [],\n  \"message\": \"Override removed — the line will revert to its derived account shortly.\"\n}"}]},{"name":"Bulk Entry Action","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/entries/bulk","host":["{{baseUrl}}"],"path":["api","ledger","entries","bulk"]},"description":"Dispatch one tracked bulk action over ledger entries. Eligible/excluded counts are computed server-side via EligibilityResolver BEFORE dispatch, so the response counts are accurate. The job reports progress via the tracked job log endpoints.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.sync` permission (403 without it).\n\n**Request body fields:**\n- `action` (required) — one of: `sync_now` (queue an immediate provider push), `set_override` (force sync on/off — requires `override`), `rebuild` (regenerate from source; posted entries corrected via reversal + replacement), `unlink` (sever the provider sync link so SKU stops tracking the remote doc — a later sync CREATES a new doc), `clear_errors` (dismiss parked needs_action / failed_retryable operations WITHOUT retrying), `fetch_remote` (the INBOUND mirror of sync_now — re-read the remote document(s) and re-grade the link state: drift / void / settled-lock; runs as a tracked job), `delete` (HARD-delete the entry — permanently removes the journal entry, its GL lines, provider sync links and operation history, and nulls any sibling reversal pointer; IRREVERSIBLE; unlike the other actions it runs even when ledger generation is disabled).\n- `scope` (optional, `selected` default | `all` | `all_stale`) — `all` targets every entry matching the CURRENT filters (send the same `filter[...]` / `filter_groups` query params as the list); `all_stale` targets every stale pre-cutover (frozen history) entry, so one `rebuild` call drains the entire stale worklist rather than only its preview page. Both are resolved server-side and capped at 5000; `entry_ids` is then optional/ignored.\n- `entry_ids` (array, required unless `scope=all` or `scope=all_stale`, max 1000) — the explicitly selected entry ids.\n- `override` (required when `action=set_override`) — `force_enabled` | `force_disabled` | null (clear).\n\n**Eligibility (excluded from the action, counted in `excluded_count`):** `sync_now` → entries eligible on at least one connection; `rebuild` → source still exists; `unlink` and `fetch_remote` → entry currently has a live remote link; `clear_errors` → entry carries a clearable parked error; `set_override` and `delete` → always applicable.\n\n**Note:** when ledger generation is disabled (kill switch) no job dispatches — the response carries `note: \"generation_disabled\"` and `tracked_job_log_id: null`. The `delete` action is the one exception: it always dispatches its job so test/erroneous entries can be cleaned up while the kill switch is on.\n\n**Response fields:** `tracked_job_log_id` (null when no job dispatched), `total` (entries the job will process), `eligible_count`, `excluded_count`. When `scope=all` or `scope=all_stale` and the match set exceeds the cap (default 5000, `ledger.bulk.all_scope_cap`), the remainder is dropped and the response adds `all_scope_truncated: true`, `matched_total` (total entries matching the filters), and `cap` (the limit applied); the message then names how many of how many entries were started.\n\n**`replace_nominal_code` action (find & replace account):** remaps every line deriving `from_nominal_code_id` to `to_nominal_code_id` across the selected/all-filtered entries by pinning a durable per-entry override (re-applied on every rebuild). Extra body fields when `action = \"replace_nominal_code\"`:\n- `from_nominal_code_id` (required, integer, exists:nominal_codes)\n- `to_nominal_code_id` (required, integer, exists:nominal_codes, different from `from`)\nRuns as a tracked background job (tracked job log endpoints).","body":{"mode":"raw","raw":"{\n  \"action\": \"unlink\",\n  \"entry_ids\": [\n    1842,\n    1836,\n    1831\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/entries/bulk","host":["{{baseUrl}}"],"path":["api","ledger","entries","bulk"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 4821,\n    \"total\": 312,\n    \"eligible_count\": 298,\n    \"excluded_count\": 14\n  },\n  \"message\": \"Bulk action started — track progress in the job tray.\",\n  \"status\": \"success\"\n}"}]},{"name":"Export Journal Entries","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/export","host":["{{baseUrl}}"],"path":["api","ledger","entries","export"]},"description":"Download the journal-entry list as an XLSX or CSV file (the Export modal). Honours the same filters as the index plus `format` (xlsx/csv) and `columns` (visible-column subset). Scope is expressed by the filters sent: 'all' sends only the base scope, 'filtered' sends the active filters, 'selected' adds filter[ids]. Returns a binary file download (Content-Disposition), not JSON. Requires a Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."}},{"name":"Get Selection Summary","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/entries/selection-summary","host":["{{baseUrl}}"],"path":["api","ledger","entries","selection-summary"]},"description":"Per-nominal-code debit/credit/line-count breakdown of a selection of entries plus grand totals — powers the table's expandable Selection Summary panel (parity with the legacy SelectedTransactionsSummary), sourced from the real journal lines.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Request body:**\n- `scope` (optional, `selected` default | `all`) — `all` summarizes every entry matching the CURRENT filters (forward the same `filter[...]` / `filter_groups` query params), capped at 5000; `entry_ids` then optional.\n- `entry_ids` (array, required unless `scope=all`, max 5000) — the selected entry ids.\n\n**Response:** `entry_count`, `currency` (null when the selection mixes currencies), `total_debit`, `total_credit`, and `accounts[]` of `{nominal_code_id, code, name, debit, credit, line_count}` (unmapped lines collapse under a null nominal_code_id).","body":{"mode":"raw","raw":"{\n  \"entry_ids\": [\n    1842,\n    1836,\n    1831\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/entries/selection-summary","host":["{{baseUrl}}"],"path":["api","ledger","entries","selection-summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"selection_summary\": {\n      \"entry_count\": 2,\n      \"currency\": \"USD\",\n      \"total_debit\": \"140.0000\",\n      \"total_credit\": \"140.0000\",\n      \"accounts\": [\n        {\n          \"nominal_code_id\": 4,\n          \"code\": \"1000\",\n          \"name\": \"Inventory Control\",\n          \"debit\": \"140.0000\",\n          \"credit\": \"0.0000\",\n          \"line_count\": 2\n        },\n        {\n          \"nominal_code_id\": 3,\n          \"code\": \"5000\",\n          \"name\": \"Cost of Goods Sold\",\n          \"debit\": \"0.0000\",\n          \"credit\": \"140.0000\",\n          \"line_count\": 2\n        }\n      ]\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Repost All Entries","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/repost-all","host":["{{baseUrl}}"],"path":["api","ledger","entries","repost-all"]},"description":"Repost every current (non-discarded, non-reversed) ledger entry so posted GL lines pick up a changed chart of accounts — the onboarding backfill after adopting a provider's chart. Reuses the bulk rebuild pipeline: posted entries auto-reverse + replace with the new nominal codes, drafts regenerate in place, batch aggregates route through the batch state machine. Requires the `accounting.sync` permission. Returns the tracked job log id + eligible/excluded counts.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/entries/repost-all","host":["{{baseUrl}}"],"path":["api","ledger","entries","repost-all"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 5120,\n    \"total\": 842,\n    \"eligible_count\": 842,\n    \"excluded_count\": 0\n  },\n  \"message\": \"Repost started for 842 ledger entries — track progress in the job tray.\"\n}"}]},{"name":"Get Entry by Source","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/by-source?source_type=App\\Models\\PurchaseInvoice&source_id=3","host":["{{baseUrl}}"],"path":["api","ledger","entries","by-source"],"query":[{"key":"source_type","value":"App\\Models\\PurchaseInvoice","description":"Source model FQN (required)","disabled":true},{"key":"source_id","value":"3","description":"Source record id (required)","disabled":true}]},"description":"The current v2 ledger journal entry for a source document (or data:null). Lets a source-doc accounting tab (Purchase Invoice, PO, Sales Order, etc.) show its v2 ledger entry as the accounting transaction — linked to /v2/ledger/entries/{id} — instead of the legacy v1 accounting_transaction. source_type is the source model FQN.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/entries/by-source?source_type=App\\Models\\PurchaseInvoice&source_id=3","host":["{{baseUrl}}"],"path":["api","ledger","entries","by-source"],"query":[{"key":"source_type","value":"App\\Models\\PurchaseInvoice","description":"Source model FQN (required)","disabled":true},{"key":"source_id","value":"3","description":"Source record id (required)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"document_number\": \"JE-3\",\n    \"lineage_root_id\": 3,\n    \"document_number_is_inherited\": false,\n    \"document_number_is_provider_assigned\": false,\n    \"reference\": \"PO-0001: ionv001\",\n    \"display_status\": \"excluded\",\n    \"can_sync_now\": true,\n    \"locked_update_pending\": false,\n    \"locked_amend_blocked\": false,\n    \"total_debit\": \"5000.0000\",\n    \"total_credit\": \"5000.0000\",\n    \"effective_at\": \"2026-06-26T00:00:00Z\"\n  }\n}"}]},{"name":"List Entries by Source","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/entries/by-source-list?source_type=Modules\\Amazon\\Entities\\AmazonFinancialEventGroup&source_id=16","host":["{{baseUrl}}"],"path":["api","ledger","entries","by-source-list"],"query":[{"key":"source_type","value":"Modules\\Amazon\\Entities\\AmazonFinancialEventGroup","description":"Source model FQN (required)","disabled":true},{"key":"source_id","value":"16","description":"Source record id (required)","disabled":true}]},"description":"Every live (non-discarded, non-reversal, non-reversed) v2 ledger journal entry for a source document, as a lightweight list. Where /by-source returns only the latest single entry, a source can post more than one current entry — an Amazon settlement posts both an invoice AND a bill; a vendor credit posts the credit entry plus one applied entry per allocation — so a source document's accounting list needs the full set. Each item links to /v2/ledger/entries/{id}. source_type is the source model FQN.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nReturns data: an array of { id, type, type_label, status, reference, effective_at, total, display_status }. Empty array when the source has no live entries.\n\n**`total`** is the NET document total (the provider-booked face value — net control-account movement), not the gross debit total. For an Amazon settlement bill with internal reserve contra legs the two differ (e.g. 842.67 net vs 1,305.86 gross).\n\n**`status`** is the entry's lifecycle status (draft / posted / reversed). **`display_status`** is the composite sync status ({ key, label, color }); its fields are null when the entry does not participate in provider sync."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/entries/by-source-list?source_type=Modules\\Amazon\\Entities\\AmazonFinancialEventGroup&source_id=16","host":["{{baseUrl}}"],"path":["api","ledger","entries","by-source-list"],"query":[{"key":"source_type","value":"Modules\\Amazon\\Entities\\AmazonFinancialEventGroup","description":"Source model FQN (required)","disabled":true},{"key":"source_id","value":"16","description":"Source record id (required)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 29,\n      \"type\": \"sales_order_invoice\",\n      \"type_label\": \"Sales Order Invoice\",\n      \"status\": \"posted\",\n      \"reference\": \"13029880822\",\n      \"effective_at\": \"2026-06-14T00:00:00+00:00\",\n      \"total\": \"115229.93\",\n      \"display_status\": {\n        \"key\": \"synced\",\n        \"label\": \"Synced\",\n        \"color\": \"success\"\n      }\n    },\n    {\n      \"id\": 30,\n      \"type\": \"bill\",\n      \"type_label\": \"Bill\",\n      \"status\": \"posted\",\n      \"reference\": \"13029880822\",\n      \"effective_at\": \"2026-06-14T00:00:00+00:00\",\n      \"total\": \"38755.67\",\n      \"display_status\": {\n        \"key\": \"pending_sync\",\n        \"label\": \"Pending Sync\",\n        \"color\": \"info\"\n      }\n    }\n  ]\n}"}]},{"name":"List Payments","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/payments","host":["{{baseUrl}}"],"path":["api","ledger","payments"]},"description":"SKU-side payments (App\\Models\\Payment) paginated, each with its source document and the linked accounting-provider payment (Xero/QBO) cross-link. Backs the Accounting v2 Payments tab. `provider_payment` is null when the payment has not been synced to a provider; `status` is synced / error / not_synced.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthenticate with a Personal Access Token (PAT)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/payments","host":["{{baseUrl}}"],"path":["api","ledger","payments"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 4821,\n      \"amount\": \"59.99\",\n      \"currency_code\": \"USD\",\n      \"payment_date\": \"2026-06-14T00:00:00+00:00\",\n      \"type\": \"direct\",\n      \"payment_type\": \"Amazon Payment\",\n      \"external_reference\": \"114-4389768-5982613\",\n      \"source\": {\n        \"type\": \"App\\\\Models\\\\SalesOrder\",\n        \"id\": 18519,\n        \"label\": \"114-4389768-5982613\",\n        \"link_type_label\": \"Sales Order\",\n        \"link_route\": \"/orders/sales-orders/18519\",\n        \"link_legacy_url\": null,\n        \"url\": \"/orders/sales-orders/18519\"\n      },\n      \"provider_payment\": {\n        \"provider\": \"Xero\",\n        \"reference\": \"pay-86906814-f1e5\",\n        \"link_route\": \"/integrations/xero/payments/12\",\n        \"remote_url\": \"https://go.xero.com/Bank/ViewTransaction.aspx?bankTransactionID=pay-86906814-f1e5\",\n        \"sync_status\": \"synced\"\n      },\n      \"status\": \"synced\",\n      \"created_at\": \"2026-06-14T08:58:00+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 2704,\n  \"per_page\": 15,\n  \"total\": 40547,\n  \"from\": 1,\n  \"to\": 15\n}"}]},{"name":"Sync Payment to Provider","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/payments/{payment}/sync","host":["{{baseUrl}}"],"path":["api","ledger","payments",":payment","sync"],"variable":[{"key":"payment","value":"42","description":"The SKU payment id"}]},"description":"Manually push (or re-push) one SKU payment to the connected accounting provider (two-way payment sync) — the row action on the Payments tab. Runs inline: eligibility rejections and provider validation errors are persisted to the payment's last_sync_error and returned as 422; transport/auth failures return 502 with no state change.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRequires Bearer token + `accounting.sync` permission and an active ledger cutover.\n\nEligibility: the payment must be SKU-originated (not ingested from the provider), a cash-style type, and its owner document's current ledger entry must have a live sync link to a provider invoice/bill. On success the payment is linked to the created provider payment (accounting_integration) and its status on the Payments tab flips to Synced."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/payments/{payment}/sync","host":["{{baseUrl}}"],"path":["api","ledger","payments",":payment","sync"],"variable":[{"key":"payment","value":"42","description":"The SKU payment id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Payment synced to the accounting provider.\",\n  \"status\": \"success\"\n}"}]},{"name":"List Batches","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/batches","host":["{{baseUrl}}"],"path":["api","ledger","batches"]},"description":"Paginated ledger batches listing: the grouping key (type, period, channel, currency), lifecycle status with the derived `is_dirty` flag, members_count and the aggregate journal entry summary.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Filters (Spatie QueryBuilder):**\n- `filter[type]` — exact entry type\n- `filter[period_type]` — daily | monthly\n- `filter[status]` — open | dirty | closing | posted | reopened\n- `filter[currency_code]` — exact currency\n- `filter[channel]` — exact integration_instance_id\n- `filter[date_from]` / `filter[date_to]` — m/d/Y on period_start (invalid dates silently ignored)\n- `filter[dirty]` — boolean; true = status dirty or reopened (the needs-rebuild indicator), false = everything else\n\n**Sorts:** id, period_start, type, members_count, created_at (default: -period_start).\n\n**Pagination:** per_page default 10, clamped to max 100.\n\n`entry` is the batch's current aggregate journal entry summary (null when not yet built). `channel` is null for batches not scoped to a channel.\n\n**Advanced filters (`filter_groups`):** base64-encoded JSON tree of AND/OR groups via GroupedFilterSupport (same stack as the entries index).\n**Filterable columns:** `batch_type`, `batch_status`, `batch_period_type`, `currency` (text operators); `id`, `members_count` (numeric operators); `period_start`, `period_end` (date operators); `created_at` (datetime operators). Unregistered columns are rejected with HTTP 400.\n\n**Search:** `filter[search]` matches the batch type and the aggregate journal entry's reference.\n**Sorts:** id, period_start, type, members_count, currency_code, status, created_at, channel (channel name via subquery) — prefix with - for DESC; default -period_start.\n\n**Provider sync (entry object):** each batch's `entry` includes `provider_status` — the accounting provider's own status for the synced document (e.g. AUTHORISED, POSTED, PAID; null when never synced) — and `provider_link`, the linked provider document (`provider`, `label`, `link_route`, `remote_url`; null when never synced).\n\n**Overdue:** `is_overdue` is true when the batch has not closed and its `settle_deadline` has passed. The settle deadline is the period end plus a grace window that lets late documents still join the batch (72 hours for monthly, 24 for daily). A batch past it should have closed and posted: its figures sit in the books as a re-derivable draft that has not reached the accounting provider."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/batches","host":["{{baseUrl}}"],"path":["api","ledger","batches"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 57,\n      \"type\": {\n        \"value\": \"fba_shipments\",\n        \"label\": \"FBA Shipments\"\n      },\n      \"period_type\": \"monthly\",\n      \"period_start\": \"2026-06-01\",\n      \"period_end\": \"2026-06-30\",\n      \"channel\": {\n        \"id\": 8,\n        \"name\": \"Amazon US\"\n      },\n      \"currency_code\": \"USD\",\n      \"status\": \"open\",\n      \"is_dirty\": false,\n      \"members_count\": 1240,\n      \"is_sealed_import\": false,\n      \"entry\": {\n        \"id\": 1901,\n        \"reference\": \"FBA_SHIPMENTS-M-2026-06-01-USD-CH8\",\n        \"description\": \"Fba Shipments — Amazon US for Jun 2026\",\n        \"status\": \"draft\",\n        \"display_status\": {\n          \"key\": \"pending_sync\",\n          \"label\": \"Pending Sync\",\n          \"color\": \"info\"\n        },\n        \"can_sync_now\": true,\n        \"provider_link\": null,\n        \"provider_status\": null,\n        \"total_debit\": \"48211.7500\",\n        \"total_credit\": \"48211.7500\"\n      },\n      \"created_at\": \"2026-06-01T00:05:11+00:00\",\n      \"settle_deadline\": \"2026-07-04T00:00:00+00:00\",\n      \"is_overdue\": true\n    },\n    {\n      \"id\": 52,\n      \"type\": {\n        \"value\": \"batch_sales_order_invoice\",\n        \"label\": \"Batch Sales Order Invoice\"\n      },\n      \"period_type\": \"daily\",\n      \"period_start\": \"2026-06-08\",\n      \"period_end\": \"2026-06-08\",\n      \"channel\": {\n        \"id\": 5,\n        \"name\": \"Shopify Main Store\"\n      },\n      \"currency_code\": \"USD\",\n      \"status\": \"posted\",\n      \"is_dirty\": false,\n      \"members_count\": 86,\n      \"is_sealed_import\": false,\n      \"entry\": {\n        \"id\": 1888,\n        \"reference\": \"BATCH_SALES_ORDER_INVOICE-D-2026-06-08-USD-CH5\",\n        \"description\": \"Batch Sales Order Invoices — Shopify Main Store for 2026-06-08\",\n        \"status\": \"posted\",\n        \"display_status\": {\n          \"key\": \"synced\",\n          \"label\": \"Synced\",\n          \"color\": \"success\"\n        },\n        \"can_sync_now\": true,\n        \"provider_link\": {\n          \"provider\": \"Xero\",\n          \"remote_type\": \"xero_invoice\",\n          \"remote_id\": \"b7c1e2a4-9f3d-4a2b-8e6c-2f1d0a9b8c77\",\n          \"link_type_label\": \"Invoice\",\n          \"label\": \"INV-1042\",\n          \"link_route\": \"/integrations/xero/invoices/318\",\n          \"remote_url\": \"https://go.xero.com/app/invoicing/view/b7c1e2a4-9f3d-4a2b-8e6c-2f1d0a9b8c77\"\n        },\n        \"provider_status\": \"AUTHORISED\",\n        \"total_debit\": \"10422.9000\",\n        \"total_credit\": \"10422.9000\"\n      },\n      \"created_at\": \"2026-06-08T00:02:33+00:00\",\n      \"settle_deadline\": \"2026-07-04T00:00:00+00:00\",\n      \"is_overdue\": true\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"https://sku-alt.test/api/ledger/batches?page=1\",\n  \"from\": 1,\n  \"last_page\": 6,\n  \"last_page_url\": \"https://sku-alt.test/api/ledger/batches?page=6\",\n  \"links\": [\n    {\n      \"url\": null,\n      \"label\": \"&laquo; Previous\",\n      \"active\": false\n    },\n    {\n      \"url\": \"https://sku-alt.test/api/ledger/batches?page=1\",\n      \"label\": \"1\",\n      \"active\": true\n    },\n    {\n      \"url\": \"https://sku-alt.test/api/ledger/batches?page=2\",\n      \"label\": \"Next &raquo;\",\n      \"active\": false\n    }\n  ],\n  \"next_page_url\": \"https://sku-alt.test/api/ledger/batches?page=2\",\n  \"path\": \"https://sku-alt.test/api/ledger/batches\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 10,\n  \"total\": 54\n}"}]},{"name":"Get Batch","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/batches/{batch}","host":["{{baseUrl}}"],"path":["api","ledger","batches",":batch"],"variable":[{"key":"batch","value":"57","description":"Ledger batch ID (numeric). Route-model bound — returns 404 if the batch does not exist."}]},"description":"Batch detail: the batch resource (under the `batch` key) plus `totals_by_account` — the aggregate journal entry's GL impact grouped by nominal code, one row per account with its debit and credit sums (4-decimal strings).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n`totals_by_account` is an empty array when the batch's aggregate entry has not been built yet. Returns 404 when the batch does not exist.\n\n**Provider sync (entry object):** each batch's `entry` includes `provider_status` — the accounting provider's own status for the synced document (e.g. AUTHORISED, POSTED, PAID; null when never synced) — and `provider_link`, the linked provider document (`provider`, `label`, `link_route`, `remote_url`; null when never synced).\n\n`members_count` counts the batch's member documents; `member_entries_count` counts the journal entries stamped with the batch id. They match for a batch built by the posting rule, and differ when the batch was carried over whole at the accounting cutover (`is_sealed_import: true`), in which case the period's aggregate exists but the per-member entries do not. Read membership with GET /api/ledger/batches/{batch}/members in either case.\n\n**Overdue:** `is_overdue` is true when the batch has not closed and its `settle_deadline` has passed. The settle deadline is the period end plus a grace window that lets late documents still join the batch (72 hours for monthly, 24 for daily). A batch past it should have closed and posted: its figures sit in the books as a re-derivable draft that has not reached the accounting provider."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/batches/{batch}","host":["{{baseUrl}}"],"path":["api","ledger","batches",":batch"],"variable":[{"key":"batch","value":"57","description":"Ledger batch ID (numeric). Route-model bound — returns 404 if the batch does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"batch\": {\n      \"id\": 57,\n      \"type\": {\n        \"value\": \"fba_shipments\",\n        \"label\": \"FBA Shipments\"\n      },\n      \"period_type\": \"monthly\",\n      \"period_start\": \"2026-06-01\",\n      \"period_end\": \"2026-06-30\",\n      \"channel\": {\n        \"id\": 8,\n        \"name\": \"Amazon US\"\n      },\n      \"currency_code\": \"USD\",\n      \"status\": \"open\",\n      \"is_dirty\": false,\n      \"members_count\": 1240,\n      \"member_entries_count\": 1240,\n      \"is_sealed_import\": false,\n      \"entry\": {\n        \"id\": 1901,\n        \"reference\": \"FBA_SHIPMENTS-M-2026-06-01-USD-CH8\",\n        \"description\": \"Fba Shipments — Amazon US for Jun 2026\",\n        \"status\": \"draft\",\n        \"display_status\": {\n          \"key\": \"pending_sync\",\n          \"label\": \"Pending Sync\",\n          \"color\": \"info\"\n        },\n        \"can_sync_now\": true,\n        \"provider_link\": null,\n        \"provider_status\": null,\n        \"total_debit\": \"48211.7500\",\n        \"total_credit\": \"48211.7500\"\n      },\n      \"created_at\": \"2026-06-01T00:05:11+00:00\",\n      \"settle_deadline\": \"2026-07-04T00:00:00+00:00\",\n      \"is_overdue\": true\n    },\n    \"totals_by_account\": [\n      {\n        \"nominal_code\": {\n          \"id\": 40,\n          \"code\": \"4000\",\n          \"name\": \"Sales Revenue\"\n        },\n        \"debit\": \"0.0000\",\n        \"credit\": \"48211.7500\"\n      },\n      {\n        \"nominal_code\": {\n          \"id\": 12,\n          \"code\": \"1100\",\n          \"name\": \"Accounts Receivable\"\n        },\n        \"debit\": \"48211.7500\",\n        \"credit\": \"0.0000\"\n      }\n    ]\n  }\n}"}]},{"name":"List Batch Members","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/batches/{batch}/members","host":["{{baseUrl}}"],"path":["api","ledger","batches",":batch","members"],"variable":[{"key":"batch","value":"57","description":"Ledger batch ID (numeric). Route-model bound — returns 404 if the batch does not exist."}]},"description":"Paginated member documents of a batch — the A2X-style lineage view. Each member row carries:\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n- `member` — the cross-link view-model for the member document (morph type + ID, friendly label/reference, link_type_label, the application UI link_route when a detail page exists)\n- `contribution` — the member's debit/credit contribution inside this batch (its journal lines summed by direction, 4-decimal strings)\n- `added_at` — when the document joined the batch\n\n**Authentication:** Requires Bearer token.\n\n**Pagination:** per_page default 10, clamped to max 100. Newest members first.\n\nReturns 404 when the batch does not exist.\n\n**Advanced filters (`filter_groups`):** base64-encoded JSON tree of AND/OR groups via GroupedFilterSupport.\n**Filterable columns:** `member_type` (text operators); `member_id`, `id` (numeric operators); `added_at` (datetime operators). Unregistered columns are rejected with HTTP 400.\n\n**Search:** `filter[search]` matches the member's morph type and (when numeric, # prefix allowed) the document id.\n**Sorts:** id, member_id, member_type, added_at, member (alias of member_id), debit, credit, document_date (contribution sums via correlated subquery) — prefix with - for DESC; default -id.\n\n**Response field `document_date`:** the member's accounting date, distinct from `added_at` (when the membership row was created). It is the member's own journal entry's effective date; when the member has no entry of its own — a batch carried over whole at the accounting cutover never had per-member entries generated — it falls back to the source document's own date, so the field is populated either way. Null only for member types whose accounting date is derived rather than stored. Sortable via `sort=document_date`, which sorts on the entry-derived value."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/batches/{batch}/members","host":["{{baseUrl}}"],"path":["api","ledger","batches",":batch","members"],"variable":[{"key":"batch","value":"57","description":"Ledger batch ID (numeric). Route-model bound — returns 404 if the batch does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 9120,\n      \"member\": {\n        \"type\": \"App\\\\Models\\\\SalesOrderFulfillment\",\n        \"id\": 44312,\n        \"label\": \"SO-10293-F1\",\n        \"link_type_label\": \"Sales Order Fulfillment\",\n        \"link_route\": null,\n        \"link_legacy_url\": null,\n        \"url\": null\n      },\n      \"contribution\": {\n        \"debit\": \"129.9900\",\n        \"credit\": \"129.9900\"\n      },\n      \"document_date\": \"2026-06-08T00:00:00+00:00\",\n      \"added_at\": \"2026-06-08T10:15:30+00:00\"\n    },\n    {\n      \"id\": 9119,\n      \"member\": {\n        \"type\": \"App\\\\Models\\\\SalesOrder\",\n        \"id\": 10288,\n        \"label\": \"SO-10288\",\n        \"link_type_label\": \"Sales Order\",\n        \"link_route\": \"/orders/sales-orders/10288\",\n        \"link_legacy_url\": null,\n        \"url\": \"/orders/sales-orders/10288\"\n      },\n      \"contribution\": {\n        \"debit\": \"84.5000\",\n        \"credit\": \"84.5000\"\n      },\n      \"document_date\": \"2026-06-07T00:00:00+00:00\",\n      \"added_at\": \"2026-06-08T09:42:12+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"https://sku-alt.test/api/ledger/batches/57/members?page=1\",\n  \"from\": 1,\n  \"last_page\": 124,\n  \"last_page_url\": \"https://sku-alt.test/api/ledger/batches/57/members?page=124\",\n  \"links\": [\n    {\n      \"url\": null,\n      \"label\": \"&laquo; Previous\",\n      \"active\": false\n    },\n    {\n      \"url\": \"https://sku-alt.test/api/ledger/batches/57/members?page=1\",\n      \"label\": \"1\",\n      \"active\": true\n    },\n    {\n      \"url\": \"https://sku-alt.test/api/ledger/batches/57/members?page=2\",\n      \"label\": \"Next &raquo;\",\n      \"active\": false\n    }\n  ],\n  \"next_page_url\": \"https://sku-alt.test/api/ledger/batches/57/members?page=2\",\n  \"path\": \"https://sku-alt.test/api/ledger/batches/57/members\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 10,\n  \"total\": 1240\n}"}]},{"name":"Get Batch History","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/batches/{batch}/history","host":["{{baseUrl}}"],"path":["api","ledger","batches",":batch","history"],"variable":[{"key":"batch","value":"57","description":"Ledger batch ID (numeric). Route-model bound — returns 404 if the batch does not exist."}]},"description":"Batch membership + correction history. Returns two keys:\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n- `events` — a paginated list of membership audit events (action added | removed, the member cross-link, `occurred_after_post` — whether the batch's aggregate entry was already POSTED at the moment of the change, i.e. the change triggered an automatic reversal + replacement) — newest first, per_page default 10 clamped to 100\n- `reversal_chain` — every non-discarded aggregate journal entry sourced from this batch, oldest first: the current entry, reversed originals and the reversal mirror rows (id, reference, status, reversal_of_id, replaced_by_id, created_at)\n\n**Authentication:** Requires Bearer token.\n\nReturns 404 when the batch does not exist."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/batches/{batch}/history","host":["{{baseUrl}}"],"path":["api","ledger","batches",":batch","history"],"variable":[{"key":"batch","value":"57","description":"Ledger batch ID (numeric). Route-model bound — returns 404 if the batch does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"events\": {\n      \"data\": [\n        {\n          \"id\": 15233,\n          \"action\": \"added\",\n          \"member\": {\n            \"type\": \"App\\\\Models\\\\SalesOrder\",\n            \"id\": 10301,\n            \"label\": \"SO-10301\",\n            \"link_type_label\": \"Sales Order\",\n            \"link_route\": \"/orders/sales-orders/10301\",\n            \"link_legacy_url\": null,\n            \"url\": \"/orders/sales-orders/10301\"\n          },\n          \"occurred_after_post\": true,\n          \"created_at\": \"2026-06-09T16:20:44+00:00\"\n        },\n        {\n          \"id\": 15201,\n          \"action\": \"removed\",\n          \"member\": {\n            \"type\": \"App\\\\Models\\\\SalesOrder\",\n            \"id\": 10295,\n            \"label\": \"SO-10295\",\n            \"link_type_label\": \"Sales Order\",\n            \"link_route\": \"/orders/sales-orders/10295\",\n            \"link_legacy_url\": null,\n            \"url\": \"/orders/sales-orders/10295\"\n          },\n          \"occurred_after_post\": false,\n          \"created_at\": \"2026-06-08T11:02:10+00:00\"\n        }\n      ],\n      \"current_page\": 1,\n      \"first_page_url\": \"https://sku-alt.test/api/ledger/batches/52/history?page=1\",\n      \"from\": 1,\n      \"last_page\": 9,\n      \"last_page_url\": \"https://sku-alt.test/api/ledger/batches/52/history?page=9\",\n      \"links\": [\n        {\n          \"url\": null,\n          \"label\": \"&laquo; Previous\",\n          \"active\": false\n        },\n        {\n          \"url\": \"https://sku-alt.test/api/ledger/batches/52/history?page=1\",\n          \"label\": \"1\",\n          \"active\": true\n        },\n        {\n          \"url\": \"https://sku-alt.test/api/ledger/batches/52/history?page=2\",\n          \"label\": \"Next &raquo;\",\n          \"active\": false\n        }\n      ],\n      \"next_page_url\": \"https://sku-alt.test/api/ledger/batches/52/history?page=2\",\n      \"path\": \"https://sku-alt.test/api/ledger/batches/52/history\",\n      \"per_page\": 10,\n      \"prev_page_url\": null,\n      \"to\": 10,\n      \"total\": 88\n    },\n    \"reversal_chain\": [\n      {\n        \"id\": 1888,\n        \"reference\": \"BATCH-SALES-2026-06-08-SHOPIFY\",\n        \"status\": \"reversed\",\n        \"reversal_of_id\": null,\n        \"replaced_by_id\": 1925,\n        \"created_at\": \"2026-06-08T00:02:35+00:00\"\n      },\n      {\n        \"id\": 1924,\n        \"reference\": \"BATCH-SALES-2026-06-08-SHOPIFY\",\n        \"status\": \"posted\",\n        \"reversal_of_id\": 1888,\n        \"replaced_by_id\": null,\n        \"created_at\": \"2026-06-09T16:21:02+00:00\"\n      },\n      {\n        \"id\": 1925,\n        \"reference\": \"BATCH-SALES-2026-06-08-SHOPIFY\",\n        \"status\": \"posted\",\n        \"reversal_of_id\": null,\n        \"replaced_by_id\": null,\n        \"created_at\": \"2026-06-09T16:21:02+00:00\"\n      }\n    ]\n  }\n}"}]},{"name":"Get Batch Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/batches/{batch}/activity-log","host":["{{baseUrl}}"],"path":["api","ledger","batches",":batch","activity-log"],"variable":[{"key":"batch","value":"12","description":"Ledger batch id"}]},"description":"The batch's standard LogsActivity feed (the detail page Activity tab): creation plus every status transition (open, dirty, closing, posted, reopened) with old/new values, causer and timestamp. Guarded query-update transitions are logged manually by the repository, so the feed covers the full state machine.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\nNote: membership add/remove changes are NOT mirrored here — they live in the dedicated `/history` endpoint (ledger_batch_member_events)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/batches/{batch}/activity-log","host":["{{baseUrl}}"],"path":["api","ledger","batches",":batch","activity-log"],"variable":[{"key":"batch","value":"12","description":"Ledger batch id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 166,\n      \"description\": \"updated\",\n      \"event\": \"updated\",\n      \"subject_type\": \"LedgerBatch\",\n      \"subject_id\": 12,\n      \"properties\": {\n        \"attributes\": {\n          \"status\": \"dirty\"\n        },\n        \"old\": {\n          \"status\": \"closing\"\n        },\n        \"metadata\": {\n          \"type\": \"sales_order_fulfillment\",\n          \"period_type\": \"daily\",\n          \"period_start\": \"2026-06-11\",\n          \"currency_code\": \"USD\"\n        }\n      },\n      \"causer_name\": null,\n      \"created_at\": \"2026-06-12T23:12:00.000000Z\"\n    }\n  ],\n  \"per_page\": 15,\n  \"total\": 1\n}"}]},{"name":"Rebuild Batch","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/batches/{batch}/rebuild","host":["{{baseUrl}}"],"path":["api","ledger","batches",":batch","rebuild"],"variable":[{"key":"batch","value":"57","description":"Ledger batch ID (numeric). Route-model bound — returns 404 if the batch does not exist."}]},"description":"Manually rebuild the batch's aggregate journal entry: marks the batch dirty (posted batches flip to `reopened` — the post-post correction pathway, where the rebuild reverses + replaces the posted aggregate entry) and dispatches a background job immediately (no debounce for a manual request; the job's per-batch uniqueness still collapses doubles).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nDeliberately NOT a tracked job: batch builds run in seconds on the per-tenant serial queue, and the batch row's status transition (dirty/reopened → open/closing) is the user-visible completion signal.\n\n**Authentication:** Requires Bearer token.\n\n**Request body:** none.\n\n**Response:** accepted-style 200 — the work is queued (App\\Response normalizes every 2xx to 200). Returns 404 when the batch does not exist.\n\nPermission: requires `accounting.sync` (granular permissions; admins bypass; unenforced when the tenant's granular_permissions feature flag is off). Returns 403 without it."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/batches/{batch}/rebuild","host":["{{baseUrl}}"],"path":["api","ledger","batches",":batch","rebuild"],"variable":[{"key":"batch","value":"57","description":"Ledger batch ID (numeric). Route-model bound — returns 404 if the batch does not exist."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [],\n  \"message\": \"Batch rebuild queued — the aggregate entry will be rebuilt shortly.\"\n}"}]},{"name":"Get Account Overview","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/accounts/{nominalCode}","host":["{{baseUrl}}"],"path":["api","ledger","accounts",":nominalCode"],"variable":[{"key":"nominalCode","value":"6","description":"Nominal code id"}]},"description":"GL account drill-down header (the nominal code detail page): the account's identity plus debit/credit/net totals and line count. Accepts the SAME filter[...] and `filter_groups` params as the lines endpoint, so the KPIs always describe exactly what the lines table shows.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Filterable columns (filter_groups):** `entry_reference`, `entry_type`, `line_direction`, `line_description`, `currency` (text operators); `amount`, `entry_id`, `id` (numeric operators); `effective_at` (date operators). Unregistered columns are rejected with HTTP 400.\n\n`account.posted_lines_count` is the UNfiltered count of live journal lines on the account (independent of the filter params) — the type editor uses it to decide whether a reclassification is guarded.\n\n`account.is_system_reserved` is true when the account is a control account mapped in the accounting settings (A/R, A/P, inventory control, …) — its type is locked and the editor disables the control.\n\n`account.integration_account` is the connected accounting-provider account (Xero or QBO) this nominal code maps to, or null when no provider is connected or no match exists. `link_basis` is `mapping` for an explicit polymorphic mapping (QBO always sets it; Xero sets it when the chart was mapped through the onboarding wizard) or `code` when inferred by matching the account code (xero_accounts.Code ⇄ nominal_codes.code — the fallback Xero's sync uses for an adopted chart). The ledger account page renders the provider account inline and, for Xero, deep-links to /v2/integrations/xero/accounts/{integration_account.id}."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/accounts/{nominalCode}","host":["{{baseUrl}}"],"path":["api","ledger","accounts",":nominalCode"],"variable":[{"key":"nominalCode","value":"6","description":"Nominal code id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"account\": {\n      \"id\": 6,\n      \"code\": \"1200\",\n      \"name\": \"Accounts Receivable\",\n      \"type\": \"Current\",\n      \"posted_lines_count\": 16,\n      \"is_system_reserved\": true,\n      \"archived_at\": null,\n      \"integration_account\": {\n        \"provider\": \"xero\",\n        \"provider_label\": \"Xero\",\n        \"id\": 61,\n        \"external_id\": \"2c8b3e10-7f4a-4d2e-9b1c-5a6f8e0d1234\",\n        \"code\": \"1200\",\n        \"name\": \"Accounts Receivable\",\n        \"type\": \"CURRENT\",\n        \"class\": \"ASSET\",\n        \"status\": \"ACTIVE\",\n        \"link_basis\": \"code\"\n      }\n    },\n    \"totals\": {\n      \"total_debit\": \"44545.4100\",\n      \"total_credit\": \"0.0000\",\n      \"net\": \"44545.4100\",\n      \"lines_count\": 16\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"List Account Lines","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/accounts/{nominalCode}/lines","host":["{{baseUrl}}"],"path":["api","ledger","accounts",":nominalCode","lines"],"variable":[{"key":"nominalCode","value":"6","description":"Nominal code id"}]},"description":"Paginated journal lines hitting one GL account — every line of every non-discarded entry, reversals included (originals and reversal mirrors both appear and net to zero, as a general ledger account view should read).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Quick filters:** `filter[direction]` (debit|credit), `filter[type]` (entry type), `filter[date_from]` / `filter[date_to]` (m/d/Y on entry effective date), `filter[search]` (entry reference + descriptions).\n**Advanced filters (`filter_groups`):** same columns as Get Account Overview.\n**Sorts:** id, amount, direction, effective_at, entry (reference), entry_type, debit, credit — default -effective_at."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/accounts/{nominalCode}/lines","host":["{{baseUrl}}"],"path":["api","ledger","accounts",":nominalCode","lines"],"variable":[{"key":"nominalCode","value":"6","description":"Nominal code id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 91,\n      \"direction\": \"debit\",\n      \"amount\": \"5081.13\",\n      \"tax_amount\": null,\n      \"description\": \"Accounts receivable\",\n      \"entry\": {\n        \"id\": 24,\n        \"reference\": \"SALES_ORDER_INVOICE-2671\",\n        \"reference_suffix\": 0,\n        \"type\": {\n          \"value\": \"sales_order_invoice\",\n          \"label\": \"Sales Order Invoice\"\n        },\n        \"status\": \"draft\",\n        \"effective_at\": \"2026-06-08T00:00:00+00:00\",\n        \"currency_code\": \"USD\"\n      },\n      \"source\": {\n        \"type\": \"App\\\\Models\\\\SalesOrder\",\n        \"id\": 2671,\n        \"label\": \"SO-2671\",\n        \"link_type_label\": \"Sales Order\",\n        \"link_route\": \"/orders/sales-orders/2671\",\n        \"link_legacy_url\": null,\n        \"url\": \"/orders/sales-orders/2671\"\n      }\n    }\n  ],\n  \"per_page\": 10,\n  \"total\": 16\n}"}]},{"name":"Update Account Type","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/accounts/{nominalCode}/type","host":["{{baseUrl}}"],"path":["api","ledger","accounts",":nominalCode","type"],"variable":[{"key":"nominalCode","value":"6","description":"Nominal code id"}]},"description":"Reclassify a GL account's type (the account-class enum).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token. Gated by the `accounting.manage_settings` permission.\n\n**Body:** `type` (required) — one of: Revenue, Expense, Asset, Liability, Equity, Bank, Current, Inventory, Fixed, Directcosts, Currliab, Termliab, Sales.\n\n**Tiered guardrail (mirrors NetSuite/SAP/QBO/Xero):**\n1. No-op (same type) → always allowed.\n2. System/reserved control account (mapped in accounting settings — A/R, A/P, inventory control, COGS, sales tax) → BLOCKED regardless of posting (422). Re-map the setting to a different account instead.\n3. No posted journal lines → the type may change to anything.\n4. Posted lines exist → the new type must stay in the SAME reclassification group (same statement side AND same normal balance): same-group changes (e.g. Current → Fixed asset, Liability → Equity) are allowed; changes that cross the Balance Sheet ↔ Income Statement boundary or flip the normal balance (e.g. Asset → Expense) are rejected with 422.\n\nReturns the same shape as Get Account Overview so the page header re-renders in place. The change is recorded in the account's activity log.","body":{"mode":"raw","raw":"{\n  \"type\": \"Fixed\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/ledger/accounts/{nominalCode}/type","host":["{{baseUrl}}"],"path":["api","ledger","accounts",":nominalCode","type"],"variable":[{"key":"nominalCode","value":"6","description":"Nominal code id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"account\": {\n      \"id\": 6,\n      \"code\": \"1500\",\n      \"name\": \"Office Equipment\",\n      \"type\": \"Fixed\",\n      \"posted_lines_count\": 4,\n      \"is_system_reserved\": false,\n      \"archived_at\": null\n    },\n    \"totals\": {\n      \"total_debit\": \"12000.0000\",\n      \"total_credit\": \"0.0000\",\n      \"net\": \"12000.0000\",\n      \"lines_count\": 4\n    }\n  },\n  \"message\": \"Account type updated.\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Ledger Account Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/accounts/{nominalCode}/activity-log","host":["{{baseUrl}}"],"path":["api","ledger","accounts",":nominalCode","activity-log"],"variable":[{"key":"nominalCode","value":"48","description":"Ledger (nominal) account ID"}]},"description":"Returns a paginated audit trail for a single general-ledger account, newest first. Each entry records the event, the affected record, a snapshot of changed properties, who made the change, and when. Supports free-text search across the actor name, event and properties.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/accounts/{nominalCode}/activity-log","host":["{{baseUrl}}"],"path":["api","ledger","accounts",":nominalCode","activity-log"],"variable":[{"key":"nominalCode","value":"48","description":"Ledger (nominal) account ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 771204,\n      \"description\": \"updated\",\n      \"event\": \"updated\",\n      \"subject_type\": \"NominalCode\",\n      \"subject_id\": 48,\n      \"properties\": {\n        \"old\": {\n          \"account_type\": \"asset\"\n        },\n        \"attributes\": {\n          \"account_type\": \"expense\"\n        }\n      },\n      \"causer_name\": \"Jane Accountant\",\n      \"created_at\": \"2026-07-05T09:12:44Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Link Provider Account","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/accounts/{nominalCode}/integration-account","host":["{{baseUrl}}"],"path":["api","ledger","accounts",":nominalCode","integration-account"],"variable":[{"key":"nominalCode","value":"6","description":"Nominal code id"}]},"description":"Point a GL account at a specific provider account in your connected accounting system (QuickBooks Online or Xero). Future syncs post this account's journal lines to the linked provider account; entries already synced are not moved.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token. Gated by the `accounting.manage_settings` permission.\n\n**Body:**\n- `provider` (required) — `qbo` or `xero`.\n- `provider_account_id` (required) — id of the imported provider account row (from the provider's accounts list).\n- `acknowledge_type_mismatch` (optional, boolean) — required `true` when the provider account's classification (Asset/Liability/Equity/Revenue/Expense) conflicts with the GL account's type; the request is rejected with 422 on `acknowledge_type_mismatch` until confirmed.\n\n**Guardrails (422):** the provider account must exist; Xero inventory-type accounts are never linkable; the account mapped as Inventory Control only accepts an asset-class provider account.\n\nReturns the same shape as Get Account Overview. The change is recorded in the account's activity log.","body":{"mode":"raw","raw":"{\n  \"provider\": \"qbo\",\n  \"provider_account_id\": 123,\n  \"acknowledge_type_mismatch\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/ledger/accounts/{nominalCode}/integration-account","host":["{{baseUrl}}"],"path":["api","ledger","accounts",":nominalCode","integration-account"],"variable":[{"key":"nominalCode","value":"6","description":"Nominal code id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"account\": {\n      \"id\": 6,\n      \"code\": \"5000\",\n      \"name\": \"Cost of Goods Sold\",\n      \"type\": \"Directcosts\",\n      \"posted_lines_count\": 12,\n      \"is_system_reserved\": true,\n      \"archived_at\": null,\n      \"integration_account\": {\n        \"provider\": \"qbo\",\n        \"provider_label\": \"QuickBooks Online\",\n        \"id\": 123,\n        \"external_id\": \"82\",\n        \"code\": \"5000\",\n        \"name\": \"Cost of Goods Sold\",\n        \"type\": \"Cost of Goods Sold\",\n        \"class\": \"Expense\",\n        \"status\": \"ACTIVE\",\n        \"link_basis\": \"mapping\"\n      }\n    },\n    \"totals\": {\n      \"total_debit\": \"1250.00\",\n      \"total_credit\": \"0.00\",\n      \"net\": \"1250.00\",\n      \"lines_count\": 12\n    }\n  },\n  \"message\": \"Provider account linked — future syncs post there.\"\n}"}]},{"name":"Unlink Provider Account","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/accounts/{nominalCode}/integration-account","host":["{{baseUrl}}"],"path":["api","ledger","accounts",":nominalCode","integration-account"],"variable":[{"key":"nominalCode","value":"6","description":"Nominal code id"}]},"description":"Remove a GL account's explicit provider-account link. Syncing falls back to the provider's implicit matching (QuickBooks Online: exact account-name match; Xero: account-code match). If nothing matches, entries posting to this account fail to sync until the account is linked again.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token. Gated by the `accounting.manage_settings` permission.\n\nReturns the same shape as Get Account Overview."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/ledger/accounts/{nominalCode}/integration-account","host":["{{baseUrl}}"],"path":["api","ledger","accounts",":nominalCode","integration-account"],"variable":[{"key":"nominalCode","value":"6","description":"Nominal code id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"account\": {\n      \"id\": 6,\n      \"code\": \"5000\",\n      \"name\": \"Cost of Goods Sold\",\n      \"type\": \"Directcosts\",\n      \"posted_lines_count\": 12,\n      \"is_system_reserved\": true,\n      \"archived_at\": null,\n      \"integration_account\": null\n    },\n    \"totals\": {\n      \"total_debit\": \"1250.00\",\n      \"total_credit\": \"0.00\",\n      \"net\": \"1250.00\",\n      \"lines_count\": 12\n    }\n  },\n  \"message\": \"Provider account link removed.\"\n}"}]},{"name":"Get Accounting Dimensions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/accounting-dimensions","host":["{{baseUrl}}"],"path":["api","ledger","accounting-dimensions"]},"description":"Read the tracking-category (Xero) / class (QBO) settings for the accounting connection: the active provider, its dimension capabilities, the mirrored tracking categories/options (or classes) available to map, the sales channels (including a synthetic \"SKU.io (internal)\" pseudo-channel for internal orders), and the saved per-channel slot configuration. Used to build P&L segmentation such as COGS by sales channel.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\nReturns 404 when no accounting connection is configured. For QBO the `capabilities` object also carries `class_tracking_enabled`, `class_tracking_per_line`, and `plan_supports_classes`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/accounting-dimensions","host":["{{baseUrl}}"],"path":["api","ledger","accounting-dimensions"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"connection_id\": 7,\n    \"provider\": \"xero\",\n    \"capabilities\": {\n      \"max_slots\": 2,\n      \"dimension_types\": [\n        \"xero_tracking_category\"\n      ],\n      \"class_tracking_enabled\": null,\n      \"class_tracking_per_line\": null,\n      \"plan_supports_classes\": null,\n      \"write_scope_available\": true,\n      \"warnings\": []\n    },\n    \"dimensions\": [\n      {\n        \"mirror_id\": 12,\n        \"name\": \"Sales Channel\",\n        \"status\": \"ACTIVE\",\n        \"values\": [\n          {\n            \"mirror_id\": 33,\n            \"name\": \"Amazon US\",\n            \"status\": \"ACTIVE\"\n          },\n          {\n            \"mirror_id\": 34,\n            \"name\": \"Shopify\",\n            \"status\": \"ACTIVE\"\n          }\n        ]\n      }\n    ],\n    \"channels\": [\n      {\n        \"instance_id\": 5,\n        \"name\": \"Amazon US\",\n        \"sub_channels\": [\n          {\n            \"id\": 12,\n            \"name\": \"amazon.com\"\n          }\n        ]\n      },\n      {\n        \"instance_id\": null,\n        \"name\": \"SKU.io (internal)\",\n        \"sub_channels\": []\n      }\n    ],\n    \"config\": {\n      \"slots\": [\n        {\n          \"slot\": 1,\n          \"provider_dimension\": {\n            \"type\": \"xero_tracking_category\",\n            \"mirror_id\": 12\n          },\n          \"driver\": \"sales_channel\",\n          \"apply_to\": {\n            \"revenue\": true,\n            \"cogs\": true,\n            \"fba\": true\n          },\n          \"default_value_mirror_id\": null,\n          \"mappings\": [\n            {\n              \"channel_instance_id\": 5,\n              \"value_mirror_id\": 33,\n              \"sub_channel_overrides\": {\n                \"12\": 34\n              }\n            }\n          ]\n        }\n      ]\n    },\n    \"warnings\": [\n      {\n        \"slot\": 1,\n        \"type\": \"unmapped_channels\",\n        \"count\": 1\n      }\n    ]\n  }\n}"}]},{"name":"Update Accounting Dimensions","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/accounting-dimensions","host":["{{baseUrl}}"],"path":["api","ledger","accounting-dimensions"]},"description":"Save the per-channel slot configuration. Up to two dimension slots are allowed; each slot must reference a distinct provider dimension (a 422 is returned otherwise).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Slot fields:**\n- `slot` — 1 or 2\n- `provider_dimension.type` — `xero_tracking_category` or `qbo_class`\n- `provider_dimension.mirror_id` — the local id of the chosen dimension\n- `driver` — `sales_channel`\n- `apply_to` — object with `revenue`, `cogs`, `fba` booleans (which document families are tagged)\n- `default_value_mirror_id` — fallback value id applied to unmapped channels (null to leave them untagged)\n- `mappings[]` — one row per channel: `channel_instance_id` (null for the internal pseudo-channel), `value_mirror_id`, and optional `sub_channel_overrides` keyed by sub-channel id\n\nReturns the saved configuration with recomputed warnings.","body":{"mode":"raw","raw":"{\n  \"slots\": [\n    {\n      \"slot\": 1,\n      \"provider_dimension\": {\n        \"type\": \"xero_tracking_category\",\n        \"mirror_id\": 12\n      },\n      \"driver\": \"sales_channel\",\n      \"apply_to\": {\n        \"revenue\": true,\n        \"cogs\": true,\n        \"fba\": true\n      },\n      \"default_value_mirror_id\": null,\n      \"mappings\": [\n        {\n          \"channel_instance_id\": 5,\n          \"value_mirror_id\": 33,\n          \"sub_channel_overrides\": {}\n        }\n      ]\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/ledger/accounting-dimensions","host":["{{baseUrl}}"],"path":["api","ledger","accounting-dimensions"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"provider\": \"xero\",\n    \"capabilities\": {\n      \"max_slots\": 2,\n      \"dimension_types\": [\n        \"xero_tracking_category\"\n      ],\n      \"class_tracking_enabled\": null,\n      \"class_tracking_per_line\": null,\n      \"plan_supports_classes\": null,\n      \"write_scope_available\": true,\n      \"warnings\": []\n    },\n    \"dimensions\": [\n      {\n        \"mirror_id\": 12,\n        \"name\": \"Sales Channel\",\n        \"status\": \"ACTIVE\",\n        \"values\": [\n          {\n            \"mirror_id\": 33,\n            \"name\": \"Amazon US\",\n            \"status\": \"ACTIVE\"\n          },\n          {\n            \"mirror_id\": 34,\n            \"name\": \"Shopify\",\n            \"status\": \"ACTIVE\"\n          }\n        ]\n      }\n    ],\n    \"channels\": [\n      {\n        \"instance_id\": 5,\n        \"name\": \"Amazon US\",\n        \"sub_channels\": [\n          {\n            \"id\": 12,\n            \"name\": \"amazon.com\"\n          }\n        ]\n      },\n      {\n        \"instance_id\": null,\n        \"name\": \"SKU.io (internal)\",\n        \"sub_channels\": []\n      }\n    ],\n    \"config\": {\n      \"slots\": [\n        {\n          \"slot\": 1,\n          \"provider_dimension\": {\n            \"type\": \"xero_tracking_category\",\n            \"mirror_id\": 12\n          },\n          \"driver\": \"sales_channel\",\n          \"apply_to\": {\n            \"revenue\": true,\n            \"cogs\": true,\n            \"fba\": true\n          },\n          \"default_value_mirror_id\": null,\n          \"mappings\": [\n            {\n              \"channel_instance_id\": 5,\n              \"value_mirror_id\": 33,\n              \"sub_channel_overrides\": {\n                \"12\": 34\n              }\n            }\n          ]\n        }\n      ]\n    },\n    \"warnings\": [\n      {\n        \"slot\": 1,\n        \"type\": \"unmapped_channels\",\n        \"count\": 1\n      }\n    ]\n  }\n}"}]},{"name":"Refresh Accounting Dimensions","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/accounting-dimensions/refresh","host":["{{baseUrl}}"],"path":["api","ledger","accounting-dimensions","refresh"]},"description":"Re-pull the connected provider's tracking categories/options (Xero) or classes (QBO) into the local mirror, then return the refreshed settings payload. Runs synchronously; the datasets are small.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\nReturns 502 when the provider could not be reached."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/accounting-dimensions/refresh","host":["{{baseUrl}}"],"path":["api","ledger","accounting-dimensions","refresh"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"provider\": \"xero\",\n    \"capabilities\": {\n      \"max_slots\": 2,\n      \"dimension_types\": [\n        \"xero_tracking_category\"\n      ],\n      \"class_tracking_enabled\": null,\n      \"class_tracking_per_line\": null,\n      \"plan_supports_classes\": null,\n      \"write_scope_available\": true,\n      \"warnings\": []\n    },\n    \"dimensions\": [\n      {\n        \"mirror_id\": 12,\n        \"name\": \"Sales Channel\",\n        \"status\": \"ACTIVE\",\n        \"values\": [\n          {\n            \"mirror_id\": 33,\n            \"name\": \"Amazon US\",\n            \"status\": \"ACTIVE\"\n          },\n          {\n            \"mirror_id\": 34,\n            \"name\": \"Shopify\",\n            \"status\": \"ACTIVE\"\n          }\n        ]\n      }\n    ],\n    \"channels\": [\n      {\n        \"instance_id\": 5,\n        \"name\": \"Amazon US\",\n        \"sub_channels\": [\n          {\n            \"id\": 12,\n            \"name\": \"amazon.com\"\n          }\n        ]\n      },\n      {\n        \"instance_id\": null,\n        \"name\": \"SKU.io (internal)\",\n        \"sub_channels\": []\n      }\n    ],\n    \"config\": {\n      \"slots\": [\n        {\n          \"slot\": 1,\n          \"provider_dimension\": {\n            \"type\": \"xero_tracking_category\",\n            \"mirror_id\": 12\n          },\n          \"driver\": \"sales_channel\",\n          \"apply_to\": {\n            \"revenue\": true,\n            \"cogs\": true,\n            \"fba\": true\n          },\n          \"default_value_mirror_id\": null,\n          \"mappings\": [\n            {\n              \"channel_instance_id\": 5,\n              \"value_mirror_id\": 33,\n              \"sub_channel_overrides\": {\n                \"12\": 34\n              }\n            }\n          ]\n        }\n      ]\n    },\n    \"warnings\": [\n      {\n        \"slot\": 1,\n        \"type\": \"unmapped_channels\",\n        \"count\": 1\n      }\n    ]\n  }\n}"}]},{"name":"Create Accounting Dimension Values","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/accounting-dimensions/values","host":["{{baseUrl}}"],"path":["api","ledger","accounting-dimensions","values"]},"description":"Create one or more tracking options (Xero) or classes (QBO) in the connected provider and mirror them locally. Names are matched case-insensitively against the mirror first, so an existing value is reused instead of duplicated.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Body:** `slot` (1 or 2) and `names` (array of value names, each ≤ 100 chars).\n\nReturns per-name results: `created`, `matched_existing`, and `failed` (each failure carries an `error` code such as `qbo_usage_limit` or `missing_scope` and a message). Partial success is expected.","body":{"mode":"raw","raw":"{\n  \"slot\": 1,\n  \"names\": [\n    \"Amazon US\",\n    \"eBay\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/accounting-dimensions/values","host":["{{baseUrl}}"],"path":["api","ledger","accounting-dimensions","values"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"created\": [\n      {\n        \"mirror_id\": 41,\n        \"name\": \"Amazon US\",\n        \"status\": \"ACTIVE\"\n      }\n    ],\n    \"matched_existing\": [\n      {\n        \"mirror_id\": 33,\n        \"name\": \"eBay\"\n      }\n    ],\n    \"failed\": [\n      {\n        \"name\": \"Retail\",\n        \"error\": \"qbo_usage_limit\",\n        \"message\": \"QuickBooks Plus allows 40 combined classes and locations.\"\n      }\n    ]\n  }\n}"}]},{"name":"Create Accounting Dimension","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/accounting-dimensions/dimensions","host":["{{baseUrl}}"],"path":["api","ledger","accounting-dimensions","dimensions"]},"description":"Create a new Xero tracking category in the connected organisation. Only succeeds when a free active category slot is available (Xero allows at most two active tracking categories).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Body:** `name` (≤ 100 chars).\n\nWhen both active category slots are already consumed, returns an `{ error: \"xero_category_slots_consumed\", message }` body.","body":{"mode":"raw","raw":"{\n  \"name\": \"Sales Channel\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/accounting-dimensions/dimensions","host":["{{baseUrl}}"],"path":["api","ledger","accounting-dimensions","dimensions"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"mirror_id\": 13,\n    \"name\": \"Sales Channel\",\n    \"status\": \"ACTIVE\"\n  }\n}"}]},{"name":"Get Tax Treatment","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/connections/{connectionId}/tax-treatment","host":["{{baseUrl}}"],"path":["api","ledger","connections",":connectionId","tax-treatment"],"variable":[{"key":"connectionId","value":"","description":"The connection ID."}]},"description":"Read the connection's tax treatment: the active mode, the organisation's country (captured from the provider and cached), the mode that country implies as a default, how many tax rates still lack a provider tax-code mapping, and the provider's available tax codes.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Modes:**\n- `provider_computed` — each document line declares its mapped provider tax code and the provider computes the tax. Required for filing regions (AU, NZ, UK, CA) where the tax return is prepared from the provider.\n- `posted_as_line` — documents are pushed without tax codes and the tax posts as a ledger line to the mapped Sales Tax account. The default, and standard for US organisations.\n\n`region_default` is the mode implied by `country_code`; `is_filing_region` is true for AU/NZ/UK/CA.\n\nReturns 404 when the connection id does not name the configured accounting connection."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/connections/{connectionId}/tax-treatment","host":["{{baseUrl}}"],"path":["api","ledger","connections",":connectionId","tax-treatment"],"variable":[{"key":"connectionId","value":"","description":"The connection ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tax_treatment\": {\n      \"connection_id\": 12,\n      \"provider\": \"xero\",\n      \"mode\": \"posted_as_line\",\n      \"region_default\": \"provider_computed\",\n      \"country_code\": \"AU\",\n      \"is_filing_region\": true,\n      \"unmapped_tax_rate_count\": 2,\n      \"modes\": [\n        {\n          \"value\": \"provider_computed\",\n          \"label\": \"Provider calculates tax\",\n          \"description\": \"Each document line declares its mapped tax rate and your accounting provider computes the tax. Required for BAS/GST/VAT filing regions.\"\n        },\n        {\n          \"value\": \"posted_as_line\",\n          \"label\": \"Post tax as a ledger line\",\n          \"description\": \"Documents are pushed without tax codes and the tax posts as a ledger line to your mapped Sales Tax account. Standard for US organisations.\"\n        }\n      ],\n      \"provider_tax_codes\": [\n        {\n          \"code\": \"OUTPUT\",\n          \"name\": \"GST on Income\",\n          \"rate\": 10\n        },\n        {\n          \"code\": \"EXEMPTOUTPUT\",\n          \"name\": \"GST Free Income\",\n          \"rate\": 0\n        },\n        {\n          \"code\": \"INPUT\",\n          \"name\": \"GST on Expenses\",\n          \"rate\": 10\n        }\n      ]\n    }\n  }\n}"}]},{"name":"Update Tax Treatment","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/connections/{connectionId}/tax-treatment","host":["{{baseUrl}}"],"path":["api","ledger","connections",":connectionId","tax-treatment"],"variable":[{"key":"connectionId","value":"","description":"The connection ID."}]},"description":"Set who computes the tax on documents pushed to this connection.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Body fields:**\n- `mode` (string, required) — `provider_computed` or `posted_as_line`.\n\nForward-only: changing the mode never re-pushes documents that already synced — it only affects how the next document is built. In `provider_computed` mode, a document whose line carries an unmapped tax rate will fail to sync with an actionable error until the rate is mapped.\n\nReturns the refreshed tax-treatment state. Returns 404 when the connection id does not name the configured accounting connection; 422 when the mode is not a known option.","body":{"mode":"raw","raw":"{\n  \"mode\": \"provider_computed\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/ledger/connections/{connectionId}/tax-treatment","host":["{{baseUrl}}"],"path":["api","ledger","connections",":connectionId","tax-treatment"],"variable":[{"key":"connectionId","value":"","description":"The connection ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tax_treatment\": {\n      \"connection_id\": 12,\n      \"provider\": \"xero\",\n      \"mode\": \"posted_as_line\",\n      \"region_default\": \"provider_computed\",\n      \"country_code\": \"AU\",\n      \"is_filing_region\": true,\n      \"unmapped_tax_rate_count\": 2,\n      \"modes\": [\n        {\n          \"value\": \"provider_computed\",\n          \"label\": \"Provider calculates tax\",\n          \"description\": \"Each document line declares its mapped tax rate and your accounting provider computes the tax. Required for BAS/GST/VAT filing regions.\"\n        },\n        {\n          \"value\": \"posted_as_line\",\n          \"label\": \"Post tax as a ledger line\",\n          \"description\": \"Documents are pushed without tax codes and the tax posts as a ledger line to your mapped Sales Tax account. Standard for US organisations.\"\n        }\n      ],\n      \"provider_tax_codes\": [\n        {\n          \"code\": \"OUTPUT\",\n          \"name\": \"GST on Income\",\n          \"rate\": 10\n        },\n        {\n          \"code\": \"EXEMPTOUTPUT\",\n          \"name\": \"GST Free Income\",\n          \"rate\": 0\n        },\n        {\n          \"code\": \"INPUT\",\n          \"name\": \"GST on Expenses\",\n          \"rate\": 10\n        }\n      ]\n    }\n  },\n  \"message\": \"Tax treatment saved.\"\n}"}]},{"name":"List Tax Rate Mappings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/connections/{connectionId}/tax-rate-mappings","host":["{{baseUrl}}"],"path":["api","ledger","connections",":connectionId","tax-rate-mappings"],"variable":[{"key":"connectionId","value":"","description":"The connection ID."}]},"description":"One row per active tax rate with its current provider mapping state, plus the provider tax codes available to map to.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\nA rate whose mapped provider code was archived or deactivated in the provider reads as unmapped until re-selected. `unmapped_tax_rate_count` counts the rows where `mapped` is false.\n\nMap zero-rate and exempt rates to their real exempt codes (e.g. GST Free Income → EXEMPTOUTPUT) — on a tax return, exempt is not the same as untaxed.\n\nReturns 404 when the connection id does not name the configured accounting connection."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/connections/{connectionId}/tax-rate-mappings","host":["{{baseUrl}}"],"path":["api","ledger","connections",":connectionId","tax-rate-mappings"],"variable":[{"key":"connectionId","value":"","description":"The connection ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tax_rate_mappings\": {\n      \"connection_id\": 12,\n      \"provider\": \"xero\",\n      \"tax_rates\": [\n        {\n          \"tax_rate_id\": 3,\n          \"name\": \"GST Free Income\",\n          \"rate\": 0,\n          \"provider_tax_code\": \"EXEMPTOUTPUT\",\n          \"provider_tax_code_name\": \"GST Free Income\",\n          \"mapped\": true\n        },\n        {\n          \"tax_rate_id\": 1,\n          \"name\": \"GST on Income\",\n          \"rate\": 10,\n          \"provider_tax_code\": \"OUTPUT\",\n          \"provider_tax_code_name\": \"GST on Income\",\n          \"mapped\": true\n        },\n        {\n          \"tax_rate_id\": 7,\n          \"name\": \"Not Taxable\",\n          \"rate\": 0,\n          \"provider_tax_code\": null,\n          \"provider_tax_code_name\": null,\n          \"mapped\": false\n        }\n      ],\n      \"provider_tax_codes\": [\n        {\n          \"code\": \"OUTPUT\",\n          \"name\": \"GST on Income\",\n          \"rate\": 10\n        },\n        {\n          \"code\": \"EXEMPTOUTPUT\",\n          \"name\": \"GST Free Income\",\n          \"rate\": 0\n        },\n        {\n          \"code\": \"INPUT\",\n          \"name\": \"GST on Expenses\",\n          \"rate\": 10\n        }\n      ],\n      \"unmapped_tax_rate_count\": 1\n    }\n  }\n}"}]},{"name":"Update Tax Rate Mapping","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/connections/{connectionId}/tax-rate-mappings/{taxRateId}","host":["{{baseUrl}}"],"path":["api","ledger","connections",":connectionId","tax-rate-mappings",":taxRateId"],"variable":[{"key":"connectionId","value":"","description":"The connection ID."},{"key":"taxRateId","value":"","description":"The tax rate ID."}]},"description":"Map one tax rate to a provider tax code, or clear the mapping.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Body fields:**\n- `provider_tax_code` (string or null, required key) — the provider tax code to map to (Xero: the tax type identifier, e.g. `OUTPUT`; QuickBooks: the tax code id). Send `null` to clear the mapping. The key must be present even when null.\n\nReturns the refreshed mapping rows. Returns 404 when the connection id does not name the configured accounting connection; 422 when the code is not in the provider's downloaded tax codes, or is archived/inactive in the provider (refresh the codes and retry).","body":{"mode":"raw","raw":"{\n  \"provider_tax_code\": \"OUTPUT\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/ledger/connections/{connectionId}/tax-rate-mappings/{taxRateId}","host":["{{baseUrl}}"],"path":["api","ledger","connections",":connectionId","tax-rate-mappings",":taxRateId"],"variable":[{"key":"connectionId","value":"","description":"The connection ID."},{"key":"taxRateId","value":"","description":"The tax rate ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tax_rate_mappings\": {\n      \"connection_id\": 12,\n      \"provider\": \"xero\",\n      \"tax_rates\": [\n        {\n          \"tax_rate_id\": 3,\n          \"name\": \"GST Free Income\",\n          \"rate\": 0,\n          \"provider_tax_code\": \"EXEMPTOUTPUT\",\n          \"provider_tax_code_name\": \"GST Free Income\",\n          \"mapped\": true\n        },\n        {\n          \"tax_rate_id\": 1,\n          \"name\": \"GST on Income\",\n          \"rate\": 10,\n          \"provider_tax_code\": \"OUTPUT\",\n          \"provider_tax_code_name\": \"GST on Income\",\n          \"mapped\": true\n        },\n        {\n          \"tax_rate_id\": 7,\n          \"name\": \"Not Taxable\",\n          \"rate\": 0,\n          \"provider_tax_code\": null,\n          \"provider_tax_code_name\": null,\n          \"mapped\": false\n        }\n      ],\n      \"provider_tax_codes\": [\n        {\n          \"code\": \"OUTPUT\",\n          \"name\": \"GST on Income\",\n          \"rate\": 10\n        },\n        {\n          \"code\": \"EXEMPTOUTPUT\",\n          \"name\": \"GST Free Income\",\n          \"rate\": 0\n        },\n        {\n          \"code\": \"INPUT\",\n          \"name\": \"GST on Expenses\",\n          \"rate\": 10\n        }\n      ],\n      \"unmapped_tax_rate_count\": 1\n    }\n  },\n  \"message\": \"Tax rate mapping saved.\"\n}"}]},{"name":"Refresh Provider Tax Codes","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/connections/{connectionId}/tax-rate-mappings/refresh","host":["{{baseUrl}}"],"path":["api","ledger","connections",":connectionId","tax-rate-mappings","refresh"],"variable":[{"key":"connectionId","value":"","description":"The connection ID."}]},"description":"Re-pull the provider's tax codes (and the organisation's country) into the local mirror, then return the refreshed mapping rows.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\nThe pull is upsert-only — a provider failure mid-refresh never wipes previously downloaded codes or existing mappings. When the provider is unreachable the endpoint returns 502 and the last-known codes remain in effect.\n\nReturns 404 when the connection id does not name the configured accounting connection."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/connections/{connectionId}/tax-rate-mappings/refresh","host":["{{baseUrl}}"],"path":["api","ledger","connections",":connectionId","tax-rate-mappings","refresh"],"variable":[{"key":"connectionId","value":"","description":"The connection ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tax_rate_mappings\": {\n      \"connection_id\": 12,\n      \"provider\": \"xero\",\n      \"tax_rates\": [\n        {\n          \"tax_rate_id\": 3,\n          \"name\": \"GST Free Income\",\n          \"rate\": 0,\n          \"provider_tax_code\": \"EXEMPTOUTPUT\",\n          \"provider_tax_code_name\": \"GST Free Income\",\n          \"mapped\": true\n        },\n        {\n          \"tax_rate_id\": 1,\n          \"name\": \"GST on Income\",\n          \"rate\": 10,\n          \"provider_tax_code\": \"OUTPUT\",\n          \"provider_tax_code_name\": \"GST on Income\",\n          \"mapped\": true\n        },\n        {\n          \"tax_rate_id\": 7,\n          \"name\": \"Not Taxable\",\n          \"rate\": 0,\n          \"provider_tax_code\": null,\n          \"provider_tax_code_name\": null,\n          \"mapped\": false\n        }\n      ],\n      \"provider_tax_codes\": [\n        {\n          \"code\": \"OUTPUT\",\n          \"name\": \"GST on Income\",\n          \"rate\": 10\n        },\n        {\n          \"code\": \"EXEMPTOUTPUT\",\n          \"name\": \"GST Free Income\",\n          \"rate\": 0\n        },\n        {\n          \"code\": \"INPUT\",\n          \"name\": \"GST on Expenses\",\n          \"rate\": 10\n        }\n      ],\n      \"unmapped_tax_rate_count\": 1\n    }\n  },\n  \"message\": \"Provider tax codes refreshed.\"\n}"}]},{"name":"Auto-Map Tax Rates","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/connections/{connectionId}/tax-rate-mappings/auto-map","host":["{{baseUrl}}"],"path":["api","ledger","connections",":connectionId","tax-rate-mappings","auto-map"],"variable":[{"key":"connectionId","value":"","description":"The connection ID."}]},"description":"Suggest a provider tax code for every tax rate that does not have a valid one yet.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Saves nothing.** The response returns the unchanged mapping rows plus a `proposals` array to review; apply the ones you accept with `POST .../tax-rate-mappings/apply`.\n\n**Proposal fields:**\n- `confidence` (number) - 0 to 1.\n- `reason` (string) - `exact_name` (identical names), `rate` (the only provider code at that percentage), `rate_and_name` (several codes share the percentage, resolved by the closest name), or `name_similarity`.\n- `is_certain` (boolean) - true only for `exact_name` and `rate`. An ambiguous percentage is never certain, because two codes at the same rate can mean opposite things (tax on income vs tax on purchases).\n\nRates that are already mapped are left alone. Returns 404 when the connection id does not name the configured accounting connection."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/connections/{connectionId}/tax-rate-mappings/auto-map","host":["{{baseUrl}}"],"path":["api","ledger","connections",":connectionId","tax-rate-mappings","auto-map"],"variable":[{"key":"connectionId","value":"","description":"The connection ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tax_rate_mappings\": {\n      \"connection_id\": 12,\n      \"provider\": \"xero\",\n      \"tax_rates\": [\n        {\n          \"tax_rate_id\": 3,\n          \"name\": \"GST on Income\",\n          \"rate\": 10,\n          \"provider_tax_code\": \"OUTPUT\",\n          \"provider_tax_code_name\": \"GST on Income\",\n          \"provider_tax_code_id\": 41,\n          \"mapped\": true\n        },\n        {\n          \"tax_rate_id\": 4,\n          \"name\": \"VAT on purchases\",\n          \"rate\": 20,\n          \"provider_tax_code\": null,\n          \"provider_tax_code_name\": null,\n          \"provider_tax_code_id\": null,\n          \"mapped\": false\n        }\n      ],\n      \"provider_tax_codes\": [\n        {\n          \"code\": \"OUTPUT\",\n          \"name\": \"GST on Income\",\n          \"rate\": 10,\n          \"id\": 41\n        },\n        {\n          \"code\": \"INPUT2\",\n          \"name\": \"VAT on purchases 2\",\n          \"rate\": 20,\n          \"id\": 47\n        }\n      ],\n      \"unmapped_tax_rate_count\": 1,\n      \"proposals\": [\n        {\n          \"tax_rate_id\": 4,\n          \"tax_rate_name\": \"VAT on purchases\",\n          \"tax_rate\": 20,\n          \"provider_tax_code\": \"INPUT2\",\n          \"provider_tax_code_name\": \"VAT on purchases 2\",\n          \"confidence\": 0.869,\n          \"reason\": \"rate_and_name\",\n          \"is_certain\": false\n        }\n      ]\n    }\n  },\n  \"message\": \"1 suggested mapping(s) - review them before saving.\"\n}"}]},{"name":"Apply Tax Rate Mappings","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/connections/{connectionId}/tax-rate-mappings/apply","host":["{{baseUrl}}"],"path":["api","ledger","connections",":connectionId","tax-rate-mappings","apply"],"variable":[{"key":"connectionId","value":"","description":"The connection ID."}]},"description":"Save several tax rate mappings at once - the companion save for Auto-Map.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Body fields:**\n- `mappings` (array, required, min 1) - the pairings to save.\n- `mappings[].tax_rate_id` (integer, required) - the tax rate to map.\n- `mappings[].provider_tax_code` (string or null, required key) - the provider tax code (Xero: the tax type identifier, e.g. `OUTPUT`; QuickBooks: the tax code id). Send `null` to clear that mapping. The key must be present even when null.\n\nAll or nothing: if any code is unknown or archived in the provider the whole set is rejected and nothing is saved. Returns the refreshed mapping rows. Returns 404 when the connection id does not name the configured accounting connection; 422 on an unknown tax rate or provider code.","body":{"mode":"raw","raw":"{\n  \"mappings\": [\n    {\n      \"tax_rate_id\": 3,\n      \"provider_tax_code\": \"OUTPUT\"\n    },\n    {\n      \"tax_rate_id\": 4,\n      \"provider_tax_code\": \"INPUT2\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/connections/{connectionId}/tax-rate-mappings/apply","host":["{{baseUrl}}"],"path":["api","ledger","connections",":connectionId","tax-rate-mappings","apply"],"variable":[{"key":"connectionId","value":"","description":"The connection ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tax_rate_mappings\": {\n      \"connection_id\": 12,\n      \"provider\": \"xero\",\n      \"tax_rates\": [\n        {\n          \"tax_rate_id\": 3,\n          \"name\": \"GST on Income\",\n          \"rate\": 10,\n          \"provider_tax_code\": \"OUTPUT\",\n          \"provider_tax_code_name\": \"GST on Income\",\n          \"provider_tax_code_id\": 41,\n          \"mapped\": true\n        },\n        {\n          \"tax_rate_id\": 4,\n          \"name\": \"VAT on purchases\",\n          \"rate\": 20,\n          \"provider_tax_code\": \"INPUT2\",\n          \"provider_tax_code_name\": \"VAT on purchases 2\",\n          \"provider_tax_code_id\": 47,\n          \"mapped\": true\n        }\n      ],\n      \"provider_tax_codes\": [\n        {\n          \"code\": \"OUTPUT\",\n          \"name\": \"GST on Income\",\n          \"rate\": 10,\n          \"id\": 41\n        },\n        {\n          \"code\": \"INPUT2\",\n          \"name\": \"VAT on purchases 2\",\n          \"rate\": 20,\n          \"id\": 47\n        }\n      ],\n      \"unmapped_tax_rate_count\": 0\n    }\n  },\n  \"message\": \"2 tax rate mapping(s) saved.\"\n}"}]},{"name":"Get Granularity Matrix","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/settings/granularity","host":["{{baseUrl}}"],"path":["api","ledger","settings","granularity"]},"description":"Read the type × channel posting granularity matrix stored on the accounting connection (— the Cin7-style settings matrix), consumed by BatchGranularityResolver.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Matrix shape:** `{ \"<type>\": { \"default\": \"<granularity>\", \"channels\": { \"<integration instance id>\": \"<granularity>\" } } }` where granularity is one of:\n- `off` — the type generates no synced accounting output for the channel\n- `per_document` — every source document syncs as its own journal entry\n- `daily` — documents aggregate into one batch per local day\n- `monthly` — documents aggregate into one batch per local month\n\nAn empty object means no overrides are stored — legacy per-type defaults apply. Also returns `connection_id` — the accounting connection the matrix lives on.\n\nReturns 404 when no accounting connection is configured."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/settings/granularity","host":["{{baseUrl}}"],"path":["api","ledger","settings","granularity"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"granularity_matrix\": {\n      \"sales_order_invoice\": {\n        \"default\": \"per_document\",\n        \"channels\": {\n          \"5\": \"daily\",\n          \"8\": \"off\"\n        }\n      },\n      \"sales_order_fulfillment\": {\n        \"default\": \"off\"\n      },\n      \"fba_shipments\": {\n        \"default\": \"monthly\",\n        \"channels\": {\n          \"8\": \"monthly\"\n        }\n      },\n      \"inventory_adjustment\": {\n        \"default\": \"per_document\"\n      }\n    },\n    \"connection_id\": 3\n  }\n}"}]},{"name":"Update Granularity Matrix","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/settings/granularity","host":["{{baseUrl}}"],"path":["api","ledger","settings","granularity"]},"description":"Validate, clamp and persist the type × channel granularity matrix. Returns the matrix AS SAVED — the client must re-render from the response because of the server-side clamp.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Request body fields:**\n- `matrix` (present, object) — MAY be an empty object `{}`: an empty matrix clears all stored overrides back to the legacy per-type defaults\n- `matrix.<type>` (object) — keyed by AccountingTransactionTypeEnum value (e.g. sales_order_invoice, fba_shipments); unknown types are rejected\n- `matrix.<type>.default` (required, string) — `off` | `per_document` | `daily` | `monthly`\n- `matrix.<type>.channels` (optional, object) — per-channel overrides keyed by EXISTING integration instance ID (string keys); unknown IDs are rejected\n- `matrix.<type>.channels.<id>` (string) — `off` | `per_document` | `daily` | `monthly`\n\n**Server-side FBA clamp:** FBA monthly-only types (fba_adjustments, fba_customer_returns, fba_receipts, fba_shipments, fba_vendor_returns, fba_warehouse_transfers, amazon_pending_inbound_shipment) are clamped server-side — `daily` / `per_document` become `monthly`; `off` survives. The clamp is never trusted to the client.\n\n**Validation errors (422):** unknown type key, invalid granularity value, non-object type/channels config, or non-existent channel integration instance ID — all reported under the `matrix` key.\n\nReturns 404 when no accounting connection is configured.\n\nPermission: requires `accounting.manage_settings` (granular permissions; admins bypass; unenforced when the tenant's granular_permissions feature flag is off). Returns 403 without it.","body":{"mode":"raw","raw":"{\n  \"matrix\": {\n    \"sales_order_invoice\": {\n      \"default\": \"per_document\",\n      \"channels\": {\n        \"5\": \"daily\",\n        \"8\": \"off\"\n      }\n    },\n    \"sales_order_fulfillment\": {\n      \"default\": \"off\"\n    },\n    \"fba_shipments\": {\n      \"default\": \"monthly\",\n      \"channels\": {\n        \"8\": \"monthly\"\n      }\n    },\n    \"inventory_adjustment\": {\n      \"default\": \"per_document\"\n    }\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/ledger/settings/granularity","host":["{{baseUrl}}"],"path":["api","ledger","settings","granularity"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"granularity_matrix\": {\n      \"sales_order_invoice\": {\n        \"default\": \"per_document\",\n        \"channels\": {\n          \"5\": \"daily\",\n          \"8\": \"off\"\n        }\n      },\n      \"sales_order_fulfillment\": {\n        \"default\": \"off\"\n      },\n      \"fba_shipments\": {\n        \"default\": \"monthly\",\n        \"channels\": {\n          \"8\": \"monthly\"\n        }\n      },\n      \"inventory_adjustment\": {\n        \"default\": \"per_document\"\n      }\n    }\n  },\n  \"message\": \"Granularity settings saved.\"\n}"}]},{"name":"Get Generation Status","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/settings/generation","host":["{{baseUrl}}"],"path":["api","ledger","settings","generation"]},"description":"Effective state of the ledger-generation kill switch plus outbox health (the Settings → Generation card).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Response fields:**\n- `enabled` — the EFFECTIVE state: tenant switch OR the environment override\n- `tenant_enabled` — the per-tenant switch (`AccountingSettings::$ledger_generation_enabled`)\n- `environment_override` — the `LEDGER_GENERATION_ENABLED` env flag; when true, generation runs for every tenant on the install regardless of the tenant switch\n- `generate_purchase_order_transactions` — per-tenant opt-in (`AccountingSettings::$generate_purchase_order_transactions`, default false) for the non-posting PURCHASE_ORDER commitment entry; off by default — when true a PURCHASE_ORDER commitment entry is generated per PO, when false (the default) the PURCHASE_ORDER posting rule returns null so no PO entries are generated (existing ones are left untouched)\n- `outbox.pending` — document changes waiting to be turned into ledger entries\n- `outbox.failed` — outbox rows that exhausted their retry budget (terminal)\n- `outbox.last_processed_at` — last time the generation worker completed a row (null = never)\n- `schedule.next_generation_run_at` — UTC datetime of the next per-minute outbox drain run\n- `schedule.next_reconcile_run_at` — UTC datetime of the next daily reconciliation sweep\n\nThe response also reports the live committed-spend bills (`committed_spend_bill_count`, `committed_spend_bill_total`, `tenant_currency_code`) so a client can state exactly what switching away from that policy would remove."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/settings/generation","host":["{{baseUrl}}"],"path":["api","ledger","settings","generation"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"generation\": {\n      \"enabled\": true,\n      \"tenant_enabled\": true,\n      \"environment_override\": false,\n      \"outbox\": {\n        \"pending\": 3,\n        \"failed\": 0,\n        \"last_processed_at\": \"2026-06-12 02:31:32\"\n      },\n      \"schedule\": {\n        \"next_generation_run_at\": \"2026-06-12 20:21:00\",\n        \"next_reconcile_run_at\": \"2026-06-13 00:00:00\"\n      },\n      \"purchase_order_accounting_policy\": \"commitment_entry\",\n      \"committed_spend_bill_count\": 1,\n      \"committed_spend_bill_total\": 772.5,\n      \"tenant_currency_code\": \"USD\"\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Update Generation Switch","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/settings/generation","host":["{{baseUrl}}"],"path":["api","ledger","settings","generation"]},"description":"Flip the per-tenant generation kill switch (`AccountingSettings::$ledger_generation_enabled`) and return the refreshed status.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.manage_settings` permission (403 without it).\n\n**Request body:**\n- `enabled` (boolean, required) — whether this tenant generates accounting entries\n\n**Note:** the `LEDGER_GENERATION_ENABLED` environment override is not writable from the API — disabling the tenant switch while the override is on leaves generation effectively running, which the response surfaces via `environment_override`.\n- `schedule.next_generation_run_at` — UTC datetime of the next per-minute outbox drain run\n- `schedule.next_reconcile_run_at` — UTC datetime of the next daily reconciliation sweep\n- `purchase_order_accounting_policy` (string, optional) — what a purchase order puts into the books. One of `on_invoice` (nothing posts until the supplier invoice is recorded — the default), `commitment_entry` (a non-posting commitment entry per order), or `committed_spend_bill` (a committed-spend bill when the order is submitted, which reduces as real supplier invoices arrive). Omit to leave unchanged. Replaces the former boolean `generate_purchase_order_transactions`.\n\nThe response also reports the live committed-spend bills (`committed_spend_bill_count`, `committed_spend_bill_total`, `tenant_currency_code`) so a client can state exactly what switching away from that policy would remove.","body":{"mode":"raw","raw":"{\n  \"enabled\": true,\n  \"purchase_order_accounting_policy\": \"commitment_entry\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/ledger/settings/generation","host":["{{baseUrl}}"],"path":["api","ledger","settings","generation"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"generation\": {\n      \"enabled\": true,\n      \"tenant_enabled\": true,\n      \"environment_override\": false,\n      \"outbox\": {\n        \"pending\": 3,\n        \"failed\": 0,\n        \"last_processed_at\": \"2026-06-12 02:31:32\"\n      },\n      \"schedule\": {\n        \"next_generation_run_at\": \"2026-06-12 20:21:00\",\n        \"next_reconcile_run_at\": \"2026-06-13 00:00:00\"\n      },\n      \"purchase_order_accounting_policy\": \"commitment_entry\",\n      \"committed_spend_bill_count\": 1,\n      \"committed_spend_bill_total\": 772.5,\n      \"tenant_currency_code\": \"USD\"\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"List Connections","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/connections","host":["{{baseUrl}}"],"path":["api","ledger","connections"]},"description":"The connections panel view-model: one row per supported accounting integration instance (QuickBooks Online / Xero).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Per-connection fields:**\n- `id` / `provider` (`qbo` | `xero`) / `name`\n- `health.sync_status` — the connection's sync status value; `health.paused` — true when sync is not active\n- `lock_date` — the imported provider books-closed lock date (Y-m-d), or null\n- `rate_budget_remaining` — Xero only: the remaining daily API budget from the X-DayLimit-Remaining header; null for QBO (no daily budget)\n- `last_inbound_event_at` — when the last inbound (webhook/CDC) event arrived, or null\n- `open_operations` — open sync-operation counts keyed by state (pending, queued, in_flight, failed_retryable, needs_action); empty object when no open work\n- `capabilities` — per-provider feature flags used to show/hide reconciliation lanes: `supports_trial_balance` (Starting Balances lane — true for both Xero and QuickBooks Online), `supports_bank_statement_lines` (Statement Match lane — Xero only), `supports_webhooks` (inbound change events — Xero only)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/connections","host":["{{baseUrl}}"],"path":["api","ledger","connections"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"connections\": [\n      {\n        \"id\": 3,\n        \"provider\": \"qbo\",\n        \"name\": \"QuickBooks Online\",\n        \"health\": {\n          \"sync_status\": \"active\",\n          \"paused\": false\n        },\n        \"lock_date\": \"2026-04-30\",\n        \"rate_budget_remaining\": null,\n        \"last_inbound_event_at\": \"2026-06-10T18:42:11+00:00\",\n        \"open_operations\": {\n          \"pending\": 4,\n          \"queued\": 2,\n          \"in_flight\": 1,\n          \"needs_action\": 3,\n          \"failed_retryable\": 1\n        },\n        \"capabilities\": {\n          \"supports_trial_balance\": true,\n          \"supports_bank_statement_lines\": false,\n          \"supports_webhooks\": false\n        }\n      },\n      {\n        \"id\": 7,\n        \"provider\": \"xero\",\n        \"name\": \"Xero AU\",\n        \"health\": {\n          \"sync_status\": \"paused\",\n          \"paused\": true\n        },\n        \"lock_date\": \"2026-03-31\",\n        \"rate_budget_remaining\": 4120,\n        \"last_inbound_event_at\": \"2026-06-09T02:15:40+00:00\",\n        \"open_operations\": {},\n        \"capabilities\": {\n          \"supports_trial_balance\": true,\n          \"supports_bank_statement_lines\": true,\n          \"supports_webhooks\": true\n        }\n      }\n    ]\n  }\n}"}]},{"name":"Get Setup Status","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/setup/status","host":["{{baseUrl}}"],"path":["api","ledger","setup","status"]},"description":"Accounting setup wizard state: which steps are done and what's left.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\nReturns `generation_enabled` (the per-tenant on/off switch), `has_chart` + `chart_count` (does a chart of accounts exist), `accounting_integration` ({provider:xero|qbo, name} or null), and `required_mappings` — the step 2 role list (`role`, `label`, `category` plus the default `code`/`name`/`type`), and `suggested_accounts` — the step 1 chart of accounts, each `code` defined once (`code`, `name`, `type`). Manual setup is two steps: edit the accounts, then map each role to one of them. Several roles can share an account. `warehouses` lists the stock-holding warehouses, for the optional step that gives a warehouse its own inventory account."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/setup/status","host":["{{baseUrl}}"],"path":["api","ledger","setup","status"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"setup\": {\n      \"generation_enabled\": false,\n      \"has_chart\": false,\n      \"chart_count\": 0,\n      \"accounting_integration\": null,\n      \"suggested_accounts\": [\n        {\n          \"code\": \"4000\",\n          \"name\": \"Sales\",\n          \"type\": \"Sales\"\n        },\n        {\n          \"code\": \"5000\",\n          \"name\": \"Cost of Goods Sold\",\n          \"type\": \"Directcosts\"\n        },\n        {\n          \"code\": \"2100\",\n          \"name\": \"Accrued Purchases\",\n          \"type\": \"Currliab\"\n        },\n        {\n          \"code\": \"6900\",\n          \"name\": \"Rounding\",\n          \"type\": \"Expense\"\n        }\n      ],\n      \"required_mappings\": [\n        {\n          \"role\": \"sales_orders\",\n          \"label\": \"Revenue recognised when you invoice a sales order.\",\n          \"category\": \"Sales & revenue\",\n          \"code\": \"4000\",\n          \"name\": \"Sales\",\n          \"type\": \"Sales\"\n        },\n        {\n          \"role\": \"shipping_sales_orders\",\n          \"label\": \"Shipping revenue charged to customers on sales orders.\",\n          \"category\": \"Sales & revenue\",\n          \"code\": \"4000\",\n          \"name\": \"Sales\",\n          \"type\": \"Sales\"\n        },\n        {\n          \"role\": \"cogs\",\n          \"label\": \"The inventory cost of items shipped — your cost of goods sold.\",\n          \"category\": \"Cost of goods sold\",\n          \"code\": \"5000\",\n          \"name\": \"Cost of Goods Sold\",\n          \"type\": \"Directcosts\"\n        }\n      ],\n      \"warehouses\": [\n        {\n          \"id\": 1,\n          \"name\": \"Main Warehouse\",\n          \"type\": \"direct\"\n        },\n        {\n          \"id\": 2,\n          \"name\": \"West Coast 3PL\",\n          \"type\": \"3pl\"\n        }\n      ]\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Apply Suggested Chart","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/setup/apply-suggested-chart","host":["{{baseUrl}}"],"path":["api","ledger","setup","apply-suggested-chart"]},"description":"Manual accounting setup in two parts: create the chart of accounts, then wire each NominalCodeMappingSettings role to one of those accounts. Gated by the `accounting.manage_settings` permission.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Body (both optional, sent together — omit both to apply SKU's default chart + assignments):**\n- `accounts` — the chart of accounts. Each: `code` (string), `name` (string), `type` (one of: Revenue, Expense, Asset, Liability, Equity, Bank, Current, Inventory, Fixed, Directcosts, Currliab, Termliab, Sales). Codes must be unique.\n- `assignments` — a `role` → account `code` map. Keys are NominalCodeMappingSettings roles (e.g. `sales_orders`, `cogs`, `inventory_control`, `accounts_payable`); each value is one of the account codes above. Several roles may share a code (e.g. `sales_orders` + `shipping_sales_orders` both on `4000`).\n- `warehouse_inventory_accounts` (optional) — a warehouse id → account `code` map giving a warehouse its own inventory account (posting resolves the inventory account per warehouse). Each code must be one of the accounts above; unknown warehouses are skipped.\n\nOnly allowed when the tenant has NO chart yet — otherwise 422 on `accounts`. Every assignment must reference an account in `accounts`, else 422 on `assignments`; a warehouse mapped to a missing code is 422 on `warehouse_inventory_accounts`.","body":{"mode":"raw","raw":"{\n  \"accounts\": [\n    {\n      \"code\": \"1000\",\n      \"name\": \"Inventory Control\",\n      \"type\": \"Current\"\n    },\n    {\n      \"code\": \"4000\",\n      \"name\": \"Sales\",\n      \"type\": \"Sales\"\n    },\n    {\n      \"code\": \"5000\",\n      \"name\": \"Cost of Goods Sold\",\n      \"type\": \"Directcosts\"\n    }\n  ],\n  \"assignments\": {\n    \"inventory_control\": \"1000\",\n    \"sales_orders\": \"4000\",\n    \"shipping_sales_orders\": \"4000\",\n    \"cogs\": \"5000\"\n  },\n  \"warehouse_inventory_accounts\": {\n    \"12\": \"1000\"\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/setup/apply-suggested-chart","host":["{{baseUrl}}"],"path":["api","ledger","setup","apply-suggested-chart"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"result\": {\n      \"created\": 14,\n      \"chart_count\": 14\n    }\n  },\n  \"message\": \"Chart of accounts created — 14 accounts added.\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Lock Dates","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/period-locks","host":["{{baseUrl}}"],"path":["api","ledger","period-locks"]},"description":"The accounting lock-date control surface: the effective lock (later of your lock date and enforced provider locks), the internal lock with audit info, per-connection provider locks + enforcement, and the locked-period policy.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/period-locks","host":["{{baseUrl}}"],"path":["api","ledger","period-locks"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"period_locks\": {\n      \"effective_lock_date\": \"2026-07-31\",\n      \"internal\": {\n        \"lock_date\": \"2026-06-30\",\n        \"note\": \"June close\",\n        \"set_by\": {\n          \"id\": 12,\n          \"name\": \"Jamie Rivera\"\n        },\n        \"set_at\": \"2026-07-01T14:03:00+00:00\"\n      },\n      \"providers\": [\n        {\n          \"connection_id\": 30,\n          \"provider\": \"xero\",\n          \"connection_name\": \"Xero\",\n          \"lock_date\": \"2026-05-31\",\n          \"enforced\": true,\n          \"imported_at\": \"2026-07-01T06:00:02+00:00\",\n          \"suggestable\": false\n        }\n      ],\n      \"policy\": \"redate\"\n    }\n  }\n}"}]},{"name":"Apply Provider Lock Suggestion","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/period-locks/apply-suggestion","host":["{{baseUrl}}"],"path":["api","ledger","period-locks","apply-suggestion"]},"description":"Adopt a connection's imported provider lock date as the internal accounting lock date.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Body:** `connection_id` (integer, required).\n\nReturns the updated lock-date surface; 422 when the connection has no imported provider lock or the date precedes the accounting start date.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 30\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/period-locks/apply-suggestion","host":["{{baseUrl}}"],"path":["api","ledger","period-locks","apply-suggestion"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"period_locks\": {\n      \"effective_lock_date\": \"2026-07-31\",\n      \"internal\": {\n        \"lock_date\": \"2026-06-30\",\n        \"note\": \"June close\",\n        \"set_by\": {\n          \"id\": 12,\n          \"name\": \"Jamie Rivera\"\n        },\n        \"set_at\": \"2026-07-01T14:03:00+00:00\"\n      },\n      \"providers\": [\n        {\n          \"connection_id\": 30,\n          \"provider\": \"xero\",\n          \"connection_name\": \"Xero\",\n          \"lock_date\": \"2026-05-31\",\n          \"enforced\": true,\n          \"imported_at\": \"2026-07-01T06:00:02+00:00\",\n          \"suggestable\": false\n        }\n      ],\n      \"policy\": \"redate\"\n    }\n  }\n}"}]},{"name":"Toggle Provider Lock Enforcement","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/period-locks/provider/{connectionId}","host":["{{baseUrl}}"],"path":["api","ledger","period-locks","provider",":connectionId"],"variable":[{"key":"connectionId","value":"","description":"The connection ID."}]},"description":"Set whether a connection's imported provider lock is enforced locally.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Path:** `connectionId` (integer). **Body:** `enforced` (boolean). Off ⇒ the provider date is suggestion-only.\n\nReturns the updated lock-date surface.","body":{"mode":"raw","raw":"{\n  \"enforced\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/ledger/period-locks/provider/{connectionId}","host":["{{baseUrl}}"],"path":["api","ledger","period-locks","provider",":connectionId"],"variable":[{"key":"connectionId","value":"","description":"The connection ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"period_locks\": {\n      \"effective_lock_date\": \"2026-07-31\",\n      \"internal\": {\n        \"lock_date\": \"2026-06-30\",\n        \"note\": \"June close\",\n        \"set_by\": {\n          \"id\": 12,\n          \"name\": \"Jamie Rivera\"\n        },\n        \"set_at\": \"2026-07-01T14:03:00+00:00\"\n      },\n      \"providers\": [\n        {\n          \"connection_id\": 30,\n          \"provider\": \"xero\",\n          \"connection_name\": \"Xero\",\n          \"lock_date\": \"2026-05-31\",\n          \"enforced\": true,\n          \"imported_at\": \"2026-07-01T06:00:02+00:00\",\n          \"suggestable\": false\n        }\n      ],\n      \"policy\": \"redate\"\n    }\n  }\n}"}]},{"name":"Get Document Numbering Settings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/settings/document-numbering","host":["{{baseUrl}}"],"path":["api","ledger","settings","document-numbering"]},"description":"How NEW sales invoices are numbered on the active accounting connection (Settings → Document Numbering).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Response fields:**\n- `document_numbering.sales_invoice_strategy` — one of `source_reference`, `system`, `provider_assigned`\n- `document_numbering.prefix` — optional string prepended to the number under the `source_reference` strategy (max 8 chars, `A-Z a-z 0-9 - _`). Empty string = no prefix\n- `document_numbering.collision_suffix_enabled` — when a source reference is already in use by another document, `true` disambiguates it (`SO-1001-2`); `false` falls back to SKU's own scheme (`INV-{id}`) for that document. A number always identifies exactly one document either way\n- `strategies[]` — the pickable options (`value`, `label`, `description`) for rendering the selector\n- `connection_id` / `connection_name` — the accounting connection these settings belong to; both `null` when none is configured\n\n**No accounting connection:** returns 200 with the defaults a new connection would get (`source_reference`, no prefix, collision suffix on) and null connection fields — it does NOT 404. Only the PUT 404s.\n\n**Scope:** the strategy governs sales invoices only, and only the NEXT document. Bills, credit notes and batch invoices always use SKU's own scheme (they have no single source number to borrow without colliding), and changing the setting never renumbers a document that has already been issued — an issued number is frozen and inherited by every later correction of that document."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/settings/document-numbering","host":["{{baseUrl}}"],"path":["api","ledger","settings","document-numbering"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"document_numbering\": {\n      \"sales_invoice_strategy\": \"source_reference\",\n      \"prefix\": \"ACME-\",\n      \"collision_suffix_enabled\": true\n    },\n    \"strategies\": [\n      {\n        \"value\": \"source_reference\",\n        \"label\": \"Use the source document number\",\n        \"description\": \"Number the invoice after the order it came from (SO-1042), so the invoice in QuickBooks or Xero carries the number the customer already knows.\"\n      },\n      {\n        \"value\": \"system\",\n        \"label\": \"Use SKU's own numbering\",\n        \"description\": \"SKU mints the number (INV-8213). Unique by construction and never collides with anything.\"\n      },\n      {\n        \"value\": \"provider_assigned\",\n        \"label\": \"Let QuickBooks / Xero number it\",\n        \"description\": \"SKU pushes no number at all and the accounting provider assigns its own from its next-number sequence.\"\n      }\n    ],\n    \"connection_id\": 12,\n    \"connection_name\": \"QuickBooks Online\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Update Document Numbering Settings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/settings/document-numbering","host":["{{baseUrl}}"],"path":["api","ledger","settings","document-numbering"]},"description":"Save how NEW sales invoices are numbered on the active accounting connection.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.manage_settings` permission (403 without it).\n\n**Request body:**\n- `sales_invoice_strategy` (string, required) — `source_reference` (number the invoice after the order it came from), `system` (SKU mints `INV-{id}`), or `provider_assigned` (push no number; QuickBooks/Xero assigns one)\n- `prefix` (string, optional, nullable) — prepended under `source_reference`. Max 8 characters, letters/numbers/dashes/underscores only. Omit or send `null`/`\"\"` for no prefix. Ignored by the other strategies\n- `collision_suffix_enabled` (boolean, optional, default `true`) — `true` disambiguates a reference already in use (`SO-1001-2`); `false` falls back to `INV-{id}` for that document instead\n\n**Errors:**\n- `404` — no accounting connection is configured (nothing to save the setting against)\n- `422` — unknown strategy, or a prefix that is too long or contains disallowed characters\n\n**Note:** this decides the NEXT document only. Documents already issued keep their number, and every correction of one re-pushes under the number the customer already holds.","body":{"mode":"raw","raw":"{\n  \"sales_invoice_strategy\": \"source_reference\",\n  \"prefix\": \"ACME-\",\n  \"collision_suffix_enabled\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/ledger/settings/document-numbering","host":["{{baseUrl}}"],"path":["api","ledger","settings","document-numbering"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"document_numbering\": {\n      \"sales_invoice_strategy\": \"source_reference\",\n      \"prefix\": \"ACME-\",\n      \"collision_suffix_enabled\": true\n    }\n  },\n  \"message\": \"Document numbering settings saved.\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Opening Balance Overview","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/opening-balance","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance"]},"description":"The opening balance is an INTERNAL journal entry seeding each GL account's position as of the tenant's books-start date — it is NEVER synced to the accounting provider (non-bypassable `internal_only` eligibility), which already holds these balances. The inventory line is derived from the valuation at end of day prior to the Inventory Start Date and snapshotted at apply time. Spec: docs/plans/ledger-opening-balance-entry.md.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\nReturns the document (draft or applied) with its lines, the live derived inventory figure, drift vs the applied snapshot, the suggested equity account, the prior-history double-count warning count, and the effective generation switch."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/opening-balance","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"opening_balance_overview\": {\n      \"opening_balance\": {\n        \"id\": 1,\n        \"as_of_date\": \"2026-01-01\",\n        \"currency_code\": \"USD\",\n        \"status\": \"applied\",\n        \"inventory_total\": \"12500.0000\",\n        \"equity_nominal_code\": {\n          \"id\": 14,\n          \"code\": \"960\",\n          \"name\": \"Opening Balance Equity\"\n        },\n        \"notes\": null,\n        \"lines\": [\n          {\n            \"id\": 1,\n            \"nominal_code\": {\n              \"id\": 6,\n              \"code\": \"1200\",\n              \"name\": \"Accounts Receivable\"\n            },\n            \"direction\": \"debit\",\n            \"amount\": \"5000.0000\",\n            \"is_inventory_line\": false\n          },\n          {\n            \"id\": 2,\n            \"nominal_code\": {\n              \"id\": 4,\n              \"code\": \"1000\",\n              \"name\": \"Inventory Control\"\n            },\n            \"direction\": \"debit\",\n            \"amount\": \"12500.0000\",\n            \"is_inventory_line\": true\n          }\n        ],\n        \"entry\": {\n          \"id\": 61,\n          \"reference\": \"OB-2026-01-01\",\n          \"status\": \"draft\",\n          \"total_debit\": \"17500.0000\",\n          \"effective_at\": \"2025-12-31T23:59:59+00:00\"\n        }\n      },\n      \"inventory_start_date\": \"2026-01-01\",\n      \"derived_inventory_total\": \"12500.0000\",\n      \"inventory_nominal_code\": {\n        \"id\": 4,\n        \"code\": \"1000\",\n        \"name\": \"Inventory Control\"\n      },\n      \"suggested_equity_nominal_code\": {\n        \"id\": 14,\n        \"code\": \"960\",\n        \"name\": \"Opening Balance Equity\"\n      },\n      \"drift\": null,\n      \"prior_history_count\": 0,\n      \"generation_enabled\": true\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Upsert Opening Balance Draft","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/opening-balance","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance"]},"description":"The opening balance is an INTERNAL journal entry seeding each GL account's position as of the tenant's books-start date — it is NEVER synced to the accounting provider (non-bypassable `internal_only` eligibility), which already holds these balances. The inventory line is derived from the valuation at end of day prior to the Inventory Start Date and snapshotted at apply time. Spec: docs/plans/ledger-opening-balance-entry.md.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.manage_settings`.\n\n**Request body:** `as_of_date` (Y-m-d, must equal the Inventory Start Date at apply time), optional `equity_nominal_code_id` (balancing account), optional `notes`, and `lines[]` of `{nominal_code_id, direction: debit|credit, amount > 0}` — accounts must be unique; the inventory account is system-managed and may not appear. 422 once the document is applied (delete and recreate, or rebuild).","body":{"mode":"raw","raw":"{\n  \"as_of_date\": \"2026-01-01\",\n  \"equity_nominal_code_id\": 14,\n  \"notes\": null,\n  \"lines\": [\n    {\n      \"nominal_code_id\": 6,\n      \"direction\": \"debit\",\n      \"amount\": \"5000.0000\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/ledger/opening-balance","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"opening_balance_overview\": {\n      \"opening_balance\": {\n        \"id\": 1,\n        \"as_of_date\": \"2026-01-01\",\n        \"currency_code\": \"USD\",\n        \"status\": \"applied\",\n        \"inventory_total\": \"12500.0000\",\n        \"equity_nominal_code\": {\n          \"id\": 14,\n          \"code\": \"960\",\n          \"name\": \"Opening Balance Equity\"\n        },\n        \"notes\": null,\n        \"lines\": [\n          {\n            \"id\": 1,\n            \"nominal_code\": {\n              \"id\": 6,\n              \"code\": \"1200\",\n              \"name\": \"Accounts Receivable\"\n            },\n            \"direction\": \"debit\",\n            \"amount\": \"5000.0000\",\n            \"is_inventory_line\": false\n          },\n          {\n            \"id\": 2,\n            \"nominal_code\": {\n              \"id\": 4,\n              \"code\": \"1000\",\n              \"name\": \"Inventory Control\"\n            },\n            \"direction\": \"debit\",\n            \"amount\": \"12500.0000\",\n            \"is_inventory_line\": true\n          }\n        ],\n        \"entry\": {\n          \"id\": 61,\n          \"reference\": \"OB-2026-01-01\",\n          \"status\": \"draft\",\n          \"total_debit\": \"17500.0000\",\n          \"effective_at\": \"2025-12-31T23:59:59+00:00\"\n        }\n      },\n      \"inventory_start_date\": \"2026-01-01\",\n      \"derived_inventory_total\": \"12500.0000\",\n      \"inventory_nominal_code\": {\n        \"id\": 4,\n        \"code\": \"1000\",\n        \"name\": \"Inventory Control\"\n      },\n      \"suggested_equity_nominal_code\": {\n        \"id\": 14,\n        \"code\": \"960\",\n        \"name\": \"Opening Balance Equity\"\n      },\n      \"drift\": null,\n      \"prior_history_count\": 0,\n      \"generation_enabled\": true\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Delete Opening Balance","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/opening-balance","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance"]},"description":"The opening balance is an INTERNAL journal entry seeding each GL account's position as of the tenant's books-start date — it is NEVER synced to the accounting provider (non-bypassable `internal_only` eligibility), which already holds these balances. The inventory line is derived from the valuation at end of day prior to the Inventory Start Date and snapshotted at apply time. Spec: docs/plans/ledger-opening-balance-entry.md.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.manage_settings`.\n\nRemoves the document; an applied document's entry is discarded (drafts) or reversed (posted) through the standard source-deleted pathway — the correction chain remains auditable."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/ledger/opening-balance","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [],\n  \"message\": \"Opening balance deleted.\",\n  \"status\": \"success\"\n}"}]},{"name":"Preview Opening Balances from Provider","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/opening-balance/provider-preview?connection_id=12","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance","provider-preview"],"query":[{"key":"connection_id","value":"12","description":"REQUIRED. Integer ID of the accounting connection (QuickBooks Online or Xero) whose trial balance is read.","disabled":true}]},"description":"Reads the selected connection's Trial Balance \"as at\" the snapshot date and proposes opening-balance lines mapped onto SKU's chart of accounts. Works for both QuickBooks Online and Xero connections — the trial balance is fetched from whichever provider the connection belongs to. Read-only — nothing is persisted; the frontend uses the result to seed the opening-balance draft editor, which the user then reviews and applies.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nMapping rules:\n- Each provider account is matched to a SKU nominal code by **code**.\n- The signed provider balance (debit-positive) is split into a `direction` (`debit` when ≥ 0, else `credit`) and a positive `amount`.\n- Near-zero balances (|balance| < 0.005) are skipped.\n- The **inventory-control** account is excluded (returned as `skipped_inventory`) — SKU derives that opening line from its own valuation, so importing the provider figure would double-count it.\n- Provider accounts with no code, or whose code matches no nominal code, are returned in `unmatched` so the user can map them and re-fetch.\n\n**Errors:** returns `422` with a friendly message when the connection's provider cannot produce a trial balance report, and `404` when `connection_id` is not a supported accounting connection.\n\n**Authentication:** Requires Bearer token. (Read-only route — no extra permission middleware; the mutating apply/upsert routes enforce `accounting.manage_settings`.)\n\n**Query parameters:**\n- `connection_id` (integer, required) — the integration instance to read from.\n- `as_of` (date `Y-m-d`, optional) — trial-balance snapshot date; defaults to today.\n\nEach `lines[]` and `unmatched[]` item also carries account metadata from the provider's chart of accounts: `account_class` (normalized bucket: `Asset`, `Liability`, `Equity`, `Revenue`, or `Expense`; null when unknown), `account_type` (the provider's own finer account type label, e.g. \"Fixed Asset\"), and `mirror_account_id` (the id of the provider account record in SKU, for linking to its detail view; null when the account is not mirrored locally)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/opening-balance/provider-preview?connection_id=12","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance","provider-preview"],"query":[{"key":"connection_id","value":"12","description":"REQUIRED. Integer ID of the accounting connection (QuickBooks Online or Xero) whose trial balance is read.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"opening_balance_import\": {\n      \"connection_id\": 12,\n      \"as_of\": \"2026-01-01\",\n      \"lines\": [\n        {\n          \"nominal_code_id\": 6,\n          \"code\": \"1200\",\n          \"name\": \"Accounts Receivable\",\n          \"direction\": \"debit\",\n          \"amount\": \"5000.00\",\n          \"provider_account_name\": \"Accounts Receivable (1200)\",\n          \"provider_balance\": 5000\n        },\n        {\n          \"nominal_code_id\": 21,\n          \"code\": \"2100\",\n          \"name\": \"Accounts Payable\",\n          \"direction\": \"credit\",\n          \"amount\": \"3200.50\",\n          \"provider_account_name\": \"Accounts Payable (2100)\",\n          \"provider_balance\": -3200.5\n        }\n      ],\n      \"unmatched\": [\n        {\n          \"code\": \"4500\",\n          \"name\": \"Foreign Exchange Gains (4500)\",\n          \"provider_balance\": -125.75\n        },\n        {\n          \"code\": null,\n          \"name\": \"Rounding\",\n          \"provider_balance\": 0.42\n        }\n      ],\n      \"skipped_inventory\": {\n        \"nominal_code_id\": 4,\n        \"code\": \"1000\",\n        \"name\": \"Inventory Control\",\n        \"direction\": \"debit\",\n        \"amount\": \"12500.00\",\n        \"provider_account_name\": \"Inventory Control (1000)\",\n        \"provider_balance\": 12500\n      }\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Create Opening Balance Equity Account","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/opening-balance/equity-account","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance","equity-account"]},"description":"Create the provider-conventional \"Opening Balance Equity\" account when the chart of accounts has none, so the opening-balance form's balancing-equity selector has something to point at. The created account is type `Equity` and is a never-synced internal plug. Its `code` is the first free preferred code (970, then 3000, then 9999, then numerically incremented past the last candidate), checked against ALL codes including archived ones since `code` is the unique key.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.manage_settings`.\n\n**Idempotent:** if an account whose name contains \"opening balance\" already exists, that existing account is returned and no new account is created.\n\nNo request body. Returns the created (or existing) nominal code as `data.nominal_code`."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/opening-balance/equity-account","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance","equity-account"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"nominal_code\": {\n      \"id\": 14,\n      \"code\": \"970\",\n      \"name\": \"Opening Balance Equity\"\n    }\n  },\n  \"message\": \"Opening Balance Equity account created.\",\n  \"status\": \"success\"\n}"}]},{"name":"Apply Opening Balance","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/opening-balance/apply","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance","apply"]},"description":"The opening balance is an INTERNAL journal entry seeding each GL account's position as of the tenant's books-start date — it is NEVER synced to the accounting provider (non-bypassable `internal_only` eligibility), which already holds these balances. The inventory line is derived from the valuation at end of day prior to the Inventory Start Date and snapshotted at apply time. Spec: docs/plans/ledger-opening-balance-entry.md.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.manage_settings`.\n\nValidates (as_of_date === Inventory Start Date; equity account present when lines don't balance; Inventory Control mapping set when valuation > 0; generation enabled), snapshots the derived inventory figure, and generates the entry through the standard outbox pipeline. The computed equity counter-line guarantees the entry balances.\n\n**Errors:** 422 with field-specific messages for each failed precondition."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/opening-balance/apply","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance","apply"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"opening_balance_overview\": {\n      \"opening_balance\": {\n        \"id\": 1,\n        \"as_of_date\": \"2026-01-01\",\n        \"currency_code\": \"USD\",\n        \"status\": \"applied\",\n        \"inventory_total\": \"12500.0000\",\n        \"equity_nominal_code\": {\n          \"id\": 14,\n          \"code\": \"960\",\n          \"name\": \"Opening Balance Equity\"\n        },\n        \"notes\": null,\n        \"lines\": [\n          {\n            \"id\": 1,\n            \"nominal_code\": {\n              \"id\": 6,\n              \"code\": \"1200\",\n              \"name\": \"Accounts Receivable\"\n            },\n            \"direction\": \"debit\",\n            \"amount\": \"5000.0000\",\n            \"is_inventory_line\": false\n          },\n          {\n            \"id\": 2,\n            \"nominal_code\": {\n              \"id\": 4,\n              \"code\": \"1000\",\n              \"name\": \"Inventory Control\"\n            },\n            \"direction\": \"debit\",\n            \"amount\": \"12500.0000\",\n            \"is_inventory_line\": true\n          }\n        ],\n        \"entry\": {\n          \"id\": 61,\n          \"reference\": \"OB-2026-01-01\",\n          \"status\": \"draft\",\n          \"total_debit\": \"17500.0000\",\n          \"effective_at\": \"2025-12-31T23:59:59+00:00\"\n        }\n      },\n      \"inventory_start_date\": \"2026-01-01\",\n      \"derived_inventory_total\": \"12500.0000\",\n      \"inventory_nominal_code\": {\n        \"id\": 4,\n        \"code\": \"1000\",\n        \"name\": \"Inventory Control\"\n      },\n      \"suggested_equity_nominal_code\": {\n        \"id\": 14,\n        \"code\": \"960\",\n        \"name\": \"Opening Balance Equity\"\n      },\n      \"drift\": null,\n      \"prior_history_count\": 0,\n      \"generation_enabled\": true\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Rebuild Opening Balance","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/opening-balance/rebuild","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance","rebuild"]},"description":"The opening balance is an INTERNAL journal entry seeding each GL account's position as of the tenant's books-start date — it is NEVER synced to the accounting provider (non-bypassable `internal_only` eligibility), which already holds these balances. The inventory line is derived from the valuation at end of day prior to the Inventory Start Date and snapshotted at apply time. Spec: docs/plans/ledger-opening-balance-entry.md.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.sync`.\n\nRe-derives the inventory snapshot and regenerates the entry — an unchanged document is a hash no-op; a posted entry is corrected via reversal + replacement. Use when the overview reports `drift` (the live valuation moved after a backfill)."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/opening-balance/rebuild","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance","rebuild"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"opening_balance_overview\": {\n      \"opening_balance\": {\n        \"id\": 1,\n        \"as_of_date\": \"2026-01-01\",\n        \"currency_code\": \"USD\",\n        \"status\": \"applied\",\n        \"inventory_total\": \"12500.0000\",\n        \"equity_nominal_code\": {\n          \"id\": 14,\n          \"code\": \"960\",\n          \"name\": \"Opening Balance Equity\"\n        },\n        \"notes\": null,\n        \"lines\": [\n          {\n            \"id\": 1,\n            \"nominal_code\": {\n              \"id\": 6,\n              \"code\": \"1200\",\n              \"name\": \"Accounts Receivable\"\n            },\n            \"direction\": \"debit\",\n            \"amount\": \"5000.0000\",\n            \"is_inventory_line\": false\n          },\n          {\n            \"id\": 2,\n            \"nominal_code\": {\n              \"id\": 4,\n              \"code\": \"1000\",\n              \"name\": \"Inventory Control\"\n            },\n            \"direction\": \"debit\",\n            \"amount\": \"12500.0000\",\n            \"is_inventory_line\": true\n          }\n        ],\n        \"entry\": {\n          \"id\": 61,\n          \"reference\": \"OB-2026-01-01\",\n          \"status\": \"draft\",\n          \"total_debit\": \"17500.0000\",\n          \"effective_at\": \"2025-12-31T23:59:59+00:00\"\n        }\n      },\n      \"inventory_start_date\": \"2026-01-01\",\n      \"derived_inventory_total\": \"12500.0000\",\n      \"inventory_nominal_code\": {\n        \"id\": 4,\n        \"code\": \"1000\",\n        \"name\": \"Inventory Control\"\n      },\n      \"suggested_equity_nominal_code\": {\n        \"id\": 14,\n        \"code\": \"960\",\n        \"name\": \"Opening Balance Equity\"\n      },\n      \"drift\": null,\n      \"prior_history_count\": 0,\n      \"generation_enabled\": true\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Get Pending Start Date Transition","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/opening-balance/transition","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance","transition"]},"description":"Returns the pending guarded accounting-start-date change awaiting confirmation, or `null` under `start_date_transition` when the books are settled (no change in flight).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nWhen an applied opening balance's effective accounting start date is moved, the change is PARKED rather than applied immediately: the old anchor's books are already posted, so the user must explicitly confirm (apply) or abandon (rollback). This read-only endpoint surfaces that parked change so the UI can render the review banner.\n\n**Authentication:** Requires Bearer token.\n\n**Response fields (`start_date_transition`):**\n- `id` — transition row id\n- `status` — `pending` while awaiting confirmation\n- `old_effective_start_date` / `new_effective_start_date` — the effective accounting start dates (where the opening entry posts)\n- `old_anchor_date` / `new_anchor_date` — the trial-balance \"as at\" anchor (= effective start − 1 day) that the replacement opening balance is imported against\n- `old_opening_total` — the frozen total of the currently-applied opening balance\n- `new_opening_total` — the PROJECTED total of the replacement opening balance at the new anchor. This is `null` until the replacement trial balance has been captured at `new_anchor_date` — a GET preview never forces a metered provider capture, so the projection is simply withheld until the async capture lands.\n\nReturns `{ \"start_date_transition\": null }` when no transition is pending."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/opening-balance/transition","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance","transition"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"start_date_transition\": {\n      \"id\": 7,\n      \"status\": \"pending\",\n      \"old_effective_start_date\": \"2026-05-01\",\n      \"new_effective_start_date\": \"2026-06-01\",\n      \"old_anchor_date\": \"2026-04-30\",\n      \"new_anchor_date\": \"2026-05-31\",\n      \"old_opening_total\": \"1234.5600\",\n      \"new_opening_total\": \"500.0000\"\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Apply Start Date Transition","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/opening-balance/transition/apply","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance","transition","apply"]},"description":"Confirms the parked accounting-start-date change. The opening balance is re-imported IN PLACE from the replacement trial balance at the new anchor (the same opening-balance row is kept — preserving its notes, created-by, equity account, applied status and history), its entry is reversed and reposted through the standard outbox pipeline, and the reconciliation is re-anchored + re-backfilled via a tracked Sync Provider GL Journals job.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.manage_settings`.\n\nNo request body. Applying intentionally permits a metered provider capture if the replacement trial balance hasn't been snapshotted yet (it is a deliberate user action). On success the pending transition settles to `applied` and `opening_balance_overview.pending_start_date_transition` becomes `null`.\n\n**Errors:** 422 when there is no pending transition, when no Xero accounting connection exists, or when ledger generation is disabled for the tenant."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/opening-balance/transition/apply","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance","transition","apply"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"opening_balance_overview\": {\n      \"opening_balance\": {\n        \"id\": 1,\n        \"as_of_date\": \"2026-05-01\",\n        \"currency_code\": \"USD\",\n        \"status\": \"applied\",\n        \"inventory_total\": \"0.0000\",\n        \"equity_nominal_code\": {\n          \"id\": 14,\n          \"code\": \"970\",\n          \"name\": \"Opening Balance Equity\"\n        },\n        \"notes\": null,\n        \"lines\": [\n          {\n            \"id\": 12,\n            \"nominal_code\": {\n              \"id\": 9,\n              \"code\": \"4000\",\n              \"name\": \"Sales\"\n            },\n            \"direction\": \"debit\",\n            \"amount\": \"500.0000\",\n            \"is_inventory_line\": false\n          }\n        ],\n        \"entry\": {\n          \"id\": 88,\n          \"reference\": \"OB-2026-05-01/R\",\n          \"status\": \"draft\",\n          \"total_debit\": \"500.0000\",\n          \"effective_at\": \"2026-04-30T23:59:59+00:00\"\n        }\n      },\n      \"inventory_start_date\": \"2026-05-01\",\n      \"derived_inventory_total\": \"0.0000\",\n      \"inventory_nominal_code\": {\n        \"id\": 4,\n        \"code\": \"1000\",\n        \"name\": \"Inventory Control\"\n      },\n      \"suggested_equity_nominal_code\": {\n        \"id\": 14,\n        \"code\": \"970\",\n        \"name\": \"Opening Balance Equity\"\n      },\n      \"drift\": null,\n      \"prior_history_count\": 1,\n      \"generation_enabled\": true,\n      \"pending_start_date_transition\": null\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Rollback Start Date Transition","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/opening-balance/transition/rollback","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance","transition","rollback"]},"description":"Abandons the parked accounting-start-date change while it is still pending. The effective accounting start date is reverted to its old value and the transition settles to `cancelled` — the posted books are left completely untouched (no reversal, no rebuild, no reconciliation re-anchor). Safe because while pending the books were never changed.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nThe revert is written directly to settings (not through the Settings endpoint) so it does NOT re-trigger the start-date-change hook that would park a fresh transition.\n\n**Authentication:** Requires Bearer token + `accounting.manage_settings`.\n\nNo request body. On success `opening_balance_overview.pending_start_date_transition` becomes `null` and the opening balance + its posted entry are unchanged.\n\n**Errors:** 422 when there is no pending transition to roll back."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/opening-balance/transition/rollback","host":["{{baseUrl}}"],"path":["api","ledger","opening-balance","transition","rollback"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"opening_balance_overview\": {\n      \"opening_balance\": {\n        \"id\": 1,\n        \"as_of_date\": \"2026-05-01\",\n        \"currency_code\": \"USD\",\n        \"status\": \"applied\",\n        \"inventory_total\": \"1234.5600\",\n        \"equity_nominal_code\": {\n          \"id\": 14,\n          \"code\": \"970\",\n          \"name\": \"Opening Balance Equity\"\n        },\n        \"notes\": null,\n        \"lines\": [\n          {\n            \"id\": 3,\n            \"nominal_code\": {\n              \"id\": 6,\n              \"code\": \"1200\",\n              \"name\": \"Accounts Receivable\"\n            },\n            \"direction\": \"debit\",\n            \"amount\": \"1234.5600\",\n            \"is_inventory_line\": false\n          }\n        ],\n        \"entry\": {\n          \"id\": 61,\n          \"reference\": \"OB-2026-05-01\",\n          \"status\": \"posted\",\n          \"total_debit\": \"1234.5600\",\n          \"effective_at\": \"2026-04-30T23:59:59+00:00\"\n        }\n      },\n      \"inventory_start_date\": \"2026-05-01\",\n      \"derived_inventory_total\": \"1234.5600\",\n      \"inventory_nominal_code\": {\n        \"id\": 4,\n        \"code\": \"1000\",\n        \"name\": \"Inventory Control\"\n      },\n      \"suggested_equity_nominal_code\": {\n        \"id\": 14,\n        \"code\": \"970\",\n        \"name\": \"Opening Balance Equity\"\n      },\n      \"drift\": null,\n      \"prior_history_count\": 1,\n      \"generation_enabled\": true,\n      \"pending_start_date_transition\": null\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"List Generation Candidates","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/generation/candidates","host":["{{baseUrl}}"],"path":["api","ledger","generation","candidates"]},"description":"Preview of the source documents eligible to have ledger entries generated — the entries page \"Generate\" modal. A document is a candidate when it passes the same readiness gate the legacy generation used (`scopeAccountingReady`: drafts, unmapped lines, Amazon channel orders, … are excluded) AND it has no current (non-discarded, non-reversed) ledger entry.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Response fields:**\n- `candidates` — Laravel paginator of `{source_type, source_id, type_label, reference, url, date}` rows. `url` is the application UI detail route when one exists; `date` is the document's accounting date (Y-m-d). Without `filter[source_type]` the listing walks every type in tab (label) order, each type's rows newest-first.\n- `tabs` — one row per source type that currently has candidates (`{source_type, label, count}`), label-sorted — drives the modal's tab strip\n- `total` — candidate count across all types for the date range\n- `schedule.next_generation_run_at` / `schedule.next_reconcile_run_at` — UTC datetimes of the next per-minute drain run and the next daily reconciliation sweep (these documents are picked up automatically; a manual run just front-runs the schedule)\n- `has_frozen_history` — whether the tenant has any imported (migrated) ledger history. When true, imported entries are frozen: scope runs skip them unless `include_frozen` is passed to Run Generation Now.\n- `enabled` — the effective generation switch (tenant setting OR environment override); when false a run request would be rejected\n\n**Date filtering** applies to each document's own accounting date column (invoice date, fulfillment date, adjustment date, …), not created_at."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/generation/candidates","host":["{{baseUrl}}"],"path":["api","ledger","generation","candidates"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"candidates\": {\n      \"current_page\": 1,\n      \"data\": [\n        {\n          \"source_type\": \"App\\\\Models\\\\Bill\",\n          \"source_id\": 4,\n          \"type_label\": \"Bill\",\n          \"reference\": \"BILL-1042\",\n          \"url\": null,\n          \"date\": \"2026-06-05\"\n        },\n        {\n          \"source_type\": \"App\\\\Models\\\\SalesOrderFulfillment\",\n          \"source_id\": 911,\n          \"type_label\": \"Sales Order Fulfillment\",\n          \"reference\": \"SO-2231/F1\",\n          \"url\": null,\n          \"date\": \"2026-06-04\"\n        }\n      ],\n      \"last_page\": 1,\n      \"per_page\": 10,\n      \"total\": 2,\n      \"from\": 1,\n      \"to\": 2\n    },\n    \"tabs\": [\n      {\n        \"source_type\": \"App\\\\Models\\\\Bill\",\n        \"label\": \"Bill\",\n        \"count\": 1\n      },\n      {\n        \"source_type\": \"App\\\\Models\\\\SalesOrderFulfillment\",\n        \"label\": \"Sales Order Fulfillment\",\n        \"count\": 1\n      }\n    ],\n    \"total\": 2,\n    \"schedule\": {\n      \"next_generation_run_at\": \"2026-06-12 20:21:00\",\n      \"next_reconcile_run_at\": \"2026-06-13 00:00:00\"\n    },\n    \"enabled\": true,\n    \"has_frozen_history\": true\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Run Generation Now","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/generation/run","host":["{{baseUrl}}"],"path":["api","ledger","generation","run"]},"description":"Trigger on-demand entry generation: every candidate in scope (see List Generation Candidates) is enqueued as an `updated` ledger outbox row and the drain job is dispatched immediately — the SAME pipeline the per-minute schedule runs, just started now. The posting service's source_version_hash makes already-current documents a free no-op, so the trigger is idempotent.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.sync` permission (403 without it).\n\n**Request body (all optional):**\n- `candidates` (array|null, max 1000) — explicit selection of documents to generate: `[{source_type, source_id}, ...]`. When present, ONLY these are enqueued and the type/date scope is ignored (the modal's multi-select). Each `source_type` must be a registered source FQN (422 otherwise).\n- `source_type` (string|null) — restrict to one source document FQN from the posting-rule registry; omit/null for all types\n- `date_from` / `date_to` (Y-m-d|null) — restrict by each document's accounting date; `date_to` must be >= `date_from`\n- `include_frozen` (boolean, default false) — scope runs normally skip documents dated before the tenant's accounting cutover (their imported history is frozen so it is never rewritten by accident). Set true to deliberately re-derive that frozen history with the current posting rules — this replaces the migrated figures and can update linked, still-editable provider documents, so treat it as a confirmed administrative action. Ignored when `candidates` is present (hand-picked documents always regenerate).\n\n**Errors:** 422 with an `enabled` error when ledger generation is disabled for the tenant (enable it under Settings → Generation first); 422 for unknown `source_type`.\n\n**Response:** `data.result.enqueued` is the number of documents processed/enqueued. `data.result.skipped_locked_period` counts documents that were skipped because they are dated in a locked accounting period — these are not generated (unlock the period or change the document date, then try again). A locked-period document is reported this way rather than failing the whole run.","body":{"mode":"raw","raw":"{\n  \"source_type\": \"App\\\\Models\\\\Bill\",\n  \"date_from\": \"2026-06-01\",\n  \"date_to\": \"2026-06-30\",\n  \"candidates\": [\n    {\n      \"source_type\": \"App\\\\Models\\\\Bill\",\n      \"source_id\": 4\n    },\n    {\n      \"source_type\": \"App\\\\Models\\\\SalesOrderFulfillment\",\n      \"source_id\": 911\n    }\n  ],\n  \"include_frozen\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/generation/run","host":["{{baseUrl}}"],"path":["api","ledger","generation","run"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"result\": {\n      \"enqueued\": 2,\n      \"skipped_locked_period\": 0,\n      \"deferred_not_ready\": 0\n    }\n  },\n  \"message\": \"Enqueued 2 document(s) for generation — entries will appear within the next minute.\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Reconciliation Window","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/window?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","window"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection (IntegrationInstance) id. Required.","disabled":true}]},"description":"The connection's reconcile window and per-kind cache freshness — \"documents cached from X through Y, last refreshed Z\".\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\nThe window runs from now back to the LATER of the connection's sync start floor and the generation start (earliest current ledger entry). 404 when connection_id is not a supported accounting connection."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/window?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","window"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection (IntegrationInstance) id. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"connection_id\": 5,\n    \"from\": \"2026-01-01\",\n    \"to\": \"2026-06-13\",\n    \"cache_status\": [\n      {\n        \"remote_type\": \"qbo_invoice\",\n        \"backfilled_from\": \"2026-01-01\",\n        \"backfilled_through\": \"2026-06-13\",\n        \"document_count\": 182,\n        \"last_backfilled_at\": \"2026-06-12T02:00:00+00:00\"\n      }\n    ]\n  }\n}"}]},{"name":"Reconciliation Matches","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/matches?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","matches"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]},"description":"The three reconcile buckets for a connection over the (optional) document-date window.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Buckets:**\n- `matched` — SKU entries linked to a provider document (with the cached remote snapshot for variance display)\n- `sku_only` — eligible SKU entries with no link (would create a remote document)\n- `provider_only` — cached provider documents with no link and not dismissed\n\n`from`/`to` (Y-m-d) override the default window. `meta.counts` carries the bucket sizes, including `excluded` (the count of unlinked entries that are not eligible to sync — fetched in full via the excluded endpoint). 404 when connection_id is not a supported accounting connection."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/matches?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","matches"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"matched\": [\n      {\n        \"entry\": {\n          \"id\": 4821,\n          \"uuid\": \"9f1c...\",\n          \"type\": {\n            \"value\": \"sales_order_invoice\",\n            \"label\": \"Sales Order Invoice\"\n          },\n          \"status\": \"posted\",\n          \"effective_at\": \"2026-06-05T00:00:00+00:00\",\n          \"currency_code\": \"USD\",\n          \"total_debit\": \"250.0000\",\n          \"total_credit\": \"250.0000\",\n          \"reference\": \"INV-1042\",\n          \"reference_suffix\": 0\n        },\n        \"link\": {\n          \"id\": 77,\n          \"provider\": \"qbo\",\n          \"remote_type\": \"qbo_invoice\",\n          \"remote_id\": \"145\",\n          \"remote_url\": \"https://app.qbo.intuit.com/app/invoice?txnId=145\",\n          \"link_state\": \"linked_current\",\n          \"remote_status\": \"Active\",\n          \"last_remote_seen_at\": \"2026-06-12T02:00:00+00:00\"\n        },\n        \"remote\": {\n          \"remote_type\": \"qbo_invoice\",\n          \"remote_id\": \"145\",\n          \"reference\": \"INV-1042\",\n          \"total\": \"250.0000\",\n          \"currency\": \"USD\",\n          \"document_date\": \"2026-06-05\",\n          \"status\": \"Active\",\n          \"remote_url\": \"https://app.qbo.intuit.com/app/invoice?txnId=145\"\n        }\n      }\n    ],\n    \"sku_only\": [\n      {\n        \"id\": 4822,\n        \"uuid\": \"9f1c...\",\n        \"type\": {\n          \"value\": \"sales_order_invoice\",\n          \"label\": \"Sales Order Invoice\"\n        },\n        \"status\": \"posted\",\n        \"effective_at\": \"2026-06-05T00:00:00+00:00\",\n        \"currency_code\": \"USD\",\n        \"total_debit\": \"250.0000\",\n        \"total_credit\": \"250.0000\",\n        \"reference\": \"INV-1043\",\n        \"reference_suffix\": 0\n      }\n    ],\n    \"provider_only\": [\n      {\n        \"remote_type\": \"qbo_invoice\",\n        \"remote_id\": \"146\",\n        \"reference\": \"INV-9001\",\n        \"total\": \"250.0000\",\n        \"currency\": \"USD\",\n        \"document_date\": \"2026-06-05\",\n        \"status\": \"Active\",\n        \"remote_url\": \"https://app.qbo.intuit.com/app/invoice?txnId=145\"\n      }\n    ]\n  },\n  \"meta\": {\n    \"from\": \"2026-01-01\",\n    \"to\": \"2026-06-13\",\n    \"counts\": {\n      \"matched\": 1,\n      \"sku_only\": 1,\n      \"provider_only\": 1,\n      \"excluded\": 12\n    }\n  }\n}"}]},{"name":"List Excluded SKU Entries","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/excluded?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","excluded"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]},"description":"The SKU ledger entries that are excluded from sync for a connection over the (optional) document-date window — unlinked entries that are not eligible to sync, each with the reason(s) it's blocked.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\nThese are exactly the entries hidden from the `sku_only` bucket of the matches endpoint (an empty `sku_only` does not mean everything is synced — thousands of entries can be excluded). Loaded on its own so the matches payload stays light.\n\nEach row carries `reasons[]` as `{code, label}` pairs. Common reason codes: `override_disabled` (manually disabled), `type_disabled`, `channel_disabled`, `before_provider_sync_start_date`, `before_type_sync_start_date`, `batch_member`, `batch_open`, `connection_inactive`, `internal_only`, `granularity_off`.\n\n`from`/`to` (Y-m-d) override the default window. 404 when connection_id is not a supported accounting connection."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/excluded?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","excluded"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 4830,\n      \"reference\": \"INV-1050\",\n      \"type\": {\n        \"value\": \"sales_order_invoice\",\n        \"label\": \"Sales Order Invoice\"\n      },\n      \"effective_at\": \"2026-06-05T00:00:00+00:00\",\n      \"currency_code\": \"USD\",\n      \"document_total\": \"250.0000\",\n      \"reasons\": [\n        {\n          \"code\": \"override_disabled\",\n          \"label\": \"Sync manually disabled\"\n        }\n      ]\n    },\n    {\n      \"id\": 4831,\n      \"reference\": \"BILL-2201\",\n      \"type\": {\n        \"value\": \"bill\",\n        \"label\": \"Bill\"\n      },\n      \"effective_at\": \"2026-06-04T00:00:00+00:00\",\n      \"currency_code\": \"USD\",\n      \"document_total\": \"980.0000\",\n      \"reasons\": [\n        {\n          \"code\": \"type_disabled\",\n          \"label\": \"Transaction type not synced\"\n        },\n        {\n          \"code\": \"before_provider_sync_start_date\",\n          \"label\": \"Dated before the sync start date\"\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"List Matched Entries","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/matched?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","matched"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]},"description":"The SKU ledger entries already linked to a provider document on a connection, as a paginated, filterable, sortable list. Each row also carries the linked provider document's snapshot in `remote` (remote total, status, and a deep link) so a variance between the two sides can be spotted. For a bill, vendor deposit or vendor credit posted in its own currency (multi-currency payables), `remote` also carries `sku_total` and `sku_currency` — the SKU figure in the currency the provider holds the document in — and the entry carries `document_currency_total`; compare `remote.total` with `remote.sku_total` for those rows. Both keys are absent for every other entry.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Query parameters:** `connection_id` (required); standard pagination (`page`, `per_page`), `sort`, per-field `filter[...]`, general `search`, and `filter_groups` (advanced filter tree). Allowed sorts: id, effective_at, reference, total_debit, created_at, type, display_status.\n\n**Response:** 200, a paginated list of entries."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/matched?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","matched"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 10488,\n      \"document_number\": \"BILL-10488\",\n      \"lineage_root_id\": 10488,\n      \"document_number_is_inherited\": false,\n      \"document_number_is_provider_assigned\": false,\n      \"type\": {\n        \"value\": \"purchase_order_invoice\",\n        \"label\": \"Purchase Order Invoice\"\n      },\n      \"status\": \"posted\",\n      \"display_status\": {\n        \"key\": \"synced\",\n        \"label\": \"Synced\",\n        \"color\": \"success\",\n        \"reason\": null\n      },\n      \"effective_at\": \"2026-05-01T00:00:00+00:00\",\n      \"currency_code\": \"USD\",\n      \"total_debit\": \"3062.6000\",\n      \"document_total\": \"3062.6000\",\n      \"reference\": \"SIBER10488\",\n      \"remote\": {\n        \"remote_type\": \"xero_bill\",\n        \"remote_id\": \"85414bb6-6f88-4c0f-8c53-53e7c1f7128c\",\n        \"link_state\": \"linked_current\",\n        \"remote_status\": \"AUTHORISED\",\n        \"total\": \"3062.6000\",\n        \"document_date\": \"2026-05-01\",\n        \"remote_url\": \"https://go.xero.com/AccountsPayable/View.aspx?InvoiceID=85414bb6-6f88-4c0f-8c53-53e7c1f7128c\"\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"List Unlinked Entries","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/unlinked?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","unlinked"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]},"description":"The SKU ledger entries in the window that are NOT yet linked to a provider document on a connection — both the sync-eligible ones and the ineligible (excluded) ones — as a paginated, filterable, sortable list. Each row carries its live per-connection sync `eligibility` (`eligible` plus the block `reasons`, e.g. `type_disabled`, `channel_disabled`) so the ineligible ones can be flagged.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Query parameters:** `connection_id` (required); standard pagination, `sort`, per-field `filter[...]`, general `search`, and `filter_groups`.\n\n**Response:** 200, a paginated list of entries."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/unlinked?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","unlinked"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 10488,\n      \"document_number\": \"BILL-10488\",\n      \"lineage_root_id\": 10488,\n      \"document_number_is_inherited\": false,\n      \"document_number_is_provider_assigned\": false,\n      \"type\": {\n        \"value\": \"purchase_order_invoice\",\n        \"label\": \"Purchase Order Invoice\"\n      },\n      \"status\": \"posted\",\n      \"display_status\": {\n        \"key\": \"synced\",\n        \"label\": \"Synced\",\n        \"color\": \"success\",\n        \"reason\": null\n      },\n      \"effective_at\": \"2026-05-01T00:00:00+00:00\",\n      \"currency_code\": \"USD\",\n      \"total_debit\": \"3062.6000\",\n      \"document_total\": \"3062.6000\",\n      \"reference\": \"FBA181D492B4: 55641\",\n      \"eligibility\": {\n        \"eligible\": false,\n        \"reasons\": [\n          \"type_disabled\"\n        ]\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"List Provider-only Documents","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/provider-documents?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","provider-documents"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]},"description":"The provider documents (Xero/QBO) that have NOT been linked to a SKU ledger entry and have not been dismissed, over a connection's window — a normalized, paginated, filterable, sortable list across every document kind (invoices, bills, credit notes, journals).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Query parameters:** `connection_id` (required); standard pagination; `sort` (prefix with - for DESC; allowed: reference, secondary_reference, counterparty, total, sub_total, tax_total, amount_due, document_date, due_date, line_count, provider_updated_at, status, remote_type, remote_id); per-field `filter[...]` (`remote_type`, `status`, `doc_class` = `invoice`|`journal`, `date_from`/`date_to`); general `search` (matches reference, secondary reference, counterparty, amount, and exact provider id); and `filter_groups`. Each row's `id` is the composite \"{remote_type}:{remote_id}\".\n\n**Response fields:** `doc_class` splits invoice-shaped documents (invoices/bills/credit notes) from journals. Invoice-shaped rows carry `counterparty`, `secondary_reference` (the provider's free-text reference), `sub_total`, `tax_total`, `amount_due`, and `due_date`; journal rows carry `line_count`. `provider_updated_at` is the provider-side last-modified timestamp. Fields not applicable to a document kind are null.\n\n**Response:** 200, a paginated list of provider documents."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/provider-documents?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","provider-documents"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": \"xero_bill:85414bb6-6f88-4c0f-8c53-53e7c1f7128c\",\n      \"remote_type\": \"xero_bill\",\n      \"remote_id\": \"85414bb6-6f88-4c0f-8c53-53e7c1f7128c\",\n      \"doc_class\": \"invoice\",\n      \"reference\": \"FBA181D492B4: 55641\",\n      \"secondary_reference\": \"PO-7788\",\n      \"counterparty\": \"Acme Corp\",\n      \"total\": \"3950.0000\",\n      \"sub_total\": \"3291.6700\",\n      \"tax_total\": \"658.3300\",\n      \"amount_due\": \"0.0000\",\n      \"currency\": \"USD\",\n      \"document_date\": \"2026-05-23\",\n      \"due_date\": \"2026-06-22\",\n      \"line_count\": null,\n      \"provider_updated_at\": \"2026-05-24T09:15:00+00:00\",\n      \"status\": \"PAID\",\n      \"remote_url\": \"https://go.xero.com/AccountsPayable/View.aspx?InvoiceID=85414bb6-6f88-4c0f-8c53-53e7c1f7128c\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"List Dismissed Documents","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/dismissed?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","dismissed"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]},"description":"The provider documents previously dismissed from reconciliation on a connection, as a paginated, filterable, sortable list — the \"show dismissed\" view for reviewing and restoring documents that were hidden. Restore them with POST /api/ledger/reconciliation/restore or /restore/bulk.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Query parameters:** `connection_id` (required); standard pagination; `sort` (prefix with - for DESC; allowed: reference, secondary_reference, counterparty, total, sub_total, tax_total, amount_due, document_date, due_date, line_count, provider_updated_at, status, remote_type, remote_id); `filter[...]` (`remote_type`, `status`, `doc_class` = `invoice`|`journal`, `date_from`/`date_to`); `search`; and `filter_groups`. Each row's `id` is the composite \"{remote_type}:{remote_id}\".\n\n**Response:** 200, a paginated list of dismissed documents (same row shape as the provider-only list, including `doc_class`, `counterparty`, `secondary_reference`, `sub_total`, `tax_total`, `amount_due`, `due_date`, `line_count`, and `provider_updated_at`)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/dismissed?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","dismissed"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": \"xero_bill:11c2a7d0-882e-4a19-9f6b-73c0d9a1e2ff\",\n      \"remote_type\": \"xero_bill\",\n      \"remote_id\": \"11c2a7d0-882e-4a19-9f6b-73c0d9a1e2ff\",\n      \"doc_class\": \"invoice\",\n      \"reference\": \"Regular Payroll Jun 1 – Jun 15\",\n      \"secondary_reference\": \"PO-7788\",\n      \"counterparty\": \"Acme Corp\",\n      \"total\": \"1371.2600\",\n      \"sub_total\": \"3291.6700\",\n      \"tax_total\": \"658.3300\",\n      \"amount_due\": \"0.0000\",\n      \"currency\": \"USD\",\n      \"document_date\": \"2026-06-15\",\n      \"due_date\": \"2026-06-22\",\n      \"line_count\": null,\n      \"provider_updated_at\": \"2026-05-24T09:15:00+00:00\",\n      \"status\": \"PAID\",\n      \"remote_url\": \"https://go.xero.com/AccountsPayable/View.aspx?InvoiceID=11c2a7d0-882e-4a19-9f6b-73c0d9a1e2ff\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Get Reconciliation Counts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/counts?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","counts"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]},"description":"The unfiltered counts for a connection: matched (linked), SKU-only (eligible unlinked), excluded (ineligible unlinked), and provider-only (unlinked provider documents).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Response:** 200 with the four counts."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/counts?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","counts"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"matched\": 546,\n    \"sku_only\": 0,\n    \"excluded\": 8257,\n    \"provider_only\": 258\n  }\n}"}]},{"name":"List Entry Link Candidates","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/link-candidates?connection_id=30&remote_type=xero_bill&remote_id=85414bb6-6f88-4c0f-8c53-53e7c1f7128c","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","link-candidates"],"query":[{"key":"connection_id","value":"30","description":"Accounting connection id. Required.","disabled":true},{"key":"remote_type","value":"xero_bill","description":"The kind of provider document (e.g. xero_bill, xero_invoice, qbo_invoice). Required.","disabled":true},{"key":"remote_id","value":"85414bb6-6f88-4c0f-8c53-53e7c1f7128c","description":"The provider document id to find SKU entries for. Required.","disabled":true}]},"description":"The SKU ledger entries a provider-only document could be linked to — the confidence-ranked picker for the reverse direction of the manual link (pick the SKU entry that matches a provider document, identified by `remote_type` + `remote_id`).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRanks the connection's unlinked entries against the target document, spanning BOTH the eligible (`sku_only`) and the excluded (ineligible) entries — an entry that will never sync can still be the real counterpart of a provider document, and linking it is safe (it reconciles the document out of the provider-only set without pushing anything). Each candidate carries per-signal match flags — `amount_match`, `date_match`, `reference_match`, `confident` — plus its sync eligibility: `eligible` (boolean) and `eligibility_reasons[]` (reason codes such as `type_disabled`, `channel_disabled`) so ineligible candidates can be flagged.\n\n**Authentication:** Requires Bearer token.\n\n**Query parameters:** `connection_id` (required), `remote_type` (required, e.g. xero_bill), `remote_id` (required — the provider document id), `search` (optional). Returns 404 when connection_id is not a supported accounting connection, and 422 when a required parameter is missing.\n\n**Response:** 200 with `data` as an array of candidate entries ordered most-confident first. Link the chosen entry with POST /api/ledger/reconciliation/link."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/link-candidates?connection_id=30&remote_type=xero_bill&remote_id=85414bb6-6f88-4c0f-8c53-53e7c1f7128c","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","link-candidates"],"query":[{"key":"connection_id","value":"30","description":"Accounting connection id. Required.","disabled":true},{"key":"remote_type","value":"xero_bill","description":"The kind of provider document (e.g. xero_bill, xero_invoice, qbo_invoice). Required.","disabled":true},{"key":"remote_id","value":"85414bb6-6f88-4c0f-8c53-53e7c1f7128c","description":"The provider document id to find SKU entries for. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"entry_id\": 10927,\n      \"reference\": \"FBA181D492B4: 55641\",\n      \"document_number\": \"BILL-10927\",\n      \"type_label\": \"Purchase Order Invoice\",\n      \"date\": \"2024-05-22T07:00:00+00:00\",\n      \"total\": \"3950.0000\",\n      \"currency\": \"USD\",\n      \"amount_match\": true,\n      \"date_match\": true,\n      \"reference_match\": true,\n      \"confident\": true,\n      \"eligible\": false,\n      \"eligibility_reasons\": [\n        \"type_disabled\"\n      ]\n    },\n    {\n      \"entry_id\": 10930,\n      \"reference\": \"FBA181D492B4\",\n      \"document_number\": \"BILL-10930\",\n      \"type_label\": \"Purchase Order Invoice\",\n      \"date\": \"2024-06-01T07:00:00+00:00\",\n      \"total\": \"120.0000\",\n      \"currency\": \"USD\",\n      \"amount_match\": false,\n      \"date_match\": false,\n      \"reference_match\": true,\n      \"confident\": false,\n      \"eligible\": true,\n      \"eligibility_reasons\": []\n    }\n  ]\n}"}]},{"name":"Reconciliation Report","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/report?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","report"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]},"description":"The period-comparison report for a connection over the (optional) document-date window: monthly SKU-ledger totals vs the provider-mirror, with per-period and grand-total variance highlighting. Read entirely off the local provider document cache — no provider API calls.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Shape:** `data.rows[]` is one row per calendar month with activity, most-recent first. Each row carries `sku_entry_count` / `sku_total_debit` / `sku_total_credit` (the matched + SKU-only entries), `provider_document_count` / `provider_total` (the matched remotes + provider-only documents), and the deltas `count_variance` (sku − provider count) and `amount_variance` (sku debit − provider total, 4dp). `has_variance` is true when the counts differ or the amount delta exceeds the configured tolerance (`ledger.sync.dedup.amount_tolerance`, default 0.01).\n\n`data.summary` rolls the same fields up across every period (`period_count`, `periods_with_variance`). `data.currency` is the dominant currency or null when the window is empty. `from`/`to` (Y-m-d) override the default window. 404 when connection_id is not a supported accounting connection."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/report?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","report"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"connection_id\": 5,\n    \"from\": \"2026-01-01\",\n    \"to\": \"2026-06-13\",\n    \"currency\": \"USD\",\n    \"rows\": [\n      {\n        \"period\": \"2026-06\",\n        \"period_label\": \"Jun 2026\",\n        \"sku_entry_count\": 12,\n        \"sku_total_debit\": \"4250.0000\",\n        \"sku_total_credit\": \"4250.0000\",\n        \"provider_document_count\": 12,\n        \"provider_total\": \"4250.0000\",\n        \"count_variance\": 0,\n        \"amount_variance\": \"0.0000\",\n        \"has_variance\": false\n      },\n      {\n        \"period\": \"2026-05\",\n        \"period_label\": \"May 2026\",\n        \"sku_entry_count\": 9,\n        \"sku_total_debit\": \"3100.0000\",\n        \"sku_total_credit\": \"3100.0000\",\n        \"provider_document_count\": 8,\n        \"provider_total\": \"2850.0000\",\n        \"count_variance\": 1,\n        \"amount_variance\": \"250.0000\",\n        \"has_variance\": true\n      }\n    ],\n    \"summary\": {\n      \"period_count\": 2,\n      \"periods_with_variance\": 1,\n      \"sku_entry_count\": 21,\n      \"sku_total_debit\": \"7350.0000\",\n      \"sku_total_credit\": \"7350.0000\",\n      \"provider_document_count\": 20,\n      \"provider_total\": \"7100.0000\",\n      \"count_variance\": 1,\n      \"amount_variance\": \"250.0000\",\n      \"has_variance\": true\n    }\n  }\n}"}]},{"name":"Preview Auto-Match","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/auto-match/preview","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","auto-match","preview"]},"description":"Compute the confident auto-match pairs for a connection WITHOUT linking anything — the preview behind the reconcile Auto-Match confirmation. Returns one row per proposed pairing of an unmatched SKU entry (SKU-only or excluded) to a provider-only document whose reference, amount (within tolerance) and date (within window) confidently match. Review the rows, then send the ones you want to keep to Run Auto-Match to link them.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.sync` permission.\n\n`from`/`to` (Y-m-d) are optional and default to the connection's reconcile window.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 5,\n  \"from\": \"2026-01-01\",\n  \"to\": \"2026-06-13\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/auto-match/preview","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","auto-match","preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"entry_id\": 8213,\n      \"entry_reference\": \"INV-1042\",\n      \"entry_document_number\": \"INV-8213\",\n      \"entry_type_label\": \"Sales Order Invoice\",\n      \"entry_date\": \"2026-06-02T00:00:00+00:00\",\n      \"entry_total\": \"200.00\",\n      \"entry_currency\": \"USD\",\n      \"remote_type\": \"qbo_invoice\",\n      \"remote_id\": \"142\",\n      \"remote_reference\": \"INV-1042\",\n      \"remote_status\": \"Open\",\n      \"remote_date\": \"2026-06-03\",\n      \"remote_total\": \"200.00\",\n      \"remote_currency\": \"USD\",\n      \"remote_url\": \"https://app.qbo.intuit.com/app/invoice?txnId=142\"\n    }\n  ]\n}"}]},{"name":"Run Auto-Match","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/auto-match","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","auto-match"]},"description":"Link the auto-match pairs confirmed from Preview Auto-Match. Send a `matches` array of `{entry_id, remote_type, remote_id}` rows; the eligible set is recomputed and only the intersection is linked, so a pairing that has since become ineligible is skipped rather than force-linked. Returns the number of links forged; each is recorded linked_stale so the next sync pushes an Update. An empty `matches` array links nothing.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.sync` permission.\n\n**Body fields:**\n- `connection_id` (integer, required) — the accounting connection.\n- `matches` (array, required) — confirmed pairs; each `{entry_id, remote_type, remote_id}`. May be empty.\n- `matches[].entry_id` (integer, required) — the SKU ledger entry to link.\n- `matches[].remote_type` (string, required) — provider document type, e.g. `qbo_invoice`, `xero_invoice`.\n- `matches[].remote_id` (string, required) — the provider document id.\n- `from` / `to` (Y-m-d, optional) — window override; defaults to the connection's reconcile window.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 5,\n  \"matches\": [\n    {\n      \"entry_id\": 8213,\n      \"remote_type\": \"qbo_invoice\",\n      \"remote_id\": \"142\"\n    },\n    {\n      \"entry_id\": 8214,\n      \"remote_type\": \"qbo_invoice\",\n      \"remote_id\": \"143\"\n    }\n  ],\n  \"from\": \"2026-01-01\",\n  \"to\": \"2026-06-13\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/auto-match","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","auto-match"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"linked\": 2\n  },\n  \"message\": \"2 documents auto-matched.\"\n}"}]},{"name":"Backfill Provider Cache","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/backfill","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","backfill"]},"description":"Populate the local provider document cache (the xero_/qbo_ mirror) for a connection by dispatching a tracked background job that downloads every remote document over the reconcile window. The reconcile screen, matching, and the period-comparison report all read off this local cache, so this is the on-demand, UI-triggered equivalent of the `ledger:cache:backfill` artisan command. Returns the tracked job log id so the frontend can follow progress via the tracked job log endpoints.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.sync` permission.\n\n`from`/`to` (Y-m-d) are optional and default to the connection's reconcile window (the connection's sync start date through today).","body":{"mode":"raw","raw":"{\n  \"connection_id\": 5\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/backfill","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","backfill"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 8412\n  },\n  \"message\": \"Caching provider documents from Xero — track progress in the job tray.\"\n}"}]},{"name":"Link Remote Document","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/link","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","link"]},"description":"Manually link a SKU entry to an existing provider document — the operator's \"this is the same document\" action. Recorded linked_stale so the next sync pushes an Update bringing the remote into line.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nLinking a document also clears any reconcile-screen dismissal recorded against it.\n\n**Authentication:** Requires Bearer token + `accounting.sync` permission.\n\n422 when the entry already has a link on this connection.\n\n**Ownership validation:** the (remote_type, remote_id) pair must be a cached provider document belonging to the given connection. A document from another connection (or one not yet pulled into the local cache) is rejected with HTTP 422 — pull provider documents for the connection first, then link.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 5,\n  \"entry_id\": 4822,\n  \"remote_type\": \"qbo_invoice\",\n  \"remote_id\": \"146\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/link","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","link"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 78,\n    \"provider\": \"qbo\",\n    \"remote_type\": \"qbo_invoice\",\n    \"remote_id\": \"146\",\n    \"remote_url\": \"https://app.qbo.intuit.com/app/invoice?txnId=145\",\n    \"link_state\": \"linked_stale\",\n    \"remote_status\": \"Active\",\n    \"last_remote_seen_at\": \"2026-06-12T02:00:00+00:00\"\n  },\n  \"message\": \"Linked to the provider document.\"\n}"}]},{"name":"Dismiss Provider Document","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/dismiss","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","dismiss"]},"description":"Dismiss a provider-only document — mark a remote document as \"not a SKU document\" so it stops surfacing in the provider-only bucket. Idempotent on the remote identity.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.sync` permission.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 5,\n  \"remote_type\": \"qbo_invoice\",\n  \"remote_id\": \"146\",\n  \"reason\": \"Pre-existing supplier bill, not SKU-managed\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/dismiss","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","dismiss"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Document dismissed from reconciliation.\"\n}"}]},{"name":"Bulk Dismiss Provider Documents","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/dismiss/bulk","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","dismiss","bulk"]},"description":"Dismiss many provider-only documents from reconciliation in one action (idempotent). Provide either `ids` (an array of composite \"{remote_type}:{remote_id}\" identifiers — the selected rows) or `apply_to_all: true` with `filters` to dismiss every provider-only document matching those filters. An optional `reason` is stored on each.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Body:** `connection_id` (required, integer); `ids` (array of strings, required unless `apply_to_all`); `apply_to_all` (boolean); `filters` (object, optional) — the list filters to scope `apply_to_all`, e.g. `{\"search\": \"Contractor Payment\"}`. Accepts flat filter values (`search`, `remote_type`, `status`, `date_from`, `date_to`) plus an optional `filter_groups` tree (same structure as the list endpoint's `filter_groups` parameter). When omitted, `apply_to_all` targets every document in the reconcile window.; `reason` (string, optional).\n\n**Response:** 200 with the number dismissed.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 5,\n  \"apply_to_all\": true,\n  \"filters\": {\n    \"search\": \"Contractor Payment\"\n  },\n  \"reason\": \"not a SKU document\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/dismiss/bulk","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","dismiss","bulk"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"dismissed\": 1\n  },\n  \"message\": \"1 document dismissed from reconciliation.\"\n}"}]},{"name":"Bulk Restore Provider Documents","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/restore/bulk","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","restore","bulk"]},"description":"Restore (un-dismiss) many provider documents in one action, returning them to reconciliation. Provide either `ids` (composite \"{remote_type}:{remote_id}\" identifiers — the selected rows) or `apply_to_all: true` with `filters` to restore every dismissed document matching those filters.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Body:** `connection_id` (required, integer); `ids` (array of strings, required unless `apply_to_all`); `apply_to_all` (boolean); `filters` (object, optional) — the list filters to scope `apply_to_all`, e.g. `{\"search\": \"Contractor Payment\"}`. Accepts flat filter values (`search`, `remote_type`, `status`, `date_from`, `date_to`) plus an optional `filter_groups` tree (same structure as the list endpoint's `filter_groups` parameter). When omitted, `apply_to_all` targets every document in the reconcile window..\n\n**Response:** 200 with the number restored.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 5,\n  \"apply_to_all\": true,\n  \"filters\": {\n    \"search\": \"Contractor Payment\"\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/restore/bulk","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","restore","bulk"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"restored\": 1\n  },\n  \"message\": \"1 document restored to reconciliation.\"\n}"}]},{"name":"Restore Provider Document","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/restore","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","restore"]},"description":"Undo a dismissal — restore a previously dismissed provider document to the reconcile screen.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.sync` permission.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 5,\n  \"remote_type\": \"qbo_invoice\",\n  \"remote_id\": \"146\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/restore","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","restore"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Document restored to reconciliation.\"\n}"}]},{"name":"List Provider Payments","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/payments?connection_id=2","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","payments"],"query":[{"key":"connection_id","value":"2","description":"Accounting connection (IntegrationInstance) id.","disabled":true}]},"description":"Provider payments (Xero/QBO) from the local mirror for a connection, each flagged with whether it has already been imported as a SKU payment. Backs the Reconciliation > Payments view.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthenticate with a PAT."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/payments?connection_id=2","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","payments"],"query":[{"key":"connection_id","value":"2","description":"Accounting connection (IntegrationInstance) id.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"remote_type\": \"xero_payment\",\n      \"remote_id\": \"19979c8d-b674-4784-a5ed-595a1c5356d4\",\n      \"reference\": \"19979c8d-b674-4784-a5ed-595a1c5356d4\",\n      \"amount\": \"500.0000\",\n      \"date\": \"2026-06-18T00:00:00+00:00\",\n      \"imported\": false\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 5,\n  \"per_page\": 15,\n  \"total\": 74,\n  \"from\": 1,\n  \"to\": 15\n}"}]},{"name":"Import Provider Payments","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/payments/import","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","payments","import"]},"description":"Import provider payments into SKU — create the matching SKU payment on each payment's source document, via the same idempotent pathway as the automatic inbound pull. With remote_ids, only those; omit remote_ids to import every not-yet-imported provider payment. Requires accounting.sync permission.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nResponse data: {attempted, imported}.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 2,\n  \"remote_ids\": [\n    \"19979c8d-b674-4784-a5ed-595a1c5356d4\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/payments/import","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","payments","import"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"attempted\": 1,\n    \"imported\": 1\n  },\n  \"message\": \"1 payment imported into SKU.\"\n}"}]},{"name":"Get Inventory Divergence","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection (IntegrationInstance) id. Required.","disabled":true}]},"description":"Inventory sub-ledger reconciliation (Lane 2). SKU is the authoritative subsidiary ledger for inventory, so this returns one row per inventory control account comparing SKU's per-account inventory value against the provider's live GL balance (pulled from the Xero Trial Balance) — most divergent first.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token. The provider connection must have the `accounting.reports.read` scope.\n\n**Row fields:** `gl_code`, `gl_account_name`, `gl_account_id`, `sku_value` (null when SKU cannot value the account, e.g. Amazon FBA accounts valued via a separate subsystem), `sku_quantity`, `provider_balance`, `divergence` (provider_balance − sku_value; null when sku_value is null), `status` (`divergent` | `matched` | `sku_unavailable`).\n\nThe warehouse→GL mapping is SKU's own `warehouses.nominal_code_id`. A non-zero divergence is the basis for a suggested adjusting journal on the provider side."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection (IntegrationInstance) id. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"gl_code\": \"142\",\n      \"gl_account_name\": \"SMO Inventory (142)\",\n      \"gl_account_id\": \"5e55e704-1a2b-4c3d-9e8f-0a1b2c3d4e5f\",\n      \"sku_value\": 569947.14,\n      \"sku_quantity\": 26463,\n      \"provider_balance\": 1042053.4,\n      \"divergence\": 472106.26,\n      \"status\": \"divergent\"\n    },\n    {\n      \"gl_code\": \"141\",\n      \"gl_account_name\": \"Amazon FBA US Inventory (141)\",\n      \"gl_account_id\": \"d35c45a4-aa11-4b22-8c33-9d44e55f6071\",\n      \"sku_value\": null,\n      \"sku_quantity\": null,\n      \"provider_balance\": 269416,\n      \"divergence\": null,\n      \"status\": \"sku_unavailable\"\n    },\n    {\n      \"gl_code\": \"145\",\n      \"gl_account_name\": \"Amazon FBA CA Inventory (145)\",\n      \"gl_account_id\": \"b312ed80-1122-4334-8556-77889900aabb\",\n      \"sku_value\": null,\n      \"sku_quantity\": null,\n      \"provider_balance\": 8859,\n      \"divergence\": null,\n      \"status\": \"sku_unavailable\"\n    }\n  ],\n  \"success\": true\n}"}]},{"name":"Inventory Recon Accounts (Earmark Suggestions)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/accounts","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","accounts"]},"description":"Candidate inventory control accounts for the daily reconciliation earmark step. Pulls SKU's current-asset nominal codes (plus any warehouse-mapped account), cross-references the provider's cached current-asset chart (Xero `xero_accounts`, matched by code — **no live provider call**), computes each account's point-in-time SKU inventory value as-of now, and flags which look like inventory accounts. Suggested-then-code ordered.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Row fields:** `nominal_code_id`, `code`, `sku_name`, `sku_type`, `provider_account_name`, `provider_account_type`, `provider_account_id` (nullable when no provider match), `warehouse_count`, `warehouse_names[]`, `is_earmarked` (the user's persisted decision), `is_suggested` (heuristic), `suggestion_reasons[]` (strongest first: warehouse-mapped, SKU type Inventory, provider type Inventory, name looks like inventory), `current_sku_value` (null when no warehouse — and therefore no SKU inventory — maps to it). `current_sku_value` sums THREE valuation subsystems, because inventory does not all live in one place: standard warehouse stock (`fifo_layers`), Amazon FBA stock, and Amazon AWD stock (both in `amazon_fifo_layers`). An AWD or FBA control account therefore shows its real value here rather than $0 — before AWD was folded in, the one account that most needed earmarking looked like an empty one."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/accounts","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","accounts"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"nominal_code_id\": 87,\n      \"code\": \"142\",\n      \"sku_name\": \"SMO Inventory\",\n      \"sku_type\": \"Inventory\",\n      \"provider_account_name\": \"SMO Inventory\",\n      \"provider_account_type\": \"INVENTORY\",\n      \"provider_account_id\": \"5e55e704-1a2b-4c3d-9e8f-0a1b2c3d4e5f\",\n      \"warehouse_count\": 1,\n      \"warehouse_names\": [\n        \"ShipMyOrders\"\n      ],\n      \"is_earmarked\": true,\n      \"is_suggested\": true,\n      \"suggestion_reasons\": [\n        \"Mapped to warehouse ShipMyOrders\",\n        \"SKU account type is Inventory\",\n        \"Name looks like inventory\"\n      ],\n      \"current_sku_value\": 569792.24\n    },\n    {\n      \"nominal_code_id\": 88,\n      \"code\": \"141\",\n      \"sku_name\": \"Amazon FBA US Inventory\",\n      \"sku_type\": \"Inventory\",\n      \"provider_account_name\": \"Amazon FBA US Inventory\",\n      \"provider_account_type\": \"INVENTORY\",\n      \"provider_account_id\": \"d35c45a4-aa11-4b22-8c33-9d44e55f6071\",\n      \"warehouse_count\": 0,\n      \"warehouse_names\": [],\n      \"is_earmarked\": false,\n      \"is_suggested\": true,\n      \"suggestion_reasons\": [\n        \"SKU account type is Inventory\",\n        \"Name looks like inventory\"\n      ],\n      \"current_sku_value\": null\n    }\n  ],\n  \"success\": true\n}"}]},{"name":"Save Earmarked Inventory Accounts","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/accounts","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","accounts"]},"description":"Replace the earmarked inventory control account set and return the refreshed candidate list. The submitted set replaces existing earmarks wholesale — an empty array clears them. The earmarked accounts are what the daily series and rebuild job operate on.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.sync` permission.\n\n**Request body:**\n- `nominal_code_ids` (array, **required/present** — may be empty): nominal code ids to earmark. Each must exist in `nominal_codes`.\n\nReturns the same payload shape as `GET .../inventory/accounts` with `is_earmarked` reflecting the new set.","body":{"mode":"raw","raw":"{\n  \"nominal_code_ids\": [\n    87,\n    88\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/accounts","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","accounts"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"nominal_code_id\": 87,\n      \"code\": \"142\",\n      \"sku_name\": \"SMO Inventory\",\n      \"sku_type\": \"Inventory\",\n      \"provider_account_name\": \"SMO Inventory\",\n      \"provider_account_type\": \"INVENTORY\",\n      \"provider_account_id\": \"5e55e704-1a2b-4c3d-9e8f-0a1b2c3d4e5f\",\n      \"warehouse_count\": 1,\n      \"warehouse_names\": [\n        \"ShipMyOrders\"\n      ],\n      \"is_earmarked\": true,\n      \"is_suggested\": true,\n      \"suggestion_reasons\": [\n        \"Mapped to warehouse ShipMyOrders\",\n        \"SKU account type is Inventory\",\n        \"Name looks like inventory\"\n      ],\n      \"current_sku_value\": 569792.24\n    },\n    {\n      \"nominal_code_id\": 88,\n      \"code\": \"141\",\n      \"sku_name\": \"Amazon FBA US Inventory\",\n      \"sku_type\": \"Inventory\",\n      \"provider_account_name\": \"Amazon FBA US Inventory\",\n      \"provider_account_type\": \"INVENTORY\",\n      \"provider_account_id\": \"d35c45a4-aa11-4b22-8c33-9d44e55f6071\",\n      \"warehouse_count\": 0,\n      \"warehouse_names\": [],\n      \"is_earmarked\": false,\n      \"is_suggested\": true,\n      \"suggestion_reasons\": [\n        \"SKU account type is Inventory\",\n        \"Name looks like inventory\"\n      ],\n      \"current_sku_value\": null\n    }\n  ],\n  \"success\": true\n}"}]},{"name":"Inventory Daily Divergence Series","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/daily?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","daily"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection (IntegrationInstance) id. Required.","disabled":true}]},"description":"The materialized daily reconciliation series, one entry per earmarked account (or per `nominal_code_ids[]` when given): SKU's point-in-time inventory value vs the provider GL balance for each end-of-day, and the stored divergence — the \"which days diverge\" view. Reads materialized rows only (**no live provider or valuation call**); the SKU side is built by the rebuild job and the provider side by capture-provider.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nEach day's SKU value uses the same valuation cutoff as the inventory valuation report, so a day ties to running that report for that end-of-day.\n\n**Authentication:** Requires Bearer token.\n\n**Series fields:** `nominal_code_id`, `code`, `name`, `provider_account_name`, `days_total`, `days_divergent`, `days_matched`, `days_provider_missing`, `months_total`, `months_divergent`, `latest_sku_value`, `latest_provider_balance`, `latest_divergence`, `latest_as_of_date`, `points[]`, `has_valuation_source`.\n\n**Point fields:** `as_of_date`, `sku_value`, `sku_quantity`, `provider_balance` (null on days not yet captured), `divergence` (null when either side missing), `status` (`divergent` | `matched` | `provider_missing` | `sku_missing`), `displaced_value`, `sku_value_inventory`.\n\n**Month verdicts.** `months_total` / `months_divergent` judge each calendar month at its LAST comparable point — the month-end close. Under monthly batching every intra-month day diverges by design (the batch journal is dated at period end), so `days_divergent` overstates trouble on a book that closes clean; a month only counts as divergent when its close diverges.\n\nEach account also returns opening_reconciled (boolean): true when the account has a booked reconciliation adjustment, so its opening was reconciled by an adjusting journal rather than being naturally aligned.\n\nEach account also returns has_valuation_source (boolean): false when NO SKU-side reader can claim the account — no warehouse maps to it and it is not the configured Inventory In Transit account — so its `sku_value` is a structural $0 rather than a measured zero. Such an account is never divergent (both sides read 0), which is exactly why it must NOT be rendered as \"Matched\": clients should show a distinct \"No valuation source\" state and suppress the SKU figure. Accounts backed by a warehouse (standard, Amazon FBA, or Amazon AWD) return true.\n\n**Accounting calendar (lock dates).** `sku_value` is stated on the accounting calendar so it is directly comparable with a GL balance. When an accounting lock date pushes a posting past the day the stock actually moved, that value is held back on the SKU side for exactly as long as the GL holds it back — so a lock date can never manufacture a divergence. `sku_value_inventory` is the untouched inventory valuation for the same day (the figure the inventory valuation report shows) and `displaced_value` is the difference between them, so `sku_value_inventory` = `sku_value` + `displaced_value`. On books with no displacement `displaced_value` is `0` and the two values are identical.\n\n**QuickBooks tie-out (`provider_verification`).** On a QuickBooks Online connection each account also returns `provider_verification`: whether its provider series tied out against QuickBooks' own Trial Balance on the last General Ledger pull (opening anchor + every pulled GL line must equal QuickBooks' balance as of the pull's window end, and each month's report must agree with its own running Balance column). Fields: `status` (`verified` | `mismatch` | `check_failed` | `not_checked`), `trusted` (true only for `verified`), `label` (display text, e.g. \"Unverified – QuickBooks balance mismatch\"), `reason` (both numbers when it did not tie), `as_of`, `checked_at`, `provider_balance` (QuickBooks' Trial Balance figure, debit-positive), `computed_balance` (opening + Σ lines), `difference`. When `trusted` is false the provider side is not known to be right, so clients must NOT present that account's divergence as a real difference between SKU and the books. A QuickBooks connection that has never been tied out reports `not_checked` (untrusted). On a Xero connection the field is `null`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/daily?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","daily"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection (IntegrationInstance) id. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"nominal_code_id\": 87,\n      \"code\": \"142\",\n      \"name\": \"SMO Inventory\",\n      \"provider_account_name\": \"SMO Inventory\",\n      \"days_total\": 796,\n      \"days_divergent\": 540,\n      \"days_matched\": 250,\n      \"days_provider_missing\": 6,\n      \"months_total\": 27,\n      \"months_divergent\": 1,\n      \"latest_sku_value\": 569792.24,\n      \"latest_provider_balance\": 1042053.4,\n      \"latest_divergence\": 472261.16,\n      \"latest_as_of_date\": \"2026-06-27\",\n      \"points\": [\n        {\n          \"as_of_date\": \"2024-04-23\",\n          \"sku_value\": 0,\n          \"sku_quantity\": 0,\n          \"provider_balance\": null,\n          \"divergence\": null,\n          \"status\": \"provider_missing\",\n          \"displaced_value\": 0,\n          \"sku_value_inventory\": 0\n        },\n        {\n          \"as_of_date\": \"2026-06-26\",\n          \"sku_value\": 568120.05,\n          \"sku_quantity\": 26390,\n          \"provider_balance\": null,\n          \"divergence\": null,\n          \"status\": \"provider_missing\",\n          \"displaced_value\": 1338.75,\n          \"sku_value_inventory\": 569458.8\n        },\n        {\n          \"as_of_date\": \"2026-06-27\",\n          \"sku_value\": 569792.24,\n          \"sku_quantity\": 26463,\n          \"provider_balance\": 1042053.4,\n          \"divergence\": 472261.16,\n          \"status\": \"divergent\",\n          \"displaced_value\": 1338.75,\n          \"sku_value_inventory\": 571130.99\n        }\n      ],\n      \"months\": [\n        {\n          \"month\": \"2025-01\",\n          \"gap\": 0\n        },\n        {\n          \"month\": \"2025-07\",\n          \"gap\": 319.79\n        },\n        {\n          \"month\": \"2026-08\",\n          \"gap\": -5621.38\n        }\n      ],\n      \"opening_reconciled\": false,\n      \"has_valuation_source\": true,\n      \"provider_verification\": {\n        \"status\": \"mismatch\",\n        \"trusted\": false,\n        \"label\": \"Unverified – QuickBooks balance mismatch\",\n        \"reason\": \"QuickBooks balance mismatch: QuickBooks reports 1,042,053.40 as of the window end, but the opening balance 1,000,000.00 plus the pulled General Ledger lines 42,253.40 gives 1,042,253.40 (difference 200.00).\",\n        \"as_of\": \"2026-06-27\",\n        \"checked_at\": \"2026-06-27T09:14:03+00:00\",\n        \"provider_balance\": 1042053.4,\n        \"computed_balance\": 1042253.4,\n        \"difference\": 200\n      }\n    }\n  ],\n  \"success\": true\n}"}]},{"name":"Get Inventory Reconciliation Day Detail","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/day-detail?connection_id=5&nominal_code_id=87&date=2026-06-27","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","day-detail"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"nominal_code_id","value":"87","description":"The earmarked inventory control account (nominal code) id to explain. Required.","disabled":true},{"key":"date","value":"2026-06-27","description":"The local day to explain (date). Required.","disabled":true}]},"description":"For one earmarked inventory control account on one day, returns the reconciled figures for that day (SKU inventory value, provider GL balance, and their divergence) plus the underlying detail that produced them: the provider journal lines posted to the account that day and the inventory movements that changed SKU's value that day. Use it to trace a divergence to its source documents — reads materialized/cached data only, no live provider call.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Response fields:** `as_of_date`, `nominal_code_id`, `code`, `name`, `provider_account_name`, `sku_value`, `sku_quantity`, `provider_balance`, `divergence`, `provider_net_total` (sum of the day's provider journal-line net amounts), `sku_value_delta_total` (sum of the day's movement value deltas), `provider_lines[]` (`journal_number`, `journal_date`, `source_type`, `source_id`, `account_code`, `account_name`, `net_amount`, `gross_amount`, `tax_amount`), `sku_movements[]` (`id`, `movement_date`, `type`, `warehouse_name`, `product_sku`, `product_name`, `quantity`, `unit_cost`, `value_delta`, `reference`, `link_type`, `link_id`).\n\nEach provider line and SKU movement carries a match_group: lines of the same signed amount are paired (shared match_group id) and null means unmatched. Matched pairs net to zero, so the day's gap is exactly unmatched_provider_total minus unmatched_sku_total — an unmatched provider line was booked in the accounting GL with no SKU-side movement, and an unmatched SKU movement changed SKU inventory but has not reached the GL. matched_count and matched_amount summarise what reconciles.\n\n`displaced_value` is how much of the day's inventory value the general ledger has not recognised yet because an accounting lock date pushed its posting to a later day. `sku_value` already excludes it so both sides of the day sit on the same calendar; the amount is reported here so the day can explain why its stock is worth more than its ledger says. It is `0` on books with no displacement."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/day-detail?connection_id=5&nominal_code_id=87&date=2026-06-27","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","day-detail"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"nominal_code_id","value":"87","description":"The earmarked inventory control account (nominal code) id to explain. Required.","disabled":true},{"key":"date","value":"2026-06-27","description":"The local day to explain (date). Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"as_of_date\": \"2026-06-27\",\n    \"nominal_code_id\": 87,\n    \"code\": \"142\",\n    \"name\": \"SMO Inventory\",\n    \"provider_account_name\": \"SMO Inventory\",\n    \"sku_value\": 569792.24,\n    \"sku_quantity\": 41250,\n    \"provider_balance\": 571675.22,\n    \"divergence\": 1882.98,\n    \"provider_net_total\": 5000,\n    \"sku_value_delta_total\": 3053.74,\n    \"provider_lines\": [\n      {\n        \"journal_number\": \"212538\",\n        \"journal_date\": \"2026-06-27\",\n        \"source_type\": \"ACCREC\",\n        \"source_id\": \"b1f2c0d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d\",\n        \"account_code\": \"142\",\n        \"account_name\": \"SMO Inventory\",\n        \"net_amount\": 3117.02,\n        \"gross_amount\": 3117.02,\n        \"tax_amount\": 0,\n        \"match_group\": 1\n      },\n      {\n        \"journal_number\": \"212540\",\n        \"journal_date\": \"2026-06-27\",\n        \"source_type\": \"ManualJournal\",\n        \"source_id\": \"c2a3b4c5-6d7e-4f80-91a2-b3c4d5e6f7a8\",\n        \"account_code\": \"142\",\n        \"account_name\": \"SMO Inventory\",\n        \"net_amount\": 1882.98,\n        \"gross_amount\": 1882.98,\n        \"tax_amount\": 0,\n        \"match_group\": null\n      }\n    ],\n    \"sku_movements\": [\n      {\n        \"id\": 3068,\n        \"movement_date\": \"2026-06-27T08:02:00.000000Z\",\n        \"type\": \"sale\",\n        \"warehouse_name\": \"ShipMyOrders Warehouse\",\n        \"product_sku\": \"TM-191-O-124\",\n        \"product_name\": \"Titanium Mount 191\",\n        \"quantity\": -42,\n        \"unit_cost\": 74.215,\n        \"value_delta\": 3117.02,\n        \"reference\": \"SO-10442\",\n        \"link_type\": \"SalesOrderLine\",\n        \"link_id\": 88231,\n        \"match_group\": 1\n      },\n      {\n        \"id\": 3071,\n        \"movement_date\": \"2026-06-27T09:14:00.000000Z\",\n        \"type\": \"adjustment\",\n        \"warehouse_name\": \"ShipMyOrders Warehouse\",\n        \"product_sku\": \"TM-191-O-124\",\n        \"product_name\": \"Titanium Mount 191\",\n        \"quantity\": -1,\n        \"unit_cost\": 63.28,\n        \"value_delta\": -63.28,\n        \"reference\": \"3071\",\n        \"link_type\": \"InventoryAdjustment\",\n        \"link_id\": 3071,\n        \"match_group\": null\n      }\n    ],\n    \"matched_count\": 1,\n    \"matched_amount\": 3117.02,\n    \"unmatched_provider_total\": 1882.98,\n    \"unmatched_sku_total\": -63.28,\n    \"displaced_value\": 1338.75\n  },\n  \"success\": true\n}"}]},{"name":"Get Inventory Divergence Breakdown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/divergence-breakdown?connection_id=5&nominal_code_id=87","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","divergence-breakdown"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"nominal_code_id","value":"87","description":"The earmarked inventory control account id to decompose. Required.","disabled":true}]},"description":"The ongoing divergence for one earmarked inventory control account, decomposed into buckets that reconcile to the total: the reconciled opening, awaiting-sync postings that self-resolve once they reach the provider, permanently excluded postings (before the start date / internal-only), and the residual variance to investigate. Also returns a ranked worklist of the days that contributed most to the gap (largest impact first); each day's gap sums back to the ongoing movement, and the untruncated tail is carried in other_days_gap so the list always ties to the total. Authenticate with a Personal Access Token. Reads cached data only (no live provider call).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/divergence-breakdown?connection_id=5&nominal_code_id=87","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","divergence-breakdown"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"nominal_code_id","value":"87","description":"The earmarked inventory control account id to decompose. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"nominal_code_id\": 87,\n    \"code\": \"142\",\n    \"name\": \"SMO Inventory\",\n    \"provider_account_name\": \"SMO Inventory\",\n    \"as_of_date\": \"2026-06-27\",\n    \"opening_divergence\": 0,\n    \"opening_reconciled\": true,\n    \"raw_divergence\": 446497.65,\n    \"pending_ledger\": -1010.56,\n    \"excluded_ledger\": 538152.01,\n    \"residual\": 445487.09,\n    \"ongoing_movement\": 446497.65,\n    \"days\": [\n      {\n        \"as_of_date\": \"2025-12-12\",\n        \"provider_net\": 0,\n        \"sku_delta\": -262899.87,\n        \"daily_gap\": 262899.87,\n        \"classification\": \"sku_only\",\n        \"divergence_after\": 262899.87,\n        \"share\": 0.5888\n      },\n      {\n        \"as_of_date\": \"2025-12-15\",\n        \"provider_net\": 0,\n        \"sku_delta\": -213112.9,\n        \"daily_gap\": 213112.9,\n        \"classification\": \"sku_only\",\n        \"divergence_after\": 476012.77,\n        \"share\": 0.4773\n      },\n      {\n        \"as_of_date\": \"2026-03-14\",\n        \"provider_net\": 5000,\n        \"sku_delta\": 0,\n        \"daily_gap\": 5000,\n        \"classification\": \"provider_only\",\n        \"divergence_after\": 451497.65,\n        \"share\": 0.0112\n      }\n    ],\n    \"returned_days_count\": 30,\n    \"other_days_count\": 580,\n    \"other_days_gap\": -37361.8\n  },\n  \"success\": true\n}"}]},{"name":"Get Inventory Reconciliation Chain","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/chain?connection_id=5&nominal_code_id=87","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","chain"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"nominal_code_id","value":"87","description":"The earmarked inventory control account id. Required.","disabled":true}]},"description":"The cumulative reconciliation CHAIN for one earmarked inventory control account, since the baseline: SKU inventory moved (physical) -> SKU ledger moved (SKU's own accounting books) -> provider (Xero) moved (actual GL). Two checkpoints fall out of it: check_a = ledger - inventory (does SKU's ledger reflect its inventory? an internal booking discrepancy) and check_b = provider - ledger (did the ledger reach the provider? sync status plus any direct-in-Xero or synced-then-deleted residual). divergence = provider - inventory = check_a + check_b. The ledger split (synced/stale/awaiting/excluded) shows how much of Check B is just pending sync: ledger_moved (and therefore both checks) counts synced + stale + awaiting, where ledger_stale is content synced once but restated since and not yet re-pushed. ledger_excluded is an internal-only lane - postings that never reach the provider by design (e.g. write-off entries) - reported as its own labeled reconciling item and counted into neither check; the divergence identity is unaffected. `rows` give the per-transaction-type detail behind each. Done cumulatively so the monthly batch-sync lag washes out. Authenticate with a Personal Access Token. Reads cached data only. Every measure above is a MOVEMENT since the baseline; the balance fields are the absolute closing positions those movements land on — what inventory is actually worth in SKU (sku_balance) against what the provider's account says (provider_balance), and the resulting balance_difference = provider_balance - sku_balance. The opening trio (opening_sku_balance, opening_provider_balance, opening_difference) states the same three at the baseline. Because divergence only measures how much the gap GREW since the baseline, the total gap on the books is balance_difference = opening_difference + divergence — the two coincide only when the baseline opened reconciled.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nThe inventory side is stated on the accounting calendar, so a lock date that pushed a posting past the day its stock moved cannot break check_a — the value is held back on both sides until the GL recognises it. `displaced_close` is how much is being held back at as_of_date, `displaced_open` is how much was held back at the baseline, and `displaced_moved` is the difference (`displaced_close` − `displaced_open`) — how much the held-back amount grew since the baseline. `inventory_moved` + `displaced_moved` is the raw inventory movement before that restatement. All three are `0` on books with no displacement."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/chain?connection_id=5&nominal_code_id=87","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","chain"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"nominal_code_id","value":"87","description":"The earmarked inventory control account id. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"nominal_code_id\": 87,\n    \"code\": \"142\",\n    \"name\": \"SMO Inventory\",\n    \"provider_account_name\": \"SMO Inventory\",\n    \"baseline_date\": \"2025-01-01\",\n    \"as_of_date\": \"2026-07-18\",\n    \"inventory_moved\": -508973.54,\n    \"ledger_moved\": -103311.54,\n    \"ledger_synced\": 310779.3,\n    \"ledger_stale\": 0,\n    \"ledger_awaiting\": -414090.84,\n    \"ledger_excluded\": 0,\n    \"provider_moved\": 414576.4,\n    \"check_a\": 405662,\n    \"check_b\": 517887.94,\n    \"divergence\": 923549.94,\n    \"sku_balance\": 103506.57,\n    \"provider_balance\": 1027096.51,\n    \"balance_difference\": 923589.94,\n    \"opening_sku_balance\": 612480.11,\n    \"opening_provider_balance\": 612520.11,\n    \"opening_difference\": 40,\n    \"rows\": [\n      {\n        \"concept_key\": \"receipt\",\n        \"label\": \"Purchase receipts\",\n        \"sku_moved\": 727295.15,\n        \"provider_moved\": 487353.25,\n        \"gl_posted\": 151909.08,\n        \"gl_synced\": 151909.08,\n        \"gl_awaiting\": 0,\n        \"gl_excluded\": 0,\n        \"entry_count\": 42,\n        \"is_batched\": true,\n        \"sync_status\": \"synced\"\n      },\n      {\n        \"concept_key\": \"transfer\",\n        \"label\": \"Warehouse transfers\",\n        \"sku_moved\": -1132840.13,\n        \"provider_moved\": 0,\n        \"gl_posted\": 0,\n        \"gl_synced\": 0,\n        \"gl_awaiting\": 0,\n        \"gl_excluded\": 0,\n        \"entry_count\": 0,\n        \"is_batched\": false,\n        \"sync_status\": \"none\"\n      },\n      {\n        \"concept_key\": \"fulfillment\",\n        \"label\": \"Sales fulfillments\",\n        \"sku_moved\": -219148.4,\n        \"provider_moved\": -125384.37,\n        \"gl_posted\": -92787.33,\n        \"gl_synced\": -30000,\n        \"gl_awaiting\": -62787.33,\n        \"gl_excluded\": 0,\n        \"entry_count\": 90,\n        \"is_batched\": true,\n        \"sync_status\": \"partial\"\n      }\n    ],\n    \"displaced_open\": 0,\n    \"displaced_close\": 1338.75,\n    \"displaced_moved\": 1338.75\n  },\n  \"success\": true\n}"}]},{"name":"Get Inventory Reconciliation Period","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/period?connection_id=5&nominal_code_id=87&month=2025-01","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","period"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"nominal_code_id","value":"87","description":"The earmarked inventory control account id to reconcile. Required.","disabled":true},{"key":"month","value":"2025-01","description":"The period to reconcile. Accepts a month as YYYY-MM (e.g. 2025-03) or a whole year as YYYY (e.g. 2025). A year returns that year's own movement, equal to the sum of its twelve months. Required.","disabled":true}]},"description":"The \"reconcile ongoing\" discovery for one earmarked inventory control account and month. The header is authoritative and always reconciles: during the month the provider balance moved provider_moved, SKU inventory moved sku_moved, and the divergence went divergence_open -> divergence_close (a gap of provider_moved - sku_moved). The rows break the month's inventory postings down by transaction-type concept, framed by sync status: how much SKU inventory value moved for each concept (context) and where its postings sit in the sync pipeline (gl_synced reached the provider, gl_awaiting is a batch still closing or a manually-syncable opt-out, gl_excluded is structurally held back). Each row also carries syncable_count — how many of its entries can be pushed to the provider right now (the same eligibility the bulk sync_now action enforces), which is always <= the awaiting count because an entry waiting on its batch to close cannot be pushed on its own. sync_status is the row's headline disposition and normally follows the amounts, but when they all net to zero while entry_count > 0 (e.g. a warehouse transfer whose out and in legs cancel on this one account) it follows the entries' own dispositions instead — so a row that still owes the provider a sync never reads as none (\"no postings\"), and none is reserved for a concept with no SKU postings at all. Postings are keyed by their batch period (a monthly batch's consolidated entry is dated at a period boundary), the opening reconciliation adjustment is excluded, and the totals reconcile the ledger side. Authenticate with a Personal Access Token. Reads cached data only (no live provider call).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nEvery SKU figure in the header is stated on the accounting calendar: where a lock date pushed a posting out of the month its stock moved in, the value is reported in the month the GL recognises it, so the two sides never disagree over timing that the lock itself created. `displaced_open` and `displaced_close` are how much value the lock is holding out of the GL at each end of the month, `displaced_moved` is the difference between them, and `sku_moved` + `displaced_moved` is the raw inventory movement for the month before that restatement. All three are `0` on books with no displacement.\n\nEach row also carries `type_breakdown` — the ledger types it collapses, when it collapses more than one (Amazon FBA groups shipments, receipts, returns, adjustments and transfers). Those rows carry what is measurable per type: what was posted, what reached the accounting provider, and Check B between them. Rows also carry `sku_moved` and `check_a` where inventory movement is attributable per type — Amazon FBA is, because each stock layer records the event that created it and each consumption the event that consumed it. Both are `null` where it is not, which is not the same as zero: null means the inventory side cannot be asked at this grain, while zero means the two sides agree."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/period?connection_id=5&nominal_code_id=87&month=2025-01","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","period"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"nominal_code_id","value":"87","description":"The earmarked inventory control account id to reconcile. Required.","disabled":true},{"key":"month","value":"2025-01","description":"The period to reconcile. Accepts a month as YYYY-MM (e.g. 2025-03) or a whole year as YYYY (e.g. 2025). A year returns that year's own movement, equal to the sum of its twelve months. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"nominal_code_id\": 87,\n    \"code\": \"142\",\n    \"name\": \"SMO Inventory\",\n    \"provider_account_name\": \"SMO Inventory\",\n    \"month\": \"2025-01\",\n    \"month_label\": \"Jan 2025\",\n    \"divergence_open\": 0,\n    \"divergence_close\": -5066.73,\n    \"gap\": -5066.73,\n    \"sku_balance_open\": 118579.12,\n    \"sku_balance_close\": 113506.57,\n    \"provider_balance_open\": 118579.12,\n    \"provider_balance_close\": 108439.84,\n    \"provider_moved\": -10139.28,\n    \"sku_moved\": -5072.55,\n    \"rows\": [\n      {\n        \"concept_key\": \"fulfillment\",\n        \"label\": \"Sales fulfillments\",\n        \"sku_moved\": -9976.04,\n        \"provider_moved\": -10139.28,\n        \"gl_posted\": -9920.55,\n        \"gl_synced\": 0,\n        \"gl_stale\": 0,\n        \"gl_awaiting\": -9920.55,\n        \"gl_excluded\": 0,\n        \"entry_count\": 6,\n        \"syncable_count\": 6,\n        \"is_batched\": true,\n        \"sync_status\": \"awaiting\",\n        \"type_breakdown\": [\n          {\n            \"type\": \"fba_shipments\",\n            \"label\": \"FBA Shipments\",\n            \"sku_moved\": -217.64,\n            \"check_a\": 0,\n            \"gl_posted\": -217.64,\n            \"gl_synced\": 0,\n            \"gl_stale\": -217.64,\n            \"gl_awaiting\": 0,\n            \"gl_excluded\": 0,\n            \"provider_moved\": -195.1,\n            \"check_b\": 22.54,\n            \"entry_count\": 1,\n            \"syncable_count\": 1,\n            \"is_batched\": true,\n            \"sync_status\": \"stale\"\n          },\n          {\n            \"type\": \"fba_vendor_returns\",\n            \"label\": \"FBA Vendor Returns\",\n            \"sku_moved\": -125.48,\n            \"check_a\": 0,\n            \"gl_posted\": -125.48,\n            \"gl_synced\": 0,\n            \"gl_stale\": 0,\n            \"gl_awaiting\": -125.48,\n            \"gl_excluded\": 0,\n            \"provider_moved\": 0,\n            \"check_b\": 125.48,\n            \"entry_count\": 1,\n            \"syncable_count\": 1,\n            \"is_batched\": true,\n            \"sync_status\": \"awaiting\"\n          }\n        ]\n      },\n      {\n        \"concept_key\": \"adjustment\",\n        \"label\": \"Inventory adjustments\",\n        \"sku_moved\": 7530.53,\n        \"provider_moved\": 0,\n        \"gl_posted\": -282.54,\n        \"gl_synced\": 0,\n        \"gl_stale\": 0,\n        \"gl_awaiting\": -282.54,\n        \"gl_excluded\": 0,\n        \"entry_count\": 4,\n        \"syncable_count\": 1,\n        \"is_batched\": true,\n        \"sync_status\": \"awaiting\",\n        \"type_breakdown\": []\n      },\n      {\n        \"concept_key\": \"manufacturing\",\n        \"label\": \"Manufacturing\",\n        \"sku_moved\": -2627.04,\n        \"provider_moved\": 0,\n        \"gl_posted\": 0,\n        \"gl_synced\": 0,\n        \"gl_stale\": 0,\n        \"gl_awaiting\": 0,\n        \"gl_excluded\": 0,\n        \"entry_count\": 0,\n        \"syncable_count\": 0,\n        \"is_batched\": false,\n        \"sync_status\": \"none\",\n        \"type_breakdown\": []\n      }\n    ],\n    \"sku_moved_total\": -5072.55,\n    \"gl_synced_total\": 0,\n    \"gl_stale_total\": 0,\n    \"gl_awaiting_total\": -10203.09,\n    \"gl_excluded_total\": 0,\n    \"displaced_open\": 0,\n    \"displaced_close\": 431.2,\n    \"displaced_moved\": 431.2\n  },\n  \"success\": true\n}"}]},{"name":"Get Inventory Reconciliation Period Entries","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/period-entries?connection_id=5&nominal_code_id=87&month=2025-01&concept=fulfillment","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","period-entries"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"nominal_code_id","value":"87","description":"The earmarked inventory control account id. Required.","disabled":true},{"key":"month","value":"2025-01","description":"The period to drill into. Accepts a month as YYYY-MM (e.g. 2025-03) or a whole year as YYYY (e.g. 2025); the entry window matches whichever period the summary charged. Required.","disabled":true},{"key":"concept","value":"fulfillment","description":"The transaction-type concept whose entries to list (e.g. fulfillment, receipt, adjustment, revaluation, stock_take, transfer, customer_return, vendor_return, manufacturing, fba). Required.","disabled":true}]},"description":"The individual ledger entries behind ONE transaction-type concept of a period's ongoing reconciliation, with both sides of the comparison on every row.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nEach entry carries what SKU booked on this account (net_amount) alongside what the accounting provider actually recorded for that same entry (provider_amount, matched through the sync link) and the difference between them (check_b). That is what makes a residual traceable to specific postings instead of stopping at the transaction type.\n\nprovider_amount is null when no provider document is linked to the entry — meaning nothing reached the provider, so the whole net_amount is outstanding. This is NOT the same as the provider recording 0.00, which comes back as 0. check_b follows the same rule and is null exactly when provider_amount is. Treat the two cases differently when rendering.\n\nprovider_journal_date is when the provider posted it, which for a batched type routinely trails the entry's own date by a month — batches close in one period and sync in the next. That lag is expected and is not a gap.\n\ncan_sync is whether the entry can be pushed to the provider right now — the same eligibility the bulk sync action enforces — so an already-synced entry is false, and an entry whose sync_status is awaiting can still be false when it belongs to a batch that has not closed yet (it syncs with the batch, not on its own). An entry with net_amount 0 is still a real posting (its debit and credit legs cancel on this one account) and can be pushed like any other.\n\nPaginated, and sorted, filtered and searched across the WHOLE set before the page is cut. The meta block re-states the concept's own summary figures (so this can never contradict the period summary) and reports set-wide counts a single page cannot express — including syncable_count, which respects the active status filter.\n\nAuthenticate with a Personal Access Token. Reads cached data only (no live provider call).\n\nEach posting also carries the inventory side of the accounting period it covers: sku_moved is what SKU's stock did over that period and check_a is net_amount minus it. Both are null — never 0 — when the period holds more than one posting of the scope requested, because nothing in an inventory movement says which of them it belongs to; narrowing with `type` is usually what makes them answerable. meta.check_a_per_posting says whether any posting in the set carries them, and meta.sku_unattributed_to_period is how much of the period-scoped inventory movement lands in no period at all (stock in transit is valued as a running balance, and an accounting lock can shift a posting out of the period its stock moved in)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/period-entries?connection_id=5&nominal_code_id=87&month=2025-01&concept=fulfillment","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","period-entries"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"nominal_code_id","value":"87","description":"The earmarked inventory control account id. Required.","disabled":true},{"key":"month","value":"2025-01","description":"The period to drill into. Accepts a month as YYYY-MM (e.g. 2025-03) or a whole year as YYYY (e.g. 2025); the entry window matches whichever period the summary charged. Required.","disabled":true},{"key":"concept","value":"fulfillment","description":"The transaction-type concept whose entries to list (e.g. fulfillment, receipt, adjustment, revaluation, stock_take, transfer, customer_return, vendor_return, manufacturing, fba). Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"journal_entry_id\": 88231,\n      \"number\": \"JE-88231\",\n      \"reference\": \"FBA-USD-CH16/R2\",\n      \"document_number\": \"BATCH-546\",\n      \"description\": \"Sales Order Fulfillments — Amazon FBA US for Aug 2025\",\n      \"counterparty_name\": \"Amazon FBA US\",\n      \"date\": \"2025-08-01\",\n      \"posted_at\": \"2025-08-31\",\n      \"net_amount\": 110228.84,\n      \"provider_amount\": null,\n      \"check_b\": null,\n      \"provider_journal_number\": null,\n      \"provider_journal_date\": null,\n      \"provider_journal_count\": 0,\n      \"provider_link\": null,\n      \"sync_status\": \"awaiting\",\n      \"sync_status_reason\": null,\n      \"last_sync_error\": null,\n      \"source_type_label\": \"Ledger Batch\",\n      \"is_batch\": true,\n      \"batch_id\": 546,\n      \"batch_members_count\": 34,\n      \"batch_period_label\": \"Aug 2025 · monthly · USD\",\n      \"account_debit\": 110228.84,\n      \"account_credit\": 0,\n      \"account_line_count\": 2,\n      \"total_line_count\": 6,\n      \"can_sync\": true,\n      \"entry_route\": \"/ledger/entries/88231\",\n      \"batch_route\": \"/ledger/batches/546\",\n      \"sku_moved\": null,\n      \"check_a\": null,\n      \"period_label\": \"Aug 2025\"\n    },\n    {\n      \"journal_entry_id\": 88104,\n      \"number\": \"JE-88104\",\n      \"reference\": \"FBA-USD-CH16/R1\",\n      \"document_number\": \"BATCH-571\",\n      \"description\": \"Sales Order Fulfillments — Amazon FBA US for Mar 2025\",\n      \"counterparty_name\": \"Amazon FBA US\",\n      \"date\": \"2025-03-01\",\n      \"posted_at\": \"2025-03-31\",\n      \"net_amount\": -116148.05,\n      \"provider_amount\": -116148.05,\n      \"check_b\": 0,\n      \"provider_journal_number\": \"MJ-4471\",\n      \"provider_journal_date\": \"2025-04-02\",\n      \"provider_journal_count\": 1,\n      \"provider_link\": {\n        \"provider\": \"Xero\",\n        \"remote_type\": \"xero_manual_journal\",\n        \"remote_id\": \"5f2c9d1e-1a4b-4c0e-9c11-8a7f2b6d4e33\",\n        \"link_type_label\": \"Manual Journal\",\n        \"label\": \"MJ-4471\",\n        \"link_route\": \"/xero/manual-journals/812\",\n        \"remote_url\": \"https://go.xero.com/Journal/View.aspx?journalID=5f2c9d1e-1a4b-4c0e-9c11-8a7f2b6d4e33\"\n      },\n      \"sync_status\": \"synced\",\n      \"sync_status_reason\": null,\n      \"last_sync_error\": null,\n      \"source_type_label\": \"Ledger Batch\",\n      \"is_batch\": true,\n      \"batch_id\": 571,\n      \"batch_members_count\": 41,\n      \"batch_period_label\": \"Mar 2025 · monthly · USD\",\n      \"account_debit\": 0,\n      \"account_credit\": 116148.05,\n      \"account_line_count\": 2,\n      \"total_line_count\": 6,\n      \"can_sync\": false,\n      \"entry_route\": \"/ledger/entries/88104\",\n      \"batch_route\": \"/ledger/batches/571\",\n      \"sku_moved\": -116148.05,\n      \"check_a\": 0,\n      \"period_label\": \"Mar 2025\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 17,\n  \"per_page\": 25,\n  \"total\": 420,\n  \"from\": 1,\n  \"to\": 25,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/ledger/reconciliation/inventory/period-entries?page=1\",\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/ledger/reconciliation/inventory/period-entries?page=17\",\n  \"next_page_url\": \"{{protocol}}{{domain}}/api/ledger/reconciliation/inventory/period-entries?page=2\",\n  \"prev_page_url\": null,\n  \"path\": \"{{protocol}}{{domain}}/api/ledger/reconciliation/inventory/period-entries\",\n  \"links\": [],\n  \"meta\": {\n    \"concept_key\": \"fulfillment\",\n    \"label\": \"Sales fulfillments\",\n    \"period_label\": \"2025\",\n    \"provider_name\": \"Xero\",\n    \"sku_moved\": -52848.99,\n    \"gl_posted\": -43854.93,\n    \"provider_moved\": -44416.12,\n    \"check_a\": 8994.06,\n    \"check_b\": -561.19,\n    \"entry_count\": 420,\n    \"filtered_count\": 420,\n    \"syncable_count\": 11,\n    \"provider_linked_total\": -43293.74,\n    \"filtered_total\": -43854.93,\n    \"is_batched\": true,\n    \"check_a_per_posting\": true,\n    \"sku_unattributed_to_period\": 0\n  }\n}"}]},{"name":"Get Inventory Reconciliation Awaiting-Sync Items","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/unsynced?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","unsynced"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]},"description":"Per earmarked inventory account, the SKU ledger activity not reflected in the provider balance, split by sync disposition: `pending_ledger`/`pending_count` WILL reach the provider once it syncs (self-resolving), while `excluded_ledger`/`excluded_count` will NEVER sync (dated before the books' sync start date, or internal-only) — a permanent mismatch a measure-from baseline sets aside. Batch-aggregate aware (a batched document counts once, via its batch). Requires connection_id.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/unsynced?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","unsynced"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"as_of_date\": \"2026-07-20\",\n    \"accounts\": [\n      {\n        \"nominal_code_id\": 87,\n        \"code\": \"141\",\n        \"name\": \"Amazon FBA US Inventory\",\n        \"pending_ledger\": 0,\n        \"excluded_ledger\": -299950.38,\n        \"pending_count\": 0,\n        \"excluded_count\": 41\n      },\n      {\n        \"nominal_code_id\": 88,\n        \"code\": \"142\",\n        \"name\": \"SMO Inventory\",\n        \"pending_ledger\": -1010.56,\n        \"excluded_ledger\": 1498366.15,\n        \"pending_count\": 2,\n        \"excluded_count\": 239\n      }\n    ]\n  }\n}"}]},{"name":"Get Inventory Reconciliation Opening Breakdown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/opening-breakdown?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","opening-breakdown"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]},"description":"The opening reconciliation breakdown for inventory vs the accounting provider — a 3-way comparison per earmarked inventory control account at the opening date: the SKU inventory valuation (what the stock is worth), the internal ledger balance (what has been journaled), and the provider general-ledger balance (what has synced), plus the divergence between provider and valuation. The SKU valuation sums every valuation stream for the account: warehouse stock (standard and Amazon FBA inventory, broken down per warehouse) plus the account-level positions with no warehouse behind them — inventory in transit (including Amazon FC-to-FC and AWD-to-FBA stock in flight), Amazon AWD stock, prepaid purchases (invoiced but not yet received) and goods received not invoiced — so it ties to the Inventory Valuation report at the same date. The provider balance and divergence are reported at the account level, since a provider account has no warehouse dimension. Warehouses that currently hold no stock are still listed so an account's full warehouse footprint is visible; an account valued only by account-level streams returns an empty warehouse list with a non-zero valuation.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nEvery figure is an end-of-day position, so a window's opening is the close of the day before its first in-scope day. The opening date is the `as_of` parameter when given (a period opening passes the prior December 31); otherwise the day before the reconciliation baseline when one has been set, otherwise the day before the configured inventory start date. Reads materialized/cached data only — no live provider call.\n\n**Authentication:** Requires Bearer token.\n\n**Query parameters:**\n- `connection_id` (integer, **required**): accounting connection id.\n- `as_of` (date, optional): pin the opening to this day. Format: Y-m-d.\n\n**Response fields:** `as_of_date` (the effective opening date, null when no accounts are earmarked or no opening date is configured), `from_baseline` (whether `as_of_date` came from a set baseline rather than the `as_of` parameter or the inventory start date), `accounts[]` — each with `nominal_code_id`, `code`, `name`, `provider_account_name`, `sku_value` (inventory valuation, the sum of its warehouses), `sku_quantity`, `ledger_balance` (the internal ledger balance at the same cutoff — every posting regardless of whether it has synced to the provider yet), `provider_balance`, `divergence` (provider_balance minus sku_value, null when no provider balance is available), `point_as_of_date` (the day the provider balance was measured — the earliest captured day on/after the opening), `provider_captured_at` (when the provider balance was last taken from the provider) and `provider_source` (`xero_trial_balance` = captured live from the provider's Trial Balance; `xero_journals` = rebuilt from the synced journal cache), and `warehouses[]` (`warehouse_id`, `warehouse_name`, `is_fba`, `sku_value`, `sku_quantity`). Each account also carries `opening_adjustments[]` — the standing reconciling adjustments dated after the opening that cover its gap (reversals already netted out), each with `journal_entry_id`, `effective_date` and signed `amount` — and their sum `opening_adjustment_net`. Each account also carries `pending_ledger` (unsynced activity dated on/before the opening that WILL sync — self-resolving lag) and `excluded_ledger` (activity that will never sync — the pre-baseline mismatch a baseline sets aside).\n\nThe three balances split the total opening gap into a posting gap (`ledger_balance` − `sku_value`: value the stock carries that was never journaled) and a sync gap (`provider_balance` − `ledger_balance`: postings that haven't reached the provider). Posting gap + sync gap = `divergence`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/opening-breakdown?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","opening-breakdown"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"as_of_date\": \"2024-04-22\",\n    \"from_baseline\": true,\n    \"accounts\": [\n      {\n        \"nominal_code_id\": 88,\n        \"code\": \"141\",\n        \"name\": \"Amazon FBA US Inventory\",\n        \"provider_account_name\": \"Amazon FBA US Inventory\",\n        \"sku_value\": 4024.84,\n        \"sku_quantity\": 65,\n        \"ledger_balance\": 4024.84,\n        \"provider_balance\": 0,\n        \"divergence\": -4024.84,\n        \"pending_ledger\": 0,\n        \"excluded_ledger\": 0,\n        \"opening_adjustments\": [\n          {\n            \"journal_entry_id\": 255468,\n            \"effective_date\": \"2025-01-01\",\n            \"amount\": 189.58\n          }\n        ],\n        \"opening_adjustment_net\": 189.58,\n        \"point_as_of_date\": \"2024-04-22\",\n        \"provider_captured_at\": \"2026-08-27T10:15:00+00:00\",\n        \"provider_source\": \"xero_journals\",\n        \"warehouses\": [\n          {\n            \"warehouse_id\": 29,\n            \"warehouse_name\": \"FBA US (AMZ)\",\n            \"is_fba\": true,\n            \"sku_value\": 4024.84,\n            \"sku_quantity\": 65\n          }\n        ]\n      },\n      {\n        \"nominal_code_id\": 87,\n        \"code\": \"142\",\n        \"name\": \"SMO Inventory\",\n        \"provider_account_name\": \"SMO Inventory\",\n        \"sku_value\": 569792.24,\n        \"sku_quantity\": 41250,\n        \"ledger_balance\": 444792.24,\n        \"provider_balance\": 0,\n        \"divergence\": -569792.24,\n        \"pending_ledger\": 0,\n        \"excluded_ledger\": -125000,\n        \"opening_adjustments\": [],\n        \"opening_adjustment_net\": 0,\n        \"point_as_of_date\": \"2024-04-22\",\n        \"provider_captured_at\": \"2026-08-27T10:15:00+00:00\",\n        \"provider_source\": \"xero_journals\",\n        \"warehouses\": [\n          {\n            \"warehouse_id\": 4,\n            \"warehouse_name\": \"ShipMyOrders Warehouse\",\n            \"is_fba\": false,\n            \"sku_value\": 569792.24,\n            \"sku_quantity\": 41250\n          },\n          {\n            \"warehouse_id\": 12,\n            \"warehouse_name\": \"Overflow Warehouse\",\n            \"is_fba\": false,\n            \"sku_value\": 0,\n            \"sku_quantity\": 0\n          }\n        ]\n      }\n    ]\n  }\n}"}]},{"name":"Get Inventory Reconciliation Baseline","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/baseline?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","baseline"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]},"description":"The reconcile-opening context for an inventory reconciliation connection: the current recon baseline (a \"line in the sand\" date the divergence view is measured from — null when none is set), the inventory start date the series otherwise opens from, and the accounting provider's lock date.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Response fields:** `baseline_date` (null when no baseline is set), `baseline_note`, `inventory_start_date` (where the series opens from without a baseline), `provider_lock_date` (null when the provider reports no lock), `opening_locked` (true when the correction date falls on/before the provider lock — corrections then must be made on the SKU side, not posted to the provider)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/baseline?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","baseline"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"baseline_date\": \"2025-01-01\",\n    \"baseline_note\": \"Pre-2025 books unreliable — fresh start\",\n    \"inventory_start_date\": \"2024-04-22\",\n    \"provider_lock_date\": \"2013-12-31\",\n    \"opening_locked\": false\n  }\n}"}]},{"name":"Set Inventory Reconciliation Baseline","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/baseline","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","baseline"]},"description":"Draw the reconciliation baseline: measure divergence from `baseline_date` forward and freeze the history before it out of the view. View-only and reversible — no reconciled figure, opening balance, or start date is changed. Returns the refreshed context.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Body:** `connection_id` (required), `baseline_date` (required, date), `note` (optional, max 500 chars).\n\n**Response fields:** `baseline_date` (null when no baseline is set), `baseline_note`, `inventory_start_date` (where the series opens from without a baseline), `provider_lock_date` (null when the provider reports no lock), `opening_locked` (true when the correction date falls on/before the provider lock — corrections then must be made on the SKU side, not posted to the provider).","body":{"mode":"raw","raw":"{\n  \"connection_id\": 5,\n  \"baseline_date\": \"2025-01-01\",\n  \"note\": \"Pre-2025 books unreliable — fresh start\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/baseline","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","baseline"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"baseline_date\": \"2025-01-01\",\n    \"baseline_note\": \"Pre-2025 books unreliable — fresh start\",\n    \"inventory_start_date\": \"2024-04-22\",\n    \"provider_lock_date\": \"2013-12-31\",\n    \"opening_locked\": false\n  }\n}"}]},{"name":"Clear Inventory Reconciliation Baseline","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/baseline?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","baseline"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]},"description":"Remove the reconciliation baseline; the full history returns to the divergence view. Returns the refreshed context.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Response fields:** `baseline_date` (null when no baseline is set), `baseline_note`, `inventory_start_date` (where the series opens from without a baseline), `provider_lock_date` (null when the provider reports no lock), `opening_locked` (true when the correction date falls on/before the provider lock — corrections then must be made on the SKU side, not posted to the provider)."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/baseline?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","baseline"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"baseline_date\": null,\n    \"baseline_note\": null,\n    \"inventory_start_date\": \"2024-04-22\",\n    \"provider_lock_date\": \"2013-12-31\",\n    \"opening_locked\": false\n  }\n}"}]},{"name":"Get Inventory Reconciliation Adjustment Preview","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/adjustment/preview?connection_id=5&nominal_code_id=87&effective_date=2024-04-22","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","adjustment","preview"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"nominal_code_id","value":"87","description":"The earmarked inventory control account. Required.","disabled":true},{"key":"effective_date","value":"2024-04-22","description":"The date to align at. Required.","disabled":true}]},"description":"The proposed adjusting journal that would move the provider's inventory control account to match SKU as of a date — computed read-only so you see exactly what would post. `divergence = provider − SKU`; a positive gap credits the inventory control account (reducing it) and debits the counter, both for the same amount. When the date is inside a locked provider period `postable` is false: post the journal dated on or after `earliest_postable_date` instead. Accounts backed by Amazon warehouses (FBA/AWD) are Amazon-sourced — their stock comes from Amazon and cannot be adjusted in SKU (`sku_side_adjustable` is false), so they can only be reconciled provider-side; other accounts may instead correct SKU's opening for the warehouses in `warehouses`.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Response fields:** `divergence`, `amount`, `needs_adjustment`, `direction_summary`, `counter_nominal_code_id`/`counter_code`/`counter_name`, `postable`, `blocked_reason`, `provider_lock_date`, `sku_side_adjustable` (whether the account has editable non-Amazon warehouses), `earliest_postable_date` (first date the provider accepts a post — lock date + 1 day, null when unlocked), `lines[]` (`nominal_code_id`, `code`, `name`, `direction`, `amount`), `warehouses[]` (`id`, `name` — the SKU-adjustable, non-Amazon warehouses the SKU-side fix can target). `already_adjusted` is true when this account was already reconciled on this date (posting is blocked to avoid double-correcting), with `existing_adjustment_id` pointing at the prior adjustment.\n\nSet net_pending=1 for the ongoing (residual) true-up: it targets divergence + pending so the journal will not over-correct once awaiting-sync postings land."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/adjustment/preview?connection_id=5&nominal_code_id=87&effective_date=2024-04-22","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","adjustment","preview"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"nominal_code_id","value":"87","description":"The earmarked inventory control account. Required.","disabled":true},{"key":"effective_date","value":"2024-04-22","description":"The date to align at. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"nominal_code_id\": 87,\n    \"code\": \"141\",\n    \"name\": \"Amazon FBA US Inventory\",\n    \"provider_account_name\": \"Amazon FBA US Inventory\",\n    \"effective_date\": \"2024-04-22\",\n    \"divergence\": 104005.26,\n    \"amount\": 104005.26,\n    \"needs_adjustment\": true,\n    \"direction_summary\": \"Credit Amazon FBA US Inventory and debit Opening Balance Equity — reduces the provider GL to match SKU.\",\n    \"counter_nominal_code_id\": 94,\n    \"counter_code\": \"970\",\n    \"counter_name\": \"Opening Balance Equity\",\n    \"postable\": true,\n    \"blocked_reason\": null,\n    \"provider_lock_date\": \"2013-12-31\",\n    \"sku_side_adjustable\": false,\n    \"earliest_postable_date\": \"2014-01-01\",\n    \"already_adjusted\": false,\n    \"existing_adjustment_id\": null,\n    \"lines\": [\n      {\n        \"nominal_code_id\": 87,\n        \"code\": \"141\",\n        \"name\": \"Amazon FBA US Inventory\",\n        \"direction\": \"credit\",\n        \"amount\": 104005.26\n      },\n      {\n        \"nominal_code_id\": 94,\n        \"code\": \"970\",\n        \"name\": \"Opening Balance Equity\",\n        \"direction\": \"debit\",\n        \"amount\": 104005.26\n      }\n    ],\n    \"warehouses\": []\n  }\n}"}]},{"name":"Get Inventory Reconciliation Adjustment Journal","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/adjustment/journal?connection_id=5&adjustment_id=1","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","adjustment","journal"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"adjustment_id","value":"1","description":"The posted adjustment id (returned by the post endpoint). Required.","disabled":true}]},"description":"Where a posted inventory reconciliation adjustment landed in SKU's ledger — so a client can deep-link the user to the journal entry the adjustment created. The adjustment posts asynchronously, so immediately after posting this returns `pending: true` with null ids; poll it with the returned `adjustment_id` until `pending` is false, then use `journal_entry_id` to open the journal.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Response fields:** `pending` (true until the journal exists), `journal_entry_id` (the created journal entry's id, null while pending), `reference` (the journal's reference, e.g. `INV-RECON-ADJ-1`), `status` (the journal's lifecycle state, e.g. `draft` or `posted`), `deletable` (true while the journal has not yet synced to the provider — so the adjustment can be reversed and discarded cleanly)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/adjustment/journal?connection_id=5&adjustment_id=1","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","adjustment","journal"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"adjustment_id","value":"1","description":"The posted adjustment id (returned by the post endpoint). Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"pending\": false,\n    \"journal_entry_id\": 49687,\n    \"reference\": \"INV-RECON-ADJ-1\",\n    \"status\": \"draft\",\n    \"deletable\": true\n  }\n}"}]},{"name":"Ensure Adjustment Counter Account","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/adjustment/counter-account","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","adjustment","counter-account"]},"description":"Ensure a conventional \"Opening Balance Equity\" counter account exists (creating it if the chart of accounts has none), so the adjusting journal's balancing leg has a target. Idempotent — reuses an existing opening-balance-named account.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Body:** `connection_id` (required).\n\n**Response:** `{ data: { id, code, name } }`.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 5\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/adjustment/counter-account","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","adjustment","counter-account"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 94,\n    \"code\": \"970\",\n    \"name\": \"Opening Balance Equity\"\n  }\n}"}]},{"name":"Post Inventory Reconciliation Adjustment","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/adjustment","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","adjustment"]},"description":"Post the adjusting journal to bring the provider's inventory control account into line with SKU. The amount and direction are recomputed server-side from the divergence (the client can't post an arbitrary figure); the balanced journal is posted to the provider as a manual journal. An already-aligned or locked-period account is rejected 422.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Body:** `connection_id` (required), `nominal_code_id` (required), `counter_nominal_code_id` (required), `effective_date` (required, date), `note` (optional, max 500 chars).\n\n**Response:** `{ data: { adjustment_id }, message }`.\n\nPass net_pending=true for the ongoing true-up (targets the residual = divergence + pending). The amount and direction are always recomputed server-side.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 5,\n  \"nominal_code_id\": 87,\n  \"counter_nominal_code_id\": 94,\n  \"effective_date\": \"2024-04-22\",\n  \"note\": \"Align opening to SKU\",\n  \"net_pending\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/adjustment","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","adjustment"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"adjustment_id\": 12\n  },\n  \"message\": \"Adjusting journal posted — it will sync to the provider.\"\n}"}]},{"name":"Reverse Inventory Reconciliation Adjustment","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/adjustment?connection_id=5&adjustment_id=1","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","adjustment"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"adjustment_id","value":"1","description":"The posted adjustment id to reverse. Required.","disabled":true}]},"description":"Reverse (delete) a posted inventory reconciliation adjustment while its journal has not yet synced to the accounting provider, discarding the draft journal so the opening can be re-reconciled (for example, at a corrected date). Once the journal has synced to the provider this returns 422 — undoing it then requires posting a reversing entry in the provider.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Query parameters:** `connection_id` (required), `adjustment_id` (required — the adjustment returned by the post endpoint).\n\n**Behavior:** discards the adjustment's unsynced draft journal via the standard source-deleted pathway; the SKU inventory value is unchanged."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/adjustment?connection_id=5&adjustment_id=1","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","adjustment"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"adjustment_id","value":"1","description":"The posted adjustment id to reverse. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Adjustment reversed — its journal was discarded.\"\n}"}]},{"name":"Provider GL Sync Estimate","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/sync-estimate?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","sync-estimate"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id (the provider's integration instance). Required.","disabled":true}]},"description":"A **pre-flight, zero-API** estimate for the unified provider GL sync (Lane 2): how many provider calls the operation would cost for the chosen window — against today's remaining API budget for Xero, and with no daily cap for QuickBooks Online, so the user can weigh it before spending any of it. Computed locally from the cached journal cadence (distinct journals over their date span) plus the one Trial Balance anchor call — it makes **no** calls to the provider.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Query parameters:**\n- `connection_id` (integer, **required**): accounting connection id whose journals/budget are estimated.\n- `from` (date, optional): window start (Y-m-d). Clamped up to the Inventory Start Date — an earlier value never lowers the window below the opening-balance anchor. Defaults to the Inventory Start Date.\n- `to` (date, optional, `after_or_equal:from`): window end (Y-m-d). Defaults to today.\n\n**Response (`data`):**\n- `can_sync` (boolean): false when the Inventory Start Date is unset (no lower bound to anchor against); `reason` explains why.\n- `reason` (string|null): why the sync can't run, when `can_sync` is false.\n- `cadence_known` (boolean): false on a first-ever sync (nothing cached to extrapolate from) — then `estimated_calls` is null.\n- `estimated_calls` (integer|null): total projected Xero calls (pages + probes + anchor). Null when cadence is unknown.\n- `estimated_pages` (integer|null): projected forward Journals pages (incl. the terminating empty page).\n- `estimated_probe_calls` (integer): binary-search probe calls to locate a start offset below the cache (0 when resuming above the cache or from empty).\n- `anchor_calls` (integer): 0 or 1 — the metered Trial Balance call, charged only when no anchor has been captured for the opening day yet.\n- `journals_per_day` (number|null): the cached cadence the page count is extrapolated from.\n- `daily_remaining` (integer|null): Xero calls left in today's budget (from the connection's tracked usage; null if not tracked yet).\n- `daily_limit` (integer): Xero's per-organisation daily API cap (5000).\n- `daily_reset_at` (string|null): ISO8601 timestamp when the daily budget resets.\n- `over_budget` (boolean): true when `estimated_calls` exceeds `daily_remaining`.\n- `inventory_start_date` (string|null): Y-m-d lower bound of the window.\n- `window_from` (string|null): Y-m-d clamped window start actually estimated.\n- `window_to` (string): Y-m-d window end actually estimated.\n- `cached_from` / `cached_to` (string|null): Y-m-d span of journals already cached.\n\n**QuickBooks Online connections:** the pull is QBO's General Ledger report, one call per calendar month of the window (plus the opening Trial Balance anchor when missing), so the cost is exact (`cadence_known` is always true). QBO has no daily cap (it throttles at 500 requests/minute per company), so `daily_limit`, `daily_remaining` and `daily_reset_at` are null and `over_budget`/`paused` are always false. When `from` is omitted the window starts at `incremental_from` — the day after the QuickBooks book close date (never before the Inventory Start Date).\n\n**Additional response fields:**\n- `provider` (string): `xero` or `qbo`.\n- `incremental_from` (date|null): where a \"since last sync\" pull starts for QBO; null for Xero, which resumes from the cached journal number."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/sync-estimate?connection_id=5","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","sync-estimate"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id (the provider's integration instance). Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"can_sync\": true,\n    \"reason\": null,\n    \"cadence_known\": true,\n    \"estimated_calls\": 19,\n    \"estimated_pages\": 18,\n    \"estimated_probe_calls\": 0,\n    \"anchor_calls\": 1,\n    \"journals_per_day\": 9.42,\n    \"daily_remaining\": 4870,\n    \"daily_limit\": 5000,\n    \"daily_reset_at\": \"2026-06-28T00:00:00+00:00\",\n    \"over_budget\": false,\n    \"inventory_start_date\": \"2026-01-01\",\n    \"window_from\": \"2026-01-01\",\n    \"window_to\": \"2026-06-27\",\n    \"cached_from\": \"2026-03-15\",\n    \"cached_to\": \"2026-06-20\",\n    \"provider\": \"xero\",\n    \"incremental_from\": null\n  }\n}"}]},{"name":"Rebuild Inventory Daily Series","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/rebuild","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","rebuild"]},"description":"Kick the background tracked job that materializes the **SKU side** of the daily series (point-in-time inventory value per account per day). Free — no provider calls. Returns the tracked job log id so the UI can follow it in the global progress tray.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.sync` permission.\n\n**Request body:**\n- `connection_id` (integer, **required**): accounting connection id the points are keyed to.\n- `nominal_code_ids` (array, optional): subset of earmarked account ids. Defaults to all earmarked.\n- `from` (date, optional): local-day window start. Defaults to the inventory start date.\n- `to` (date, optional, after_or_equal:from): local-day window end. Defaults to now.\n\nWindow defaults span inventory_start_date → today (~hundreds of days), so this is a long-running job.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 5,\n  \"nominal_code_ids\": [\n    87\n  ],\n  \"from\": \"2026-01-01\",\n  \"to\": \"2026-06-27\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/rebuild","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","rebuild"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 4821\n  },\n  \"message\": \"Inventory reconciliation rebuild started\"\n}"}]},{"name":"Capture Provider Inventory Balances","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/capture-provider","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","capture-provider"]},"description":"Capture the provider's GL balance for the earmarked inventory accounts as-of a day onto the daily series — the metered counterpart to the (free) SKU-side rebuild. **Makes one live Xero Trial Balance call** (needs `accounting.reports.read`), so callers drive the cadence; the provider series is intentionally sparse (only captured days get a row). Days/accounts with no matching provider account are skipped.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.sync` permission.\n\n**Request body:**\n- `connection_id` (integer, **required**): accounting connection id.\n- `to` (date, optional): as-of day for the Trial Balance. Defaults to now.\n- `force` (boolean, optional): re-capture even when a Trial Balance snapshot for the day is already cached — use to refresh a historical balance that back-dated provider journals may have changed. Without it a cached day is served from the snapshot and costs zero provider calls.\n\nReturns `{ captured: <number of accounts written> }`.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 5,\n  \"to\": \"2026-06-27\",\n  \"force\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/capture-provider","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","capture-provider"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"captured\": 1\n  },\n  \"success\": true\n}"}]},{"name":"Sync Provider GL Journals","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/sync-journals","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","sync-journals"]},"description":"Kick the background tracked job that pulls the provider's **GL journals feed** (Xero Journals) into the local cache — the metered half of the daily reconciliation. One paginated pull per run; resumes incrementally from the highest JournalNumber already cached. Once cached, **Rebuild Inventory Daily Series** reconstructs the daily provider balance from the cache with no further provider call. Returns the tracked job log id so the UI can follow it in the global progress tray.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token + `accounting.sync` permission.\n\n**Request body:**\n- `connection_id` (integer, **required**): accounting connection id (the provider's integration instance) whose journals are pulled.\n- `from` (date `Y-m-d`, optional): lower-bounds the pull by journal date. Clamped up to the Inventory Start Date (the opening anchor replaces summing the ledger before it), so an earlier value is accepted but never lowers the window below the anchor. When omitted, the window starts at the Inventory Start Date.\n- `full` (boolean, optional, default `false`): when `true`, re-pulls the entire ledger from JournalNumber 0 instead of resuming incrementally from the highest cached journal.\n\n**Side effect:** makes metered API calls to the accounting provider (one page per ~100 journals), capped at the connection's remaining daily Xero API budget — a budget-out pauses the run cleanly and it auto-resumes once the budget refreshes.\n\n**QuickBooks Online connections:** the pull reads QBO's **General Ledger report** for the earmarked inventory accounts (matched by QBO Account Id), one report call per calendar month, and *replaces* each month in the cache — a transaction deleted or voided in QuickBooks drops out. There is no daily budget, so a QBO run never pauses. With no `from` and `full=false` the run re-reads everything after the QuickBooks book close date; `full=true` re-reads from the Inventory Start Date.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 5,\n  \"from\": \"2026-01-01\",\n  \"full\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/sync-journals","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","sync-journals"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 4822\n  },\n  \"message\": \"Provider GL journals sync started\"\n}"}]},{"name":"List Statement Imports","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements"]},"description":"Paginated list of prior bank-statement screenshot extractions (most recent first), each with its parsed line count and spent/received totals.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 6,\n      \"status\": \"ready\",\n      \"source\": \"upload\",\n      \"original_filename\": \"statement.png\",\n      \"line_count\": 2,\n      \"spent_total\": \"621.1100\",\n      \"received_total\": \"144.2200\",\n      \"error_message\": null,\n      \"created_at\": \"2026-06-28T10:00:00+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"per_page\": 15,\n  \"last_page\": 1,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Upload Statement Screenshot","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements"]},"description":"Upload a screenshot of bank statement lines and **queue** its OCR extraction (Azure Document Intelligence prebuilt-layout). Returns immediately with the import in the `extracting` state plus a top-level `tracked_job_log_id`; the parsed lines arrive once the background a background job completes (watch it via the tracked job log endpoints, then re-fetch the import via GET `.../bank-statements/{id}`). Deferring the OCR keeps an unreachable provider from hanging the upload request.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nXero exposes no API for raw statement lines, hence the screenshot path. Handles both Xero layouts: the cash-coding grid (one line per row) and the two-column reconcile card view (date / description / type stacked in one column).\n\n**Content-Type:** multipart/form-data.\n**Authentication:** Requires Bearer token.\n\nIf the OCR provider cannot read the image, the job flips the import to `failed` with an `error_message` (surfaced via the tracked job log endpoints and on re-fetch) — the upload request itself still succeeds.","body":{"mode":"formdata","formdata":[{"key":"image","type":"file","src":[]},{"key":"source","type":"text","value":""}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 6,\n    \"status\": \"extracting\",\n    \"source\": \"upload\",\n    \"original_filename\": \"statement.png\",\n    \"line_count\": 0,\n    \"spent_total\": null,\n    \"received_total\": null,\n    \"error_message\": null,\n    \"created_at\": \"2026-06-28T10:00:00+00:00\",\n    \"lines\": []\n  },\n  \"tracked_job_log_id\": 42\n}"}]},{"name":"Get Statement Import","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/{bankStatementImport}","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements",":bankStatementImport"],"variable":[{"key":"bankStatementImport","value":"6","description":"Import id."}]},"description":"A single statement-screenshot extraction with all its parsed lines.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/{bankStatementImport}","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements",":bankStatementImport"],"variable":[{"key":"bankStatementImport","value":"6","description":"Import id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 6,\n    \"status\": \"ready\",\n    \"source\": \"upload\",\n    \"original_filename\": \"statement.png\",\n    \"line_count\": 2,\n    \"spent_total\": \"621.1100\",\n    \"received_total\": \"144.2200\",\n    \"error_message\": null,\n    \"created_at\": \"2026-06-28T10:00:00+00:00\",\n    \"lines\": [\n      {\n        \"id\": 11,\n        \"bank_statement_import_id\": 6,\n        \"statement_date\": \"2025-12-15\",\n        \"payee\": null,\n        \"description\": \"ORIG CO NAME:Amazon Mexico Se\",\n        \"reference\": \"POSCREDIT\",\n        \"spent\": null,\n        \"received\": \"144.2200\",\n        \"direction\": \"received\",\n        \"match_status\": \"unmatched\",\n        \"sort_order\": 0\n      },\n      {\n        \"id\": 12,\n        \"bank_statement_import_id\": 6,\n        \"statement_date\": \"2026-01-08\",\n        \"payee\": null,\n        \"description\": \"Online ACH Payment To Stainout\",\n        \"reference\": \"BILLPAYMENT\",\n        \"spent\": \"621.1100\",\n        \"received\": null,\n        \"direction\": \"spent\",\n        \"match_status\": \"unmatched\",\n        \"sort_order\": 1\n      }\n    ]\n  }\n}"}]},{"name":"Delete Statement Import","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/{bankStatementImport}","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements",":bankStatementImport"],"variable":[{"key":"bankStatementImport","value":"6","description":"Import id."}]},"description":"Delete a statement extraction and its parsed lines.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token."},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/{bankStatementImport}","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements",":bankStatementImport"],"variable":[{"key":"bankStatementImport","value":"6","description":"Import id."}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":""}]},{"name":"List Line Match Suggestions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/{bankStatementImport}/suggestions","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements",":bankStatementImport","suggestions"],"variable":[{"key":"bankStatementImport","value":"24","description":"Import id."}]},"description":"For each parsed line in an import, candidate SKU ledger entries it could reconcile against. Money-in lines (Received) match AR documents (Sales Order Invoice, Vendor Credit); money-out lines (Spent) match AP documents (Bill, Purchase Order Invoice, Sales Credit). Candidates are entries of the matching type whose total is within $0.01 of the line amount and whose effective date is within 60 days of the statement date, ranked by date proximity then amount closeness. `synced` flags entries already pushed to the provider.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Candidate kinds:** `match_kind=entry` is a single SKU document; `match_kind=settlement` is an Amazon settlement aggregate — matched on the settlement's recorded payout (amazon_financial_event_groups.ConvertedTotalCurrencyAmount), carrying every ledger entry it generated in `entry_ids` (sales invoices + fee bills + carved-out landed-cost bills linked via amazon_report_settlement_data). Confirming a settlement candidate POSTs all of `entry_ids`.\n\nSettlement candidates carry `settlement_id` (the AmazonFinancialEventGroup id — links to /integrations/amazon/finance/bank-transaction/{id}), `settlement_reference` (the FinancialEventGroupId shown as the settlement #), and `entries[]` (each linked ledger entry: id, reference, entry_type_label, is_landed_cost) so the UI can deep-link every document.\n\n**Per-suggestion `dismissed_count`** (integer): how many candidates the user has declared “not a match” on this line (via Dismiss Line Match Suggestion). Dismissed candidates are excluded from `candidates` and restorable as a set via Restore Dismissed Suggestions.\n\n**Group candidates** (`match_kind=group`): several of one supplier's documents that together settle a money-out payment. When the line's memo names the supplier, combinations may also NET vendor credits against the invoices (payment = invoices − credits) — the label reads e.g. '2 invoices − 1 credit' and `amount` is the net. Each `entries[]` element carries its own `entry_type`, `amount`, `counterparty_name` and `effective_at` so the UI renders a signed calculation."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/{bankStatementImport}/suggestions","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements",":bankStatementImport","suggestions"],"variable":[{"key":"bankStatementImport","value":"24","description":"Import id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"line\": {\n        \"id\": 451,\n        \"bank_statement_import_id\": 23,\n        \"statement_date\": \"2026-04-28\",\n        \"payee\": null,\n        \"description\": \"ORIG CO NAME:Amazon Mexico\",\n        \"reference\": \"POSCREDIT\",\n        \"spent\": null,\n        \"received\": \"432.2700\",\n        \"direction\": \"received\",\n        \"match_status\": \"unmatched\",\n        \"sort_order\": 0,\n        \"matches\": []\n      },\n      \"candidates\": [\n        {\n          \"match_kind\": \"entry\",\n          \"amount\": \"432.2700\",\n          \"effective_at\": \"2026-04-26\",\n          \"source_label\": \"Bill\",\n          \"synced\": false,\n          \"ledger_journal_entry_id\": 612,\n          \"entry_type\": \"bill\",\n          \"entry_type_label\": \"Bill\",\n          \"counterparty_name\": \"Acme Supplies Inc\",\n          \"reference\": \"…\",\n          \"currency_code\": \"USD\",\n          \"source_type\": \"App\\\\Models\\\\Bill\",\n          \"source_id\": 91\n        },\n        {\n          \"match_kind\": \"settlement\",\n          \"amount\": \"432.2700\",\n          \"effective_at\": \"2026-04-28\",\n          \"source_label\": \"Amazon Settlement\",\n          \"synced\": false,\n          \"settlement_id\": 350,\n          \"settlement_reference\": \"5cKTkaXNDp…\",\n          \"entry_ids\": [\n            700,\n            701,\n            702\n          ],\n          \"entry_count\": 3,\n          \"components\": [\n            {\n              \"label\": \"Sales Order Invoice\",\n              \"count\": 1,\n              \"amount\": \"1274.9395\"\n            },\n            {\n              \"label\": \"Bill\",\n              \"count\": 1,\n              \"amount\": \"1305.8630\"\n            },\n            {\n              \"label\": \"Landed-cost Bill\",\n              \"count\": 1,\n              \"amount\": \"129.8400\"\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"Confirm Line Match","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/lines/{bankStatementLine}/match","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements","lines",":bankStatementLine","match"],"variable":[{"key":"bankStatementLine","value":"479","description":"Statement line id."}]},"description":"Confirm one or more ledger entries as the match for a line. Persists a denormalized snapshot of each match (so it survives ledger rebuilds) and marks the line `matched`. Idempotent — re-matching the same entry is a no-op.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Body:** `entry_ids` (required array of ledger_journal_entry ids).\n**Authentication:** Requires Bearer token.\n\n**`sync_block`** (on each line): pre-push sync verdict for the line's matched document(s) — `null` when every document will sync, otherwise `{ blocked: true, enableable: bool, reasons: string[], blocked_entry_ids: int[], enableable_entry_ids: int[] }`. Surfaced so the UI can warn (and offer Enable sync) BEFORE a push that would otherwise silently not reach Xero.\n\n**Optional Amazon-settlement context** (omit for a single-document match): `match_kind` ('entry' | 'settlement'), `settlement_id` (required when match_kind=settlement), `settlement_reference`, `settlement_payout` (the net payout the bank line matched on). When present, every persisted match row records the settlement so the UI labels it an 'Amazon Settlement · {payout}' regardless of how many SKU documents it touched.","body":{"mode":"raw","raw":"{\n  \"entry_ids\": [\n    10\n  ],\n  \"match_kind\": \"settlement\",\n  \"settlement_id\": 337,\n  \"settlement_reference\": \"5cKTkaXNDpXqmNv0MzaOHAMppp6CsZOYXrgMbDv4UDg\",\n  \"settlement_payout\": \"432.27\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/lines/{bankStatementLine}/match","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements","lines",":bankStatementLine","match"],"variable":[{"key":"bankStatementLine","value":"479","description":"Statement line id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 479,\n    \"bank_statement_import_id\": 24,\n    \"statement_date\": \"2024-04-26\",\n    \"payee\": null,\n    \"description\": \"ACH Payment to supplier A\",\n    \"reference\": \"BILLPAYMENT\",\n    \"spent\": \"73.9000\",\n    \"received\": null,\n    \"direction\": \"spent\",\n    \"match_status\": \"matched\",\n    \"sort_order\": 0,\n    \"sync_block\": null,\n    \"matches\": [\n      {\n        \"id\": 1,\n        \"ledger_journal_entry_id\": 10,\n        \"entry_type\": \"bill\",\n        \"entry_type_label\": \"Bill\",\n        \"match_kind\": \"settlement\",\n        \"settlement_id\": 337,\n        \"settlement_reference\": \"5cKTkaXNDpXqmNv0MzaOHAMppp6CsZOYXrgMbDv4UDg\",\n        \"settlement_payout\": \"432.2700\",\n        \"reference\": \"xjwbaRFTPT8...\",\n        \"counterparty_name\": \"Supplier A\",\n        \"amount\": \"73.9000\",\n        \"effective_at\": \"2024-04-24\",\n        \"source_type\": \"App\\\\Models\\\\Bill\",\n        \"source_id\": 42,\n        \"source_label\": \"Bill\"\n      }\n    ]\n  }\n}"}]},{"name":"Clear Line Match","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/lines/{bankStatementLine}/match","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements","lines",":bankStatementLine","match"],"variable":[{"key":"bankStatementLine","value":"479","description":"Statement line id."}]},"description":"Remove all confirmed matches for a line and mark it `unmatched`.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/lines/{bankStatementLine}/match","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements","lines",":bankStatementLine","match"],"variable":[{"key":"bankStatementLine","value":"479","description":"Statement line id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 479,\n    \"bank_statement_import_id\": 24,\n    \"statement_date\": \"2024-04-26\",\n    \"payee\": null,\n    \"description\": \"ACH Payment to supplier A\",\n    \"reference\": \"BILLPAYMENT\",\n    \"spent\": \"73.9000\",\n    \"received\": null,\n    \"direction\": \"spent\",\n    \"match_status\": \"unmatched\",\n    \"sort_order\": 0,\n    \"matches\": []\n  }\n}"}]},{"name":"Dismiss Line Match Suggestion","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/lines/{bankStatementLine}/dismiss-match","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements","lines",":bankStatementLine","dismiss-match"],"variable":[{"key":"bankStatementLine","value":"93","description":"Statement line id."}]},"description":"Declare a suggested candidate \"not a match\" for this line. The candidate's identity is pinned to the line (order-independent fingerprint), so the same document/combination is never re-offered by Line Match Suggestions or confirmed by Auto-match — and a dismissed group combination's per-supplier slot frees up for the next-best combination on the next refresh. Undone by the Restore Dismissed Suggestions endpoint.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Body:**\n- `match_kind` (required, 'entry' | 'settlement' | 'group') — the candidate's kind exactly as the suggestions endpoint returned it.\n- `entry_ids` (required unless match_kind=settlement, array of integers) — the candidate's ledger entry id(s); order does not matter.\n- `settlement_id` (required when match_kind=settlement, integer) — the Amazon settlement id.\n\n**Authentication:** Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"match_kind\": \"group\",\n  \"entry_ids\": [\n    338,\n    5348,\n    5623\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/lines/{bankStatementLine}/dismiss-match","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements","lines",":bankStatementLine","dismiss-match"],"variable":[{"key":"bankStatementLine","value":"93","description":"Statement line id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"message\": \"Dismissed — that combination will not be suggested again.\",\n  \"data\": []\n}"}]},{"name":"Restore Dismissed Suggestions","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/lines/{bankStatementLine}/dismissals","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements","lines",":bankStatementLine","dismissals"],"variable":[{"key":"bankStatementLine","value":"93","description":"Statement line id."}]},"description":"Clear every \"not a match\" verdict on the line, putting all dismissed candidates back into circulation — they reappear on the next Line Match Suggestions call. Returns how many dismissals were removed.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/lines/{bankStatementLine}/dismissals","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements","lines",":bankStatementLine","dismissals"],"variable":[{"key":"bankStatementLine","value":"93","description":"Statement line id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"message\": \"Dismissed suggestions restored.\",\n  \"data\": {\n    \"restored\": 2\n  }\n}"}]},{"name":"Auto-match Single-candidate Lines","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/{bankStatementImport}/auto-match","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements",":bankStatementImport","auto-match"],"variable":[{"key":"bankStatementImport","value":"23","description":"Import id."}]},"description":"Confirm every still-unmatched line in the import that has EXACTLY ONE candidate (an unambiguous match). Lines with zero or multiple candidates are left for the user. Optional body `line_ids` scopes it to a selection (bulk-match the ticked rows); omit to match all.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nReturns the number matched and the refreshed import (lines + their matches).\n\n**Authentication:** Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"line_ids\": [\n    479,\n    480\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/{bankStatementImport}/auto-match","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements",":bankStatementImport","auto-match"],"variable":[{"key":"bankStatementImport","value":"23","description":"Import id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"matched\": 31,\n    \"import\": {\n      \"id\": 23,\n      \"status\": \"ready\",\n      \"line_count\": 38,\n      \"lines\": [\n        \"…\"\n      ]\n    }\n  }\n}"}]},{"name":"Unmatch All Lines","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/{bankStatementImport}/unmatch-all","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements",":bankStatementImport","unmatch-all"],"variable":[{"key":"bankStatementImport","value":"23","description":"Import id."}]},"description":"Clear the confirmed matches across the import (bulk unmatch). Only lines whose `match_status` is `matched` are affected; the linked SKU documents are untouched — only the bank-line links are removed. Optional body `line_ids` scopes it to a selection (unmatch the ticked rows); omit to unmatch every matched line.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nReturns the number unmatched and the refreshed import (lines + their matches).\n\n**Authentication:** Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"line_ids\": [\n    479,\n    480\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/{bankStatementImport}/unmatch-all","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements",":bankStatementImport","unmatch-all"],"variable":[{"key":"bankStatementImport","value":"23","description":"Import id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"unmatched\": 31,\n    \"import\": {\n      \"id\": 23,\n      \"status\": \"ready\",\n      \"line_count\": 38,\n      \"lines\": [\n        \"…\"\n      ]\n    }\n  }\n}"}]},{"name":"Push Matched Lines to Xero","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/{bankStatementImport}/push-to-xero","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements",":bankStatementImport","push-to-xero"],"variable":[{"key":"bankStatementImport","value":"23","description":"Import id."}]},"description":"Push the matched line(s)' SKU document(s) to the accounting provider (Xero) via the shared ledger sync pipeline (the same dedup-aware path the rest of the app posts through). Only lines whose `match_status` is `matched` are pushed; each matched entry that is already linked-current in Xero is skipped, others are enqueued as a Create/Update sync operation. Optional body `line_ids` scopes it to a selection (a single line, or a bulk pick); omit to push every matched line.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nA line flips to `synced` the moment all of its documents are confirmed in Xero; lines whose documents are still being pushed stay `matched` and self-heal to `synced` on the next refresh (suggestions / show) once the async push lands.\n\nReturns: `entries_enqueued` (entries run through the sync pipeline), `lines_synced` (lines now fully in Xero), `lines_pending` (lines still pushing), and the refreshed import (lines + their matches).\n\n**Authentication:** Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"line_ids\": [\n    479,\n    480\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/{bankStatementImport}/push-to-xero","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements",":bankStatementImport","push-to-xero"],"variable":[{"key":"bankStatementImport","value":"23","description":"Import id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"entries_enqueued\": 33,\n    \"lines_synced\": 28,\n    \"lines_pending\": 5,\n    \"import\": {\n      \"id\": 23,\n      \"status\": \"ready\",\n      \"line_count\": 38,\n      \"lines\": [\n        \"…\"\n      ]\n    }\n  }\n}"}]},{"name":"Enable Sync on Matched Lines","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/{bankStatementImport}/enable-sync","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements",":bankStatementImport","enable-sync"],"variable":[{"key":"bankStatementImport","value":"23","description":"Import id."}]},"description":"Force-enable sync on the matched line(s)' document(s) whose sync is currently **disabled-but-enableable** — a settings opt-out (e.g. this entry type isn't synced, or it's before the sync start date) or a per-entry `force_disabled` override. Writes a `force_enabled` override on each such entry (via the same path as the per-entry override endpoint) so a subsequent **Push to Xero** actually reaches the provider instead of silently no-opping.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nStructurally-blocked documents — a batch member, an open batch, an inactive connection, or internal-only entries — are **left untouched** (they can't be force-enabled). Optional body `line_ids` scopes it to a single line's Enable or a bulk selection; omit to enable across every matched line.\n\nReturns: `entries_enabled` (documents flipped to force_enabled), `lines_affected` (matched lines touched), and the refreshed import (lines + their matches, with cleared `sync_block`s).\n\nGuarded by the `accounting.sync` permission.\n\n**Authentication:** Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"line_ids\": [\n    479,\n    480\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/bank-statements/{bankStatementImport}/enable-sync","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","bank-statements",":bankStatementImport","enable-sync"],"variable":[{"key":"bankStatementImport","value":"23","description":"Import id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"entries_enabled\": 4,\n    \"lines_affected\": 1,\n    \"import\": {\n      \"id\": 23,\n      \"status\": \"ready\",\n      \"line_count\": 38,\n      \"lines\": [\n        \"…\"\n      ]\n    }\n  }\n}"}]},{"name":"Resume Provider GL Journals Sync","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/resume-journals","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","resume-journals"]},"description":"Manually **resume a budget-paused** provider GL journals sync from its stored cursor, instead of waiting for the hourly auto-resume sweep (`ledger:reconciliation:resume-provider-sync`).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nWhen the metered backfill (see **Sync Provider GL Journals**) exhausts its reserved per-run provider-call budget, it pauses cleanly and records a resume cursor on the connection (`integration_settings.inventoryReconSync`: `resume_offset`, `from`, `user_id`, `paused_at`). This endpoint re-dispatches the tracked job from that cursor — same `connection_id`, `full=false`, picking up at the saved `from` / `resume_offset` — and returns the tracked job log id so the UI can follow it in the global progress tray.\n\n**Authentication:** Requires Bearer token + `accounting.sync` permission.\n\n**Request body:**\n- `connection_id` (integer, **required**): accounting connection id whose paused sync is resumed.\n\n**Responses:**\n- `200` — a paused run existed; the resume job was dispatched. Returns `data.tracked_job_log_id`.\n- `409` — no paused provider GL sync is recorded for this connection (nothing to resume).\n- `422` — `connection_id` missing or not an integer.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 5\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/resume-journals","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","resume-journals"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 4823\n  },\n  \"message\": \"Provider GL journals sync resumed\"\n}"}]},{"name":"Sync Inventory Reconciliation Period Entries","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/period-entries/sync","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","period-entries","sync"]},"description":"Push the pushable postings of one transaction type and period to the accounting provider.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nThe entry ids are resolved SERVER-SIDE from the same population the drill-down lists, using the same eligibility rule that stamps can_sync on each row — so a client never has to fetch hundreds of entries just to filter them, and can never request a push the server would decline.\n\nSend entry_ids to push an explicit selection; the selection is still intersected with what is genuinely pushable. Omit entry_ids to push the whole concept, narrowed by status when a filter is active — the count shown to a user and the set acted on must describe the same postings.\n\nThe push runs in the background: the response returns a tracked job id to follow, plus an honest eligible/excluded split. When nothing is eligible, eligible_count is 0 and tracked_job_log_id is null — no job is dispatched.\n\nRequires the accounting.sync permission. Authenticate with a Personal Access Token.\n\n**`type`** (optional, string, max 64) — narrows the push to ONE ledger type inside `concept`, matching the per-type rows the reconciliation table renders when a concept's split is complete (e.g. `concept=fba` + `type=fba_shipments`). Omit it to push every posting in the concept for the period. Without it a row displayed as FBA Shipments would push every FBA posting in the period.\n\n`status` accepts synced, stale, awaiting, excluded or untied. Use `stale` to re-push the postings the provider already holds that have changed in SKU since — this updates the existing provider journals in place rather than creating new ones.\n\nWhen nothing is eligible to push, the request does not stop there: it starts a provider GL refresh instead and returns that job's id. A posting can already be synced while the reconciliation still shows a Check B difference, because the provider column is served from the local GL cache and only a provider pull refreshes it. In that case the response carries `refreshed_provider: true` and `eligible_count: 0`, and the returned `tracked_job_log_id` tracks the refresh rather than a push.","body":{"mode":"raw","raw":"{\n  \"connection_id\": 5,\n  \"nominal_code_id\": 87,\n  \"month\": \"2025-01\",\n  \"concept\": \"fulfillment\",\n  \"type\": \"fba_shipments\",\n  \"status\": \"stale\",\n  \"entry_ids\": [\n    88231,\n    88240\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/period-entries/sync","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","period-entries","sync"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 90412,\n    \"eligible_count\": 11,\n    \"excluded_count\": 409\n  }\n}"}]},{"name":"Get Inventory Reconciliation Period Inventory","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/period-inventory?connection_id=5&nominal_code_id=87&month=2025-01&concept=fulfillment","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","period-inventory"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"nominal_code_id","value":"87","description":"The earmarked inventory control account id. Required.","disabled":true},{"key":"month","value":"2025-01","description":"The period to drill into. Accepts a month as YYYY-MM or a whole year as YYYY. Required.","disabled":true},{"key":"concept","value":"fulfillment","description":"The transaction-type concept whose inventory movements to list. Required.","disabled":true}]},"description":"The inventory side of one transaction-type concept for a period, amount-paired against that concept's ledger postings — the evidence behind Check A (SKU ledger minus SKU inventory).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nCheck A is deliberately NOT reported per journal entry. Inventory movements are keyed by product, warehouse and date and attributed to a concept, not to an entry: a monthly batch consolidates thousands of movements into a single posting, and a cost revaluation recognises against a movement dated in an earlier period. So instead of a fabricated per-entry figure, both sides are listed and paired by signed amount. Matched pairs net to zero, which makes the unmatched remainder the whole of Check A.\n\nmatch_group correlates a movement with the ledger posting of the same amount; null means unmatched. Unmatched rows are returned first — the matched pairs are definitionally not the finding.\n\nNot every stream has rows to list. Amazon FBA stock is valued in its own subledger and stock in transit has no warehouse, so both move sku_moved without recording an inventory movement. itemised_total is what the listed rows sum to and unitemised_total is that remainder, so an empty list under a large sku_moved explains itself.\n\ndisplaced_value is value whose stock had moved by the period end but whose posting an accounting lock date pushed later. The inventory side is already stated on the accounting calendar so both sides sit on the same dates — it is reported so a legitimately shifted posting is not read as a break.\n\nmeta.periods splits the SAME check_a across the accounting periods the window covers — one row per month, each with the inventory value that moved in it, what the ledger posted for it, and the difference. The rows are a partition of the window and sum back to meta.check_a exactly. This is the only evidence available for a stream valued in its own subledger: Amazon FBA records no inventory movement, so its data array is empty however large check_a is, and the period split is what localises the gap to a month. It is empty when the window is a single period (there is nothing further to split) or when the inventory side cannot be bucketed per period.\n\nsku_moved and check_a are null — never 0 — when the scope's inventory side cannot be asked at all. A zero would claim the two sides agree.\n\nAuthenticate with a Personal Access Token. Reads cached data only."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reconciliation/inventory/period-inventory?connection_id=5&nominal_code_id=87&month=2025-01&concept=fulfillment","host":["{{baseUrl}}"],"path":["api","ledger","reconciliation","inventory","period-inventory"],"query":[{"key":"connection_id","value":"5","description":"Accounting connection id. Required.","disabled":true},{"key":"nominal_code_id","value":"87","description":"The earmarked inventory control account id. Required.","disabled":true},{"key":"month","value":"2025-01","description":"The period to drill into. Accepts a month as YYYY-MM or a whole year as YYYY. Required.","disabled":true},{"key":"concept","value":"fulfillment","description":"The transaction-type concept whose inventory movements to list. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 990211,\n      \"movement_date\": \"2025-01-14T18:22:41.000000Z\",\n      \"type\": \"sale\",\n      \"warehouse_name\": \"Main Warehouse\",\n      \"product_sku\": \"HL-3528-WW-16\",\n      \"product_name\": \"3528 Warm White LED Strip, 16ft\",\n      \"quantity\": -12,\n      \"unit_cost\": 8.45,\n      \"value_delta\": -101.4,\n      \"reference\": \"SO-10442\",\n      \"link_type\": \"App\\\\Models\\\\SalesOrderFulfillmentLine\",\n      \"link_id\": 55347,\n      \"revaluation_id\": null,\n      \"original_movement_date\": null,\n      \"match_group\": null\n    },\n    {\n      \"id\": 990208,\n      \"movement_date\": \"2025-01-12T15:03:02.000000Z\",\n      \"type\": \"sale\",\n      \"warehouse_name\": \"Main Warehouse\",\n      \"product_sku\": \"HL-5050-RGB-32\",\n      \"product_name\": \"5050 RGB LED Strip, 32ft\",\n      \"quantity\": -6,\n      \"unit_cost\": 21.5,\n      \"value_delta\": -129,\n      \"reference\": \"SO-10436\",\n      \"link_type\": \"App\\\\Models\\\\SalesOrderFulfillmentLine\",\n      \"link_id\": 55302,\n      \"revaluation_id\": null,\n      \"original_movement_date\": null,\n      \"match_group\": 3\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 21,\n  \"per_page\": 25,\n  \"total\": 512,\n  \"from\": 1,\n  \"to\": 25,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/ledger/reconciliation/inventory/period-inventory?page=1\",\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/ledger/reconciliation/inventory/period-inventory?page=21\",\n  \"next_page_url\": \"{{protocol}}{{domain}}/api/ledger/reconciliation/inventory/period-inventory?page=2\",\n  \"prev_page_url\": null,\n  \"path\": \"{{protocol}}{{domain}}/api/ledger/reconciliation/inventory/period-inventory\",\n  \"links\": [],\n  \"meta\": {\n    \"concept_key\": \"fulfillment\",\n    \"label\": \"Sales fulfillments\",\n    \"period_label\": \"2025\",\n    \"provider_name\": \"Xero\",\n    \"sku_moved\": -52848.99,\n    \"gl_posted\": -43854.93,\n    \"check_a\": 8994.06,\n    \"itemised_total\": -52848.99,\n    \"unitemised_total\": 0,\n    \"matched_count\": 476,\n    \"matched_amount\": -43854.93,\n    \"unmatched_inventory_total\": -8994.06,\n    \"unmatched_ledger_total\": 0,\n    \"displaced_value\": 0,\n    \"has_inventory_side\": true,\n    \"periods\": [\n      {\n        \"period_key\": \"2025-01\",\n        \"label\": \"Jan 2025\",\n        \"sku_moved\": -9976.04,\n        \"gl_posted\": -9976.04,\n        \"check_a\": 0,\n        \"entry_count\": 6\n      },\n      {\n        \"period_key\": \"2025-02\",\n        \"label\": \"Feb 2025\",\n        \"sku_moved\": -12451.24,\n        \"gl_posted\": -3457.18,\n        \"check_a\": 8994.06,\n        \"entry_count\": 6\n      },\n      {\n        \"period_key\": \"2025-03\",\n        \"label\": \"Mar 2025\",\n        \"sku_moved\": -30421.71,\n        \"gl_posted\": -30421.71,\n        \"check_a\": 0,\n        \"entry_count\": 7\n      }\n    ]\n  }\n}"}]},{"name":"List Close Periods","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/close/periods","host":["{{baseUrl}}"],"path":["api","ledger","close","periods"]},"description":"Every month in the closable range (most recent first) with lifecycle status (open, in_review, closing, closed), checklist progress, blocking-failure and warning counts, and close sign-off metadata. Months are materialized lazily — a month that has never been interacted with reports status open with is_started false.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/close/periods","host":["{{baseUrl}}"],"path":["api","ledger","close","periods"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"periods\": [\n      {\n        \"period\": \"2026-06\",\n        \"period_start\": \"2026-06-01\",\n        \"period_end\": \"2026-06-30\",\n        \"status\": \"in_review\",\n        \"is_started\": true,\n        \"is_closable\": true,\n        \"is_reopenable\": false,\n        \"progress\": {\n          \"done\": 12,\n          \"total\": 14\n        },\n        \"blocking_failures\": 0,\n        \"warnings\": 2,\n        \"closed_at\": null,\n        \"closed_by\": null\n      }\n    ]\n  }\n}"}]},{"name":"Get Close Period","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period"],"variable":[{"key":"period","value":"","description":"The period ID."}]},"description":"Full detail for one period (period key format YYYY-MM): the close checklist with each item's latest evaluation, lock state (active lock date plus per-connection provider lock dates), report snapshot index, and close/reopen eligibility flags. For a month that has not been started, the checklist is a live read-only preview.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token.\n\nErrors: 422 when the period predates the accounting start date or is in the future.\n\nBlocking checks the month was closed over are returned in `overrides`, each with the reason given at the time, who closed the month, and the figures for that check as they stood when the close committed (`exposure`). The figures are recorded, never recomputed, so a later correction cannot rewrite what was signed off. Empty on a month that closed cleanly."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period"],"variable":[{"key":"period","value":"","description":"The period ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"period\": {\n      \"period\": \"2026-06\",\n      \"period_start\": \"2026-06-01\",\n      \"period_end\": \"2026-06-30\",\n      \"status\": \"in_review\",\n      \"status_label\": \"In Review\",\n      \"is_started\": true,\n      \"is_closable\": true,\n      \"is_reopenable\": false,\n      \"closed_at\": null,\n      \"closed_by\": null,\n      \"reopened_at\": null,\n      \"reopened_by\": null,\n      \"reopen_reason\": null,\n      \"notes\": null,\n      \"lock\": {\n        \"active_lock_date\": \"2026-05-31\",\n        \"provider_locks\": [\n          {\n            \"connection_id\": 30,\n            \"connection_name\": \"Xero\",\n            \"lock_date\": \"2026-05-31\"\n          }\n        ]\n      },\n      \"checklist\": [\n        {\n          \"id\": 41,\n          \"key\": \"entries_balanced\",\n          \"title\": \"All entries balanced\",\n          \"description\": null,\n          \"category\": \"data_completeness\",\n          \"category_label\": \"Data completeness\",\n          \"type\": \"automated\",\n          \"severity\": \"blocking\",\n          \"status\": \"pass\",\n          \"result\": {\n            \"count\": 0,\n            \"link\": {\n              \"tab\": \"transactions\",\n              \"filter\": {\n                \"balance_mismatch\": true\n              }\n            }\n          },\n          \"acknowledged\": false,\n          \"completed_by\": null,\n          \"completed_at\": null,\n          \"evaluated_at\": \"2026-07-17T10:04:00+00:00\",\n          \"sort_order\": 11,\n          \"can_be_overridden\": false,\n          \"user_can_override\": false\n        },\n        {\n          \"id\": 52,\n          \"key\": \"review_income_statement\",\n          \"title\": \"Review Income Statement\",\n          \"description\": \"Read through the month's Income Statement for unexpected balances before sign-off.\",\n          \"category\": \"review\",\n          \"category_label\": \"Review\",\n          \"type\": \"manual\",\n          \"severity\": null,\n          \"status\": \"done\",\n          \"result\": {\n            \"report_key\": \"income_statement\"\n          },\n          \"acknowledged\": false,\n          \"completed_by\": {\n            \"id\": 5,\n            \"name\": \"Dana Bookkeeper\"\n          },\n          \"completed_at\": \"2026-07-16T22:10:00+00:00\",\n          \"evaluated_at\": null,\n          \"sort_order\": 300,\n          \"can_be_overridden\": false,\n          \"user_can_override\": false\n        },\n        {\n          \"id\": 47,\n          \"key\": \"inventory_debt_outstanding\",\n          \"title\": \"Inventory debt settled\",\n          \"description\": \"No units shipped without stock behind them are still owed for this period. Outstanding debt means the period's cost of sale is still an estimate, with the correction landing later.\",\n          \"category\": \"reconciliation\",\n          \"category_label\": \"Reconciliation\",\n          \"type\": \"automated\",\n          \"severity\": \"blocking\",\n          \"status\": \"fail\",\n          \"result\": {\n            \"count\": 3,\n            \"units\": 12.5,\n            \"clearing_balance\": 418.75,\n            \"oldest_age_days\": 9,\n            \"link\": {\n              \"path\": \"/inventory/fulfillment-debt-report\",\n              \"filter\": {\n                \"period\": \"2026-06\"\n              }\n            }\n          },\n          \"acknowledged\": false,\n          \"completed_by\": null,\n          \"completed_at\": null,\n          \"evaluated_at\": \"2026-07-17T10:04:00+00:00\",\n          \"sort_order\": 47,\n          \"can_be_overridden\": true,\n          \"user_can_override\": true\n        }\n      ],\n      \"snapshots\": [],\n      \"overrides\": [\n        {\n          \"check_key\": \"inventory_debt_outstanding\",\n          \"title\": \"Inventory debt settled\",\n          \"reason\": \"Supplier receipt lands on the 3rd - signed off by the CFO\",\n          \"exposure\": \"3 still owed · 409.75 in clearing at the time of the close\",\n          \"closed_by\": \"Dana Whitfield\",\n          \"recorded_at\": \"2026-07-03T16:41:08+00:00\"\n        }\n      ]\n    }\n  }\n}"}]},{"name":"Start Period Close","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/start","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","start"],"variable":[{"key":"period","value":"","description":"The period ID."}]},"description":"Begin the close for a month: materializes the period, seeds its checklist (automated checks, built-in review items, and any custom items configured in accounting settings), and evaluates every automated check. Returns the full period detail.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token. Requires the accounting sync permission."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/start","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","start"],"variable":[{"key":"period","value":"","description":"The period ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"period\": {\n      \"period\": \"2026-06\",\n      \"period_start\": \"2026-06-01\",\n      \"period_end\": \"2026-06-30\",\n      \"status\": \"in_review\",\n      \"status_label\": \"In Review\",\n      \"is_started\": true,\n      \"is_closable\": true,\n      \"is_reopenable\": false,\n      \"closed_at\": null,\n      \"closed_by\": null,\n      \"reopened_at\": null,\n      \"reopened_by\": null,\n      \"reopen_reason\": null,\n      \"notes\": null,\n      \"lock\": {\n        \"active_lock_date\": \"2026-05-31\",\n        \"provider_locks\": [\n          {\n            \"connection_id\": 30,\n            \"connection_name\": \"Xero\",\n            \"lock_date\": \"2026-05-31\"\n          }\n        ]\n      },\n      \"checklist\": [\n        {\n          \"id\": 41,\n          \"key\": \"entries_balanced\",\n          \"title\": \"All entries balanced\",\n          \"description\": null,\n          \"category\": \"data_completeness\",\n          \"category_label\": \"Data completeness\",\n          \"type\": \"automated\",\n          \"severity\": \"blocking\",\n          \"status\": \"pass\",\n          \"result\": {\n            \"count\": 0,\n            \"link\": {\n              \"tab\": \"transactions\",\n              \"filter\": {\n                \"balance_mismatch\": true\n              }\n            }\n          },\n          \"acknowledged\": false,\n          \"completed_by\": null,\n          \"completed_at\": null,\n          \"evaluated_at\": \"2026-07-17T10:04:00+00:00\",\n          \"sort_order\": 11\n        },\n        {\n          \"id\": 52,\n          \"key\": \"review_income_statement\",\n          \"title\": \"Review Income Statement\",\n          \"description\": \"Read through the month's Income Statement for unexpected balances before sign-off.\",\n          \"category\": \"review\",\n          \"category_label\": \"Review\",\n          \"type\": \"manual\",\n          \"severity\": null,\n          \"status\": \"done\",\n          \"result\": {\n            \"report_key\": \"income_statement\"\n          },\n          \"acknowledged\": false,\n          \"completed_by\": {\n            \"id\": 5,\n            \"name\": \"Dana Bookkeeper\"\n          },\n          \"completed_at\": \"2026-07-16T22:10:00+00:00\",\n          \"evaluated_at\": null,\n          \"sort_order\": 300\n        }\n      ],\n      \"snapshots\": []\n    }\n  },\n  \"message\": \"Close started for 2026-06.\"\n}"}]},{"name":"Refresh Close Checklist","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/refresh","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","refresh"],"variable":[{"key":"period","value":"","description":"The period ID."}]},"description":"Re-evaluate every automated check on a started, non-closed period and append any newly configured custom checklist items. Completed manual items are never reset.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token. Requires the accounting sync permission.\n\nErrors: 422 when the period has not been started or is already closed."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/refresh","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","refresh"],"variable":[{"key":"period","value":"","description":"The period ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"period\": {\n      \"period\": \"2026-06\",\n      \"period_start\": \"2026-06-01\",\n      \"period_end\": \"2026-06-30\",\n      \"status\": \"in_review\",\n      \"status_label\": \"In Review\",\n      \"is_started\": true,\n      \"is_closable\": true,\n      \"is_reopenable\": false,\n      \"closed_at\": null,\n      \"closed_by\": null,\n      \"reopened_at\": null,\n      \"reopened_by\": null,\n      \"reopen_reason\": null,\n      \"notes\": null,\n      \"lock\": {\n        \"active_lock_date\": \"2026-05-31\",\n        \"provider_locks\": [\n          {\n            \"connection_id\": 30,\n            \"connection_name\": \"Xero\",\n            \"lock_date\": \"2026-05-31\"\n          }\n        ]\n      },\n      \"checklist\": [\n        {\n          \"id\": 41,\n          \"key\": \"entries_balanced\",\n          \"title\": \"All entries balanced\",\n          \"description\": null,\n          \"category\": \"data_completeness\",\n          \"category_label\": \"Data completeness\",\n          \"type\": \"automated\",\n          \"severity\": \"blocking\",\n          \"status\": \"pass\",\n          \"result\": {\n            \"count\": 0,\n            \"link\": {\n              \"tab\": \"transactions\",\n              \"filter\": {\n                \"balance_mismatch\": true\n              }\n            }\n          },\n          \"acknowledged\": false,\n          \"completed_by\": null,\n          \"completed_at\": null,\n          \"evaluated_at\": \"2026-07-17T10:04:00+00:00\",\n          \"sort_order\": 11\n        },\n        {\n          \"id\": 52,\n          \"key\": \"review_income_statement\",\n          \"title\": \"Review Income Statement\",\n          \"description\": \"Read through the month's Income Statement for unexpected balances before sign-off.\",\n          \"category\": \"review\",\n          \"category_label\": \"Review\",\n          \"type\": \"manual\",\n          \"severity\": null,\n          \"status\": \"done\",\n          \"result\": {\n            \"report_key\": \"income_statement\"\n          },\n          \"acknowledged\": false,\n          \"completed_by\": {\n            \"id\": 5,\n            \"name\": \"Dana Bookkeeper\"\n          },\n          \"completed_at\": \"2026-07-16T22:10:00+00:00\",\n          \"evaluated_at\": null,\n          \"sort_order\": 300\n        }\n      ],\n      \"snapshots\": []\n    }\n  },\n  \"message\": \"Checklist re-evaluated.\"\n}"}]},{"name":"Complete Checklist Item","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/checklist/{item}/complete","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","checklist",":item","complete"],"variable":[{"key":"period","value":"","description":"The period ID."},{"key":"item","value":"","description":"The item ID."}]},"description":"Check a manual checklist item off, recording who completed it and when. Automated checks cannot be checked off — re-evaluate them with Refresh Close Checklist instead.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token. Requires the accounting sync permission.\n\nErrors: 422 for automated items or when the period is closed."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/checklist/{item}/complete","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","checklist",":item","complete"],"variable":[{"key":"period","value":"","description":"The period ID."},{"key":"item","value":"","description":"The item ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"item\": {\n      \"id\": 52,\n      \"key\": \"review_income_statement\",\n      \"title\": \"Review Income Statement\",\n      \"description\": \"Read through the month's Income Statement for unexpected balances before sign-off.\",\n      \"category\": \"review\",\n      \"category_label\": \"Review\",\n      \"type\": \"manual\",\n      \"severity\": null,\n      \"status\": \"done\",\n      \"result\": {\n        \"report_key\": \"income_statement\"\n      },\n      \"acknowledged\": false,\n      \"completed_by\": {\n        \"id\": 5,\n        \"name\": \"Dana Bookkeeper\"\n      },\n      \"completed_at\": \"2026-07-16T22:10:00+00:00\",\n      \"evaluated_at\": null,\n      \"sort_order\": 300\n    }\n  },\n  \"message\": \"Review Income Statement checked off.\"\n}"}]},{"name":"Uncomplete Checklist Item","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/checklist/{item}/complete","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","checklist",":item","complete"],"variable":[{"key":"period","value":"","description":"The period ID."},{"key":"item","value":"","description":"The item ID."}]},"description":"Undo a manual checklist item's completion on a non-closed period.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token. Requires the accounting sync permission."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/checklist/{item}/complete","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","checklist",":item","complete"],"variable":[{"key":"period","value":"","description":"The period ID."},{"key":"item","value":"","description":"The item ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"item\": {\n      \"id\": 52,\n      \"key\": \"review_income_statement\",\n      \"title\": \"Review Income Statement\",\n      \"description\": \"Read through the month's Income Statement for unexpected balances before sign-off.\",\n      \"category\": \"review\",\n      \"category_label\": \"Review\",\n      \"type\": \"manual\",\n      \"severity\": null,\n      \"status\": \"open\",\n      \"result\": {\n        \"report_key\": \"income_statement\"\n      },\n      \"acknowledged\": false,\n      \"completed_by\": null,\n      \"completed_at\": null,\n      \"evaluated_at\": null,\n      \"sort_order\": 300\n    }\n  },\n  \"message\": \"Review Income Statement reopened.\"\n}"}]},{"name":"Close Period","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/close","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","close"],"variable":[{"key":"period","value":"","description":"The period ID."}]},"description":"Close and lock the period. Only the earliest non-closed month can close (periods close strictly in order). Every failing blocking check refuses the close; every warning-level failure and incomplete manual item must be listed in acknowledged_keys. The close runs as a background job that re-validates all gates, snapshots the report pack, inserts the period lock (new entries dated on or before the period end are refused; corrections are redated to the first open day), and records the sign-off. Returns the background job id.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token. Requires the accounting settings management permission.\n\nErrors: 422 when a blocking check fails, an acknowledgement is missing, the month is out of order, or another close is already running.\n\noverrides: an optional object of {check key: reason} for blocking checks that permit being closed over. Only checks flagged can_be_overridden in the checklist accept one, the reason is required and is stored with the close, and the caller must hold the matching permission - otherwise the request is refused with 403. Today the only such check is inventory_debt_outstanding, which blocks while units that shipped without stock are still owed; closing over it records the reason together with the number of outstanding claims and the unrelieved clearing balance at that moment. Checks that are not overridable, such as unbalanced entries, still refuse the close at any permission level.","body":{"mode":"raw","raw":"{\n  \"acknowledged_keys\": [\n    \"provider_docs_reconciled\",\n    \"review_income_statement\",\n    \"review_balance_sheet\",\n    \"review_inventory_valuation\"\n  ],\n  \"notes\": \"June close complete.\",\n  \"overrides\": {\n    \"inventory_debt_outstanding\": \"Supplier receipt lands on the 3rd - signed off by the CFO\"\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/close","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","close"],"variable":[{"key":"period","value":"","description":"The period ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 9311\n  },\n  \"message\": \"Closing 2026-06 — track progress in the job tray.\"\n}"}]},{"name":"Reopen Period","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/reopen","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","reopen"],"variable":[{"key":"period","value":"","description":"The period ID."}]},"description":"Reopen the most recently closed period (a reason is required and audit-logged). Removes the period's own lock, flags its report snapshots stale, and returns the period to review. When the accounting provider still has the month locked, residual_provider_locks lists those connections — entries stay locked provider-side until the books are reopened there too.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token. Requires the accounting settings management permission.\n\nErrors: 422 when the period is not the most recently closed one or a close is currently running.","body":{"mode":"raw","raw":"{\n  \"reason\": \"Client sent late invoices for June.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/reopen","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","reopen"],"variable":[{"key":"period","value":"","description":"The period ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"period\": {\n      \"period\": \"2026-06\",\n      \"period_start\": \"2026-06-01\",\n      \"period_end\": \"2026-06-30\",\n      \"status\": \"in_review\",\n      \"status_label\": \"In Review\",\n      \"is_started\": true,\n      \"is_closable\": true,\n      \"is_reopenable\": false,\n      \"closed_at\": null,\n      \"closed_by\": null,\n      \"reopened_at\": null,\n      \"reopened_by\": null,\n      \"reopen_reason\": null,\n      \"notes\": null,\n      \"lock\": {\n        \"active_lock_date\": \"2026-05-31\",\n        \"provider_locks\": [\n          {\n            \"connection_id\": 30,\n            \"connection_name\": \"Xero\",\n            \"lock_date\": \"2026-05-31\"\n          }\n        ]\n      },\n      \"checklist\": [\n        {\n          \"id\": 41,\n          \"key\": \"entries_balanced\",\n          \"title\": \"All entries balanced\",\n          \"description\": null,\n          \"category\": \"data_completeness\",\n          \"category_label\": \"Data completeness\",\n          \"type\": \"automated\",\n          \"severity\": \"blocking\",\n          \"status\": \"pass\",\n          \"result\": {\n            \"count\": 0,\n            \"link\": {\n              \"tab\": \"transactions\",\n              \"filter\": {\n                \"balance_mismatch\": true\n              }\n            }\n          },\n          \"acknowledged\": false,\n          \"completed_by\": null,\n          \"completed_at\": null,\n          \"evaluated_at\": \"2026-07-17T10:04:00+00:00\",\n          \"sort_order\": 11\n        },\n        {\n          \"id\": 52,\n          \"key\": \"review_income_statement\",\n          \"title\": \"Review Income Statement\",\n          \"description\": \"Read through the month's Income Statement for unexpected balances before sign-off.\",\n          \"category\": \"review\",\n          \"category_label\": \"Review\",\n          \"type\": \"manual\",\n          \"severity\": null,\n          \"status\": \"done\",\n          \"result\": {\n            \"report_key\": \"income_statement\"\n          },\n          \"acknowledged\": false,\n          \"completed_by\": {\n            \"id\": 5,\n            \"name\": \"Dana Bookkeeper\"\n          },\n          \"completed_at\": \"2026-07-16T22:10:00+00:00\",\n          \"evaluated_at\": null,\n          \"sort_order\": 300\n        }\n      ],\n      \"snapshots\": []\n    },\n    \"residual_provider_locks\": []\n  },\n  \"message\": \"2026-06 reopened.\"\n}"}]},{"name":"Get Close Report","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/reports/{report}","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","reports",":report"],"variable":[{"key":"period","value":"","description":"The period ID."},{"key":"report","value":"","description":"The report ID."}]},"description":"One report of the month-end pack. Report keys: income_statement, balance_sheet, trial_balance, inventory_valuation, cogs, ginr, reconciliation_summary. On a closed period this returns the snapshot frozen at close time (source: snapshot); otherwise a live build flagged preliminary. Payloads carry statement-level rows plus a totals map of headline figures.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token.\n\nErrors: 422 for an unknown report key."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/reports/{report}","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","reports",":report"],"variable":[{"key":"period","value":"","description":"The period ID."},{"key":"report","value":"","description":"The report ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"report\": {\n      \"report_key\": \"trial_balance\",\n      \"label\": \"Trial Balance\",\n      \"source\": \"snapshot\",\n      \"preliminary\": false,\n      \"is_stale\": false,\n      \"generated_at\": \"2026-07-05T14:22:00+00:00\",\n      \"payload\": {\n        \"totals\": {\n          \"total_opening\": 0,\n          \"total_debits\": 633597.98,\n          \"total_credits\": 633597.98,\n          \"total_closing\": 0\n        },\n        \"rows\": [\n          {\n            \"account_code\": \"1200\",\n            \"account_name\": \"Inventory\",\n            \"opening_balance\": 700000,\n            \"period_debits\": 45000,\n            \"period_credits\": 38000,\n            \"closing_balance\": 707000\n          },\n          {\n            \"account_code\": \"4000\",\n            \"account_name\": \"Sales\",\n            \"opening_balance\": 0,\n            \"period_debits\": 0,\n            \"period_credits\": 80095.74,\n            \"closing_balance\": -80095.74\n          }\n        ]\n      }\n    }\n  }\n}"}]},{"name":"Verify Closed Period","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/verify","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","verify"],"variable":[{"key":"period","value":"","description":"The period ID."}]},"description":"Drift check for a closed period: recomputes each snapshotted report live and diffs the totals against the snapshot with a $0.01 tolerance. Any difference means the closed month's numbers moved after close (for example via data repairs) and lists the changed figures. The reconciliation summary is informational — it legitimately changes as reconciliation work continues and never trips the period-level verdict.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token.\n\nErrors: 422 when the period is not closed."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/verify","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","verify"],"variable":[{"key":"period","value":"","description":"The period ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"verification\": {\n      \"period\": \"2026-06\",\n      \"has_drift\": false,\n      \"reports\": [\n        {\n          \"report_key\": \"trial_balance\",\n          \"label\": \"Trial Balance\",\n          \"matches\": true,\n          \"informational\": false,\n          \"is_stale\": false,\n          \"snapshot_generated_at\": \"2026-07-05T14:22:00+00:00\",\n          \"drift\": []\n        },\n        {\n          \"report_key\": \"reconciliation_summary\",\n          \"label\": \"Reconciliation Summary\",\n          \"matches\": false,\n          \"informational\": true,\n          \"is_stale\": false,\n          \"snapshot_generated_at\": \"2026-07-05T14:22:00+00:00\",\n          \"drift\": [\n            {\n              \"field\": \"provider_variance\",\n              \"snapshot\": 13891.46,\n              \"live\": 12100.02\n            }\n          ]\n        }\n      ]\n    }\n  }\n}"}]},{"name":"Capture Provider Trial Balances","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/capture-provider-balances","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","capture-provider-balances"],"variable":[{"key":"period","value":"","description":"The period ID."}]},"description":"Capture the accounting provider's trial balance as of the period end (period key format YYYY-MM), for every connected accounting integration. This is the one metered provider pull behind the Trial Balance Reconciliation report and the trial_balance_reconciled close check — both read only the cached snapshot this produces. Synchronous: one trial-balance call per connection; connections whose provider exposes no trial-balance report are skipped, not failed. Re-running re-captures in place so provider-side edits are picked up.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token. Requires the accounting sync permission."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/capture-provider-balances","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","capture-provider-balances"],"variable":[{"key":"period","value":"","description":"The period ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"captured\": 1,\n    \"unsupported\": 0\n  },\n  \"message\": \"1 provider trial balance captured. Re-check the close to reconcile.\"\n}"}]},{"name":"Sync Breakdown (why not synced)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/sync-breakdown","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","sync-breakdown"],"variable":[{"key":"period","value":"","description":"The period ID."}]},"description":"The why-not-synced breakdown. For the period (key format YYYY-MM), returns every journal entry that has NOT reached the accounting provider, grouped by reason (display status), with an entry count, a dollar total, and a per-reason deep-link filter that lands the entries list on exactly those rows. The synced total rides along as context (how much DID sync). Read-only and entirely local — no provider call.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nEach row's `disposition` is one of: `excluded` (unsynced by design), `in_flight` (queued/syncing/retrying), `needs_attention` (failed/conflict/removed-in-provider/needs-attention). `link` is a checklist-style deep link ({tab, filter}) for the entries list.\n\nAuthentication: Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/sync-breakdown","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","sync-breakdown"],"variable":[{"key":"period","value":"","description":"The period ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"breakdown\": {\n      \"synced\": {\n        \"entry_count\": 128,\n        \"total\": 84250\n      },\n      \"rows\": [\n        {\n          \"entry_display_status\": \"excluded\",\n          \"label\": \"Disabled\",\n          \"disposition\": \"excluded\",\n          \"reason\": \"Excluded by design — not configured to sync to the provider.\",\n          \"entry_count\": 12,\n          \"total\": 3400,\n          \"link\": {\n            \"tab\": \"all\",\n            \"filter\": {\n              \"entry_display_status\": \"excluded\"\n            }\n          }\n        },\n        {\n          \"entry_display_status\": \"failed\",\n          \"label\": \"Failed\",\n          \"disposition\": \"needs_attention\",\n          \"reason\": \"Failed — the provider rejected the push.\",\n          \"entry_count\": 2,\n          \"total\": 560,\n          \"link\": {\n            \"tab\": \"all\",\n            \"filter\": {\n              \"entry_display_status\": \"failed\"\n            }\n          }\n        }\n      ],\n      \"totals\": {\n        \"unsynced_count\": 14,\n        \"unsynced_total\": 3960\n      }\n    }\n  }\n}"}]},{"name":"Export Close Pack","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/export","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","export"],"variable":[{"key":"period","value":"","description":"The period ID."}]},"description":"Generate the month-end close pack workbook (XLSX) as a background job: a cover sheet with the period's status, sign-off and checklist outcomes, plus one sheet per report of the pack. Closed periods export their frozen snapshots; open periods export live figures flagged PRELIMINARY. The finished file is delivered as a download from the job result.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token. Requires the accounting sync permission."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/close/periods/{period}/export","host":["{{baseUrl}}"],"path":["api","ledger","close","periods",":period","export"],"variable":[{"key":"period","value":"","description":"The period ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 9312\n  },\n  \"message\": \"Close pack export started — the download will appear in the job tray.\"\n}"}]},{"name":"List Lock Date Exceptions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reports/lock-date-exceptions","host":["{{baseUrl}}"],"path":["api","ledger","reports","lock-date-exceptions"]},"description":"Paginated journal entries displaced out of a locked accounting period (booked on the first open day, source date on `original_effective_at`).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Filters:** type, source_type, original_from/original_to (m/d/Y). **Sort:** original_effective_at (default), effective_at, id, document_total. **Pagination:** page, per_page (default 15, max 100)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reports/lock-date-exceptions","host":["{{baseUrl}}"],"path":["api","ledger","reports","lock-date-exceptions"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 8891,\n      \"type\": {\n        \"value\": \"customer_return\",\n        \"label\": \"Customer Return\"\n      },\n      \"status\": \"posted\",\n      \"effective_at\": \"2026-07-01T00:00:00+00:00\",\n      \"original_effective_at\": \"2026-05-12T00:00:00+00:00\",\n      \"posted_at\": \"2026-07-01T00:00:00+00:00\",\n      \"reference\": \"CR-1042/R1\",\n      \"document_total\": \"210.55\",\n      \"currency_code\": \"USD\",\n      \"display_status\": {\n        \"key\": \"synced\",\n        \"label\": \"Synced\",\n        \"color\": \"success\"\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Get Lock Date Exceptions Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reports/lock-date-exceptions/summary","host":["{{baseUrl}}"],"path":["api","ledger","reports","lock-date-exceptions","summary"]},"description":"Displaced-entry counts and net document-total impact grouped by entry type, plus grand totals. Honours the same original_from/original_to + type/source_type filters as the list.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reports/lock-date-exceptions/summary","host":["{{baseUrl}}"],"path":["api","ledger","reports","lock-date-exceptions","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"summary\": {\n      \"total_count\": 14,\n      \"total_amount\": \"3412.88\",\n      \"by_type\": [\n        {\n          \"type\": \"customer_return\",\n          \"type_label\": \"Customer Return\",\n          \"count\": 6,\n          \"amount\": \"-1204.10\"\n        },\n        {\n          \"type\": \"bill\",\n          \"type_label\": \"Bill\",\n          \"count\": 5,\n          \"amount\": \"4102.33\"\n        },\n        {\n          \"type\": \"fba_customer_returns\",\n          \"type_label\": \"FBA Customer Returns\",\n          \"count\": 3,\n          \"amount\": \"514.65\"\n        }\n      ]\n    }\n  }\n}"}]},{"name":"List Restatement Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/reports/restatement-log","host":["{{baseUrl}}"],"path":["api","ledger","reports","restatement-log"]},"description":"Paginated audit log of retroactive cost changes that reached an accounting period you have already reported on: every change applied back to its original stock dates, plus every change whose direction was later switched.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nEach row carries the document that triggered the change, every accounting period it re-costed, the total value moved split into its on-hand and consumed shares, who resolved it and when, and links to the journal entries it posted.\n\n**Filters:** trigger_type, trigger_types (csv), resolution, resolved_by, period, period_from/period_to (YYYY-MM), resolved_from/resolved_to (m/d/Y), search. **Sort:** resolved_at (default, descending), id, created_at, total_delta, on_hand_delta, consumed_delta, earliest_affected_period, affected_products_count, affected_layers_count. **Pagination:** page, per_page (default 10)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/reports/restatement-log","host":["{{baseUrl}}"],"path":["api","ledger","reports","restatement-log"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 41,\n      \"uid\": \"4f0a1c5e-9a1e-4a6b-9f0c-2f7a1d3b8c11\",\n      \"trigger_type\": \"landed_cost_bill\",\n      \"trigger_type_label\": \"Landed Cost Bill\",\n      \"trigger\": {\n        \"type\": \"App\\\\Models\\\\Bill\",\n        \"type_label\": \"Bill\",\n        \"id\": 977,\n        \"reference\": \"FRT-2091\",\n        \"date\": \"2026-02-11\",\n        \"route\": \"/purchases/bills/977\"\n      },\n      \"status\": \"applied\",\n      \"resolution\": \"restate\",\n      \"applied_via\": \"manual\",\n      \"policy_rule_applied\": \"always_review_closed_impact\",\n      \"totals\": {\n        \"delta\": 1840,\n        \"on_hand_delta\": 1219.1,\n        \"consumed_delta\": 620.9,\n        \"products\": 3,\n        \"layers\": 14\n      },\n      \"earliest_affected_period\": \"2026-01\",\n      \"flags\": {\n        \"locked_period\": false,\n        \"closed_period\": true,\n        \"synced_entries\": false,\n        \"ledger_held\": false,\n        \"backfilled\": false,\n        \"switched\": false\n      },\n      \"notes\": \"Freight bill arrived after the January receipt was reported.\",\n      \"resolved_by\": \"Dana Whitfield\",\n      \"resolved_at\": \"2026-02-12T16:04:11+00:00\",\n      \"restaged_at\": null,\n      \"preview_computed_at\": \"2026-02-11T09:20:00+00:00\",\n      \"created_at\": \"2026-02-11T09:20:00+00:00\",\n      \"updated_at\": \"2026-02-12T16:04:11+00:00\",\n      \"affected_periods\": [\n        \"2026-01\",\n        \"2026-02\"\n      ],\n      \"journal_entries\": [\n        {\n          \"id\": 88213,\n          \"reference\": \"INV-REVAL-88-2026-01\",\n          \"route\": \"/ledger/entries/88213\"\n        },\n        {\n          \"id\": 88214,\n          \"reference\": \"INV-REVAL-88-2026-02\",\n          \"route\": \"/ledger/entries/88214\"\n        }\n      ]\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"List Outbox Events","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/outbox","host":["{{baseUrl}}"],"path":["api","ledger","outbox"]},"description":"Paginated raw listing of every `ledger_outbox` row — the queue of source-document changes awaiting (or finished with) accounting-entry generation. Unlike List Outbox Failures, this endpoint applies no masking: every status is visible, including failures already superseded by a newer event for the same source.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n**Statuses:** `pending` (queued, claimable at `available_at`), `processing` (claimed by the drain), `processed` (entry generation completed), `superseded` (coalesced away by a newer event for the same source), `failed` (retry budget exhausted or a deterministic defect), `skipped` (policy refusal — e.g. a locked accounting period).\n\n**Filters:** per-column filters (`filter[status]`, `filter[event]`, `filter[source_type]`, `filter[source_id]`, `filter[attempts]`), free-text `filter[search]`, and an advanced `filter_groups` tree. The `backlog` checkbox column (operator `is`, value true) scopes to the rows the queue still owes work for — pending, processing, and failures no newer attempt has superseded — exactly the dashboard's `outbox_backlog` count; value false returns the complement (history).\n\n**Sorts:** id, source_type, source_id, event, status, attempts, available_at, created_at, processed_at (default `-id`).\n\n**Pagination:** `per_page` (default 10, clamped to 100), `page`. Standard Laravel pagination envelope.\n\n**Each row:** the resolved `source` cross-link, raw `source_type`/`source_id` scalars, the `event` and `status` (value + label), `attempts`, `bypass_cutover_floor`, the preserved `last_error` (capped at 60 KB), and `available_at` / `created_at` / `processed_at` ISO-8601 timestamps (`processed_at` doubles as the failure or supersession timestamp for terminal rows)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/outbox","host":["{{baseUrl}}"],"path":["api","ledger","outbox"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 84213,\n      \"source\": {\n        \"type\": \"App\\\\Models\\\\SalesOrder\",\n        \"id\": 1042,\n        \"label\": \"SO-1042\",\n        \"link_type_label\": \"Sales Order\",\n        \"link_route\": \"/orders/sales-orders/1042\",\n        \"link_legacy_url\": null,\n        \"url\": \"/orders/sales-orders/1042\"\n      },\n      \"source_type\": \"App\\\\Models\\\\SalesOrder\",\n      \"source_type_label\": \"SalesOrder\",\n      \"source_id\": 1042,\n      \"event\": {\n        \"value\": \"updated\",\n        \"label\": \"Updated\"\n      },\n      \"status\": {\n        \"value\": \"failed\",\n        \"label\": \"Failed\"\n      },\n      \"attempts\": 5,\n      \"bypass_cutover_floor\": false,\n      \"last_error\": \"Nominal account 4000 is not mapped for this source.\",\n      \"available_at\": \"2026-08-15T08:44:12+00:00\",\n      \"created_at\": \"2026-08-15T08:02:03+00:00\",\n      \"processed_at\": \"2026-08-15T08:44:12+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"List Outbox Source Types","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/ledger/outbox/source-types","host":["{{baseUrl}}"],"path":["api","ledger","outbox","source-types"]},"description":"The distinct source-document types currently present in the outbox, for populating a source-type filter. Each option carries the fully-qualified type name (use it as `filter[source_type]` on List Outbox Events) and a short human-readable label.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/ledger/outbox/source-types","host":["{{baseUrl}}"],"path":["api","ledger","outbox","source-types"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"source_types\": [\n      {\n        \"value\": \"App\\\\Models\\\\PurchaseInvoice\",\n        \"label\": \"PurchaseInvoice\"\n      },\n      {\n        \"value\": \"App\\\\Models\\\\SalesOrder\",\n        \"label\": \"SalesOrder\"\n      },\n      {\n        \"value\": \"Modules\\\\Amazon\\\\Entities\\\\AmazonLedger\",\n        \"label\": \"AmazonLedger\"\n      }\n    ]\n  }\n}"}]},{"name":"Bulk Retry Failed Outbox Events","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ledger/outbox/retry","host":["{{baseUrl}}"],"path":["api","ledger","outbox","retry"]},"description":"Re-queue failed outbox rows for another generation pass: their status resets to pending with a fresh retry budget and the drain worker is kicked. Only unresolved failures are re-queued — rows in other statuses, and failures already superseded by a newer event for the same source, are skipped silently, and the response reports how many rows were actually re-queued.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token with the accounting sync permission.\n\n**Body — one of two shapes:**\n- `{ \"ids\": [1, 2, 3] }` — retry an explicit selection (required without `apply_to_all`).\n- `{ \"apply_to_all\": true, \"filters\": { ... } }` — retry every failed row matching the given filter set. `filters` accepts the same columns as List Outbox Events (bare column keys, e.g. `{\"source_type\": \"...\"}`), plus a `filter_groups` tree.\n\nNote: unlike Retry Outbox Failures, an empty selection retries nothing.","body":{"mode":"raw","raw":"{\n  \"apply_to_all\": true,\n  \"filters\": {\n    \"status\": \"failed\",\n    \"source_type\": \"App\\\\Models\\\\SalesOrder\"\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ledger/outbox/retry","host":["{{baseUrl}}"],"path":["api","ledger","outbox","retry"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"requeued\": 3\n  },\n  \"message\": \"3 failed rows re-queued — generation will reprocess them shortly.\"\n}"}]}]},{"name":"Inventory","item":[{"name":"Get Inventory Movement Tallies","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-movements/tallies?product_id=42&warehouse_id=1&inventory_status=active","host":["{{baseUrl}}"],"path":["api","inventory-movements","tallies"],"query":[{"key":"product_id","value":"42","description":"(Required) Product ID to tally. 404 if not found.","disabled":true},{"key":"warehouse_id","value":"1","description":"(Required) Warehouse ID to tally within.","disabled":true},{"key":"inventory_status","value":"active","description":"(Required) Stock ledger to tally: active or in_transit.","disabled":true}]},"description":"Get running inventory tallies for a single product in a single warehouse. Returns a map keyed by inventory movement ID where each value is the cumulative on-hand quantity (running balance) after that movement was applied, computed in movement-date order (oldest first).\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAll three query parameters are required:\n- `product_id` — the product to tally. Returns 404 if the product does not exist.\n- `warehouse_id` — the warehouse to tally within.\n- `inventory_status` — which stock ledger to tally: `active` (on-hand stock) or `in_transit` (stock moving between warehouses).\n\nUse this to reconstruct the balance history of a product at a warehouse, movement by movement.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-movements/tallies?product_id=42&warehouse_id=1&inventory_status=active","host":["{{baseUrl}}"],"path":["api","inventory-movements","tallies"],"query":[{"key":"product_id","value":"42","description":"(Required) Product ID to tally. 404 if not found.","disabled":true},{"key":"warehouse_id","value":"1","description":"(Required) Warehouse ID to tally within.","disabled":true},{"key":"inventory_status","value":"active","description":"(Required) Stock ledger to tally: active or in_transit.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"18234\": 100,\n    \"18240\": 88,\n    \"18255\": 63,\n    \"18291\": 113\n  }\n}"}]},{"name":"Get Inventory Movement","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-movements/{inventoryMovement}","host":["{{baseUrl}}"],"path":["api","inventory-movements",":inventoryMovement"],"variable":[{"key":"inventoryMovement","value":"18234","description":"Inventory movement ID"}]},"description":"Get a single inventory movement with its product and warehouse details.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAn inventory movement is one immutable ledger entry: a positive quantity adds stock (purchase receipt, return, adjustment up, assembly output) and a negative quantity removes stock (sale, transfer out, adjustment down). Movement `type` values include: purchase_receipt, transfer, sale, adjustment, stock_take, assembly, return, purchase_return, reclassification, manufacturing_consumption, manufacturing_scrap, manufacturing_output. `inventory_status` is `active` (on-hand) or `in_transit` (between warehouses).\n\nThe `type` object also carries the polymorphic source document link (`link_type` + `link_id`) that created the movement, and `layer` identifies the FIFO cost layer the movement consumed or created.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-movements/{inventoryMovement}","host":["{{baseUrl}}"],"path":["api","inventory-movements",":inventoryMovement"],"variable":[{"key":"inventoryMovement","value":"18234","description":"Inventory movement ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 18234,\n    \"inventory_movement_date\": \"2026-05-12T14:30:00.000000Z\",\n    \"product_id\": 42,\n    \"sku\": \"WIDGET-001\",\n    \"barcode\": 123456789012,\n    \"product_name\": \"Blue Widget\",\n    \"stock_uom_code\": \"EA\",\n    \"type\": {\n      \"name\": \"purchase_receipt\",\n      \"link_type\": \"App\\\\Models\\\\PurchaseOrderShipmentReceiptLine\",\n      \"link_id\": 5121,\n      \"parent_link_id\": null,\n      \"detail_link_id\": null,\n      \"manufacturing_order\": null\n    },\n    \"inventory_status\": \"active\",\n    \"quantity\": 100,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"layer\": {\n      \"fifo_layer_id\": 3311,\n      \"name\": \"PO-1042\"\n    },\n    \"fifo_layer_id\": 3311,\n    \"reference\": \"PO-1042\",\n    \"created_at\": \"2026-05-12T14:30:05.000000Z\",\n    \"updated_at\": \"2026-05-12T14:30:05.000000Z\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Get Inventory Movement Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-movements/{inventoryMovement}/activity-log","host":["{{baseUrl}}"],"path":["api","inventory-movements",":inventoryMovement","activity-log"],"variable":[{"key":"inventoryMovement","value":"18234","description":"Inventory movement ID"}]},"description":"Get the paginated change history (activity log) for an inventory movement, newest first. Each entry records what changed (`properties` holds the new and old attribute values plus the change source), the event type, and who made the change (`causer_name`).\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFilters:\n- `filter[search]` — matches against the causing user's name, the event, the changed properties, and the subject type.\n- `filter[event]` — exact event match: created, updated, deleted, bulk_upsert.\n- `filter[source]` — matches the change source recorded in properties (e.g. manual, amazon_sync, inventory_allocation).\n- `filter[batch_uuid]` — all entries written by one bulk operation.\n\nPagination: `page` and `per_page` (default 15).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-movements/{inventoryMovement}/activity-log","host":["{{baseUrl}}"],"path":["api","inventory-movements",":inventoryMovement","activity-log"],"variable":[{"key":"inventoryMovement","value":"18234","description":"Inventory movement ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 90211,\n      \"description\": \"updated\",\n      \"event\": \"updated\",\n      \"subject_type\": \"InventoryMovement\",\n      \"subject_id\": 18234,\n      \"properties\": {\n        \"attributes\": {\n          \"quantity\": 100\n        },\n        \"old\": {\n          \"quantity\": 90\n        },\n        \"source\": \"manual\"\n      },\n      \"causer_name\": \"Jane Smith\",\n      \"created_at\": \"2026-05-12T15:02:11.000000Z\"\n    },\n    {\n      \"id\": 90180,\n      \"description\": \"created\",\n      \"event\": \"created\",\n      \"subject_type\": \"InventoryMovement\",\n      \"subject_id\": 18234,\n      \"properties\": {\n        \"attributes\": {\n          \"quantity\": 90,\n          \"inventory_status\": \"active\",\n          \"warehouse_id\": 1\n        },\n        \"source\": \"manual\"\n      },\n      \"causer_name\": \"Jane Smith\",\n      \"created_at\": \"2026-05-12T14:30:05.000000Z\"\n    }\n  ],\n  \"first_page_url\": \"https://sku3.test/api/inventory-movements/18234/activity-log?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://sku3.test/api/inventory-movements/18234/activity-log?page=1\",\n  \"links\": [\n    {\n      \"url\": null,\n      \"label\": \"&laquo; Previous\",\n      \"active\": false\n    },\n    {\n      \"url\": \"https://sku3.test/api/inventory-movements/18234/activity-log?page=1\",\n      \"label\": \"1\",\n      \"active\": true\n    },\n    {\n      \"url\": null,\n      \"label\": \"Next &raquo;\",\n      \"active\": false\n    }\n  ],\n  \"next_page_url\": null,\n  \"path\": \"https://sku3.test/api/inventory-movements/18234/activity-log\",\n  \"per_page\": 15,\n  \"prev_page_url\": null,\n  \"to\": 2,\n  \"total\": 2\n}"}]},{"name":"Create Forecast Configuration","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations"]},"description":"Creates a new forecast configuration.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Required fields:**\n- `name` (string, max 255, unique among non-deleted configurations)\n- `supplier_ids` (array of integers, min 1, must exist in suppliers table)\n- `forecast_type` (enum: `with_sales_history`, `target_stock_level`, `fill_backorders`)\n- `target_stock_days` (integer 0-365) — required for `with_sales_history`. Optional for `target_stock_level` (allows 0 when `use_min_stock_level_as_target` is true) and `fill_backorders` (ignored)\n\n**Optional fields:**\n- `description` (string, max 1000)\n- `target_quantity` (integer, min 0) — fixed target quantity used by `target_stock_level` forecast type\n- `use_min_stock_level_as_target` (boolean) — when true, `target_stock_level` forecasts use each product's minimum stock level as the target instead of a fixed `target_quantity`. Persisted on both create and update.\n- `sales_history_days` (integer 1-365) — used by `with_sales_history` forecast type\n- `sales_start_date` / `sales_end_date` (Y-m-d) — alternative date range for sales history\n- `use_leadtime` (boolean)\n- `use_moq` (boolean)\n- `enforce_minimum_order_value` (boolean) — enforces supplier minimum order value when generating POs\n- `enforce_minimum_order_quantity` (boolean) — enforces supplier minimum order quantity when generating POs\n- `use_case_pack` (boolean) — round order quantities to supplier case-pack multiples\n- `case_pack_rounding` (enum: `always_round_up`, `always_round_down`, `round_nearest`) — how to round to the case-pack multiple\n- `ignore_min_stock_level` (boolean) — skip the per-product minimum stock level constraint when computing demand\n- `rounding_method` (enum: `round_half_up`, `round_half_down`, `always_round_up`, `always_round_down`, `no_rounding`)\n- `destination_warehouse_id` (integer, must exist in warehouses table)\n- `pricing_tier_id` (integer, must exist in supplier_pricing_tiers table)\n- `product_filters` (array) — filter criteria applied to candidate products\n- `sales_filters` (array) — filter criteria applied to sales history\n- `demand_modifiers` (array) — adjustments to projected demand\n\nAuthentication: Requires Bearer token.\n\n**Baseline decomposition fields (persist onto the saved configuration and drive scheduled runs):**\n- `baseline_method` (enum: `flat` default, `recency_weighted`, `damped_trend`) — how the daily velocity baseline is derived from sales history.\n- `trend_damping_factor` (number 0..1, default 0.85) — damping factor phi for `damped_trend`; lower = more damping.\n- `trend_max_growth_pct` (number >= 0, default 100) — caps how far the trend may lift the baseline above the flat mean (100 = may at most double it). When the cap binds a line reports `trend_capped: true`.\n- `trend_half_life_days` (integer >= 1, nullable, default 21) — reserved tuning for `recency_weighted`.\n- `exclude_detected_anomalies` (boolean, default false) — when true, days resolved as excluded or smoothed anomalies are dropped/replaced before the baseline is computed, de-contaminating promo spikes and stockout gaps.\n\nThese persist onto the configuration, so a schedule attached to it re-applies the same trend and anomaly-cleansing settings on every run. Baseline settings are ignored for `fill_backorders`.\n\n`is_default` is always false on the returned configuration — a newly created configuration is nobody's default until it is set via `POST /configurations/{configuration}/set-default`.","body":{"mode":"raw","raw":"{\n  \"name\": \"Min Stock Replenishment — Main Warehouse\",\n  \"description\": \"Target stock level forecast using each product's minimum stock level as the target quantity.\",\n  \"supplier_ids\": [\n    5,\n    12\n  ],\n  \"forecast_type\": \"target_stock_level\",\n  \"target_quantity\": null,\n  \"use_min_stock_level_as_target\": true,\n  \"sales_history_days\": null,\n  \"sales_start_date\": null,\n  \"sales_end_date\": null,\n  \"target_stock_days\": 0,\n  \"use_leadtime\": true,\n  \"use_measured_lead_time\": false,\n  \"use_moq\": true,\n  \"enforce_minimum_order_value\": false,\n  \"enforce_minimum_order_quantity\": false,\n  \"use_case_pack\": true,\n  \"case_pack_rounding\": \"always_round_up\",\n  \"ignore_min_stock_level\": false,\n  \"rounding_method\": \"always_round_up\",\n  \"destination_warehouse_id\": 1,\n  \"pricing_tier_id\": null,\n  \"product_filters\": [],\n  \"sales_filters\": [],\n  \"demand_modifiers\": [],\n  \"baseline_method\": \"damped_trend\",\n  \"trend_damping_factor\": 0.85,\n  \"trend_max_growth_pct\": 100,\n  \"trend_half_life_days\": 21,\n  \"exclude_detected_anomalies\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"name\": \"Weekly Sales History — Main Warehouse\",\n    \"description\": \"Standard 90-day sales history forecast for our primary warehouse.\",\n    \"supplier_ids\": [\n      5,\n      12\n    ],\n    \"forecast_type\": \"sales_history\",\n    \"target_quantity\": null,\n    \"use_min_stock_level_as_target\": false,\n    \"sales_history_days\": 90,\n    \"sales_start_date\": null,\n    \"sales_end_date\": null,\n    \"target_stock_days\": 30,\n    \"use_leadtime\": true,\n    \"use_measured_lead_time\": false,\n    \"use_moq\": true,\n    \"enforce_minimum_order_value\": false,\n    \"enforce_minimum_order_quantity\": false,\n    \"use_case_pack\": false,\n    \"case_pack_rounding\": null,\n    \"ignore_min_stock_level\": false,\n    \"rounding_method\": \"round_half_up\",\n    \"destination_warehouse_id\": 1,\n    \"pricing_tier_id\": null,\n    \"product_filters\": [],\n    \"sales_filters\": [],\n    \"demand_modifiers\": [],\n    \"created_by\": {\n      \"id\": 7,\n      \"name\": \"Jane Doe\"\n    },\n    \"is_default\": false,\n    \"created_at\": \"2024-01-15T10:30:00Z\",\n    \"updated_at\": \"2024-01-15T10:30:00Z\"\n  }\n}"}]},{"name":"List Forecast Configurations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/paginated?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations","paginated"],"query":[{"key":"page","value":"1","description":"Page number. Defaults to 1.","disabled":true},{"key":"per_page","value":"10","description":"Items per page. Defaults to 10.","disabled":true}]},"description":"Returns paginated forecast configurations.\nAuthentication: Requires Bearer token.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach configuration carries an `is_default` boolean: true when it is the **calling user's** default. The default is per user and per tenant, so the same configuration can be `is_default: true` for one user and false for another."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/paginated?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations","paginated"],"query":[{"key":"page","value":"1","description":"Page number. Defaults to 1.","disabled":true},{"key":"per_page","value":"10","description":"Items per page. Defaults to 10.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Weekly Sales History — Main Warehouse\",\n      \"description\": \"Standard 90-day sales history forecast.\",\n      \"supplier_ids\": [\n        5,\n        12\n      ],\n      \"forecast_type\": \"sales_history\",\n      \"target_quantity\": null,\n      \"use_min_stock_level_as_target\": false,\n      \"sales_history_days\": 90,\n      \"sales_start_date\": null,\n      \"sales_end_date\": null,\n      \"target_stock_days\": 30,\n      \"use_leadtime\": true,\n      \"use_measured_lead_time\": false,\n      \"use_moq\": true,\n      \"enforce_minimum_order_value\": false,\n      \"enforce_minimum_order_quantity\": false,\n      \"use_case_pack\": false,\n      \"case_pack_rounding\": null,\n      \"ignore_min_stock_level\": false,\n      \"rounding_method\": \"round_half_up\",\n      \"destination_warehouse_id\": 1,\n      \"pricing_tier_id\": null,\n      \"product_filters\": [],\n      \"sales_filters\": [],\n      \"demand_modifiers\": [],\n      \"schedules_count\": 2,\n      \"is_default\": true,\n      \"created_at\": \"2024-01-15T10:30:00Z\",\n      \"updated_at\": \"2024-01-15T10:30:00Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 10,\n  \"total\": 25,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"Get Forecast Configuration","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/{configuration}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations",":configuration"],"variable":[{"key":"configuration","value":"1","description":"Forecast configuration ID."}]},"description":"Returns a single forecast configuration with `created_by`, `updated_by`, and `pricing_tier` relationships eager-loaded, plus the `schedules_count` of automation schedules referencing it.\nAuthentication: Requires Bearer token.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach configuration carries an `is_default` boolean: true when it is the **calling user's** default. The default is per user and per tenant, so the same configuration can be `is_default: true` for one user and false for another."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/{configuration}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations",":configuration"],"variable":[{"key":"configuration","value":"1","description":"Forecast configuration ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Weekly Sales History — Main Warehouse\",\n    \"description\": \"Standard 90-day sales history forecast.\",\n    \"supplier_ids\": [\n      5,\n      12\n    ],\n    \"forecast_type\": \"sales_history\",\n    \"target_quantity\": null,\n    \"use_min_stock_level_as_target\": false,\n    \"sales_history_days\": 90,\n    \"sales_start_date\": null,\n    \"sales_end_date\": null,\n    \"target_stock_days\": 30,\n    \"use_leadtime\": true,\n    \"use_measured_lead_time\": false,\n    \"use_moq\": true,\n    \"enforce_minimum_order_value\": true,\n    \"enforce_minimum_order_quantity\": true,\n    \"use_case_pack\": false,\n    \"case_pack_rounding\": null,\n    \"ignore_min_stock_level\": false,\n    \"rounding_method\": \"round_half_up\",\n    \"destination_warehouse_id\": 1,\n    \"pricing_tier_id\": null,\n    \"product_filters\": [],\n    \"sales_filters\": [],\n    \"demand_modifiers\": [],\n    \"created_by\": {\n      \"id\": 7,\n      \"name\": \"Jane Doe\"\n    },\n    \"updated_by\": {\n      \"id\": 7,\n      \"name\": \"Jane Doe\"\n    },\n    \"schedules_count\": 2,\n    \"is_default\": true,\n    \"created_at\": \"2024-01-15T10:30:00Z\",\n    \"updated_at\": \"2024-01-15T10:30:00Z\"\n  }\n}"}]},{"name":"Update Forecast Configuration","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/{configuration}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations",":configuration"],"variable":[{"key":"configuration","value":"1","description":"Forecast configuration ID."}]},"description":"Updates an existing forecast configuration. All fields use `sometimes` validation — only send the fields you want to change.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe controller merges the incoming payload over the existing configuration data via `ForecastConfigurationData::fromModel($configuration)`, so omitted fields retain their current values. **Any field you want to change must be present in the validation rules — otherwise `validated()` strips it and the merge silently reverts it to the stored value.**\n\nAccepts the same fields as Create, including `use_min_stock_level_as_target`, `use_case_pack`, `case_pack_rounding`, and `ignore_min_stock_level`. The `name` uniqueness rule excludes the current record (so unchanged names pass validation).\n\nAuthentication: Requires Bearer token.\n\n**Baseline decomposition fields** (`baseline_method`, `trend_damping_factor`, `trend_max_growth_pct`, `trend_half_life_days`, `exclude_detected_anomalies`) persist onto the configuration and can be changed here; the new values apply to the next on-demand build and to any schedule attached to this configuration. `baseline_method` is `flat` (default), `recency_weighted`, or `damped_trend`; `trend_damping_factor` (0..1, default 0.85) is the damping factor phi (lower = more damping); `trend_max_growth_pct` (>= 0, default 100) caps the trend lift above the flat mean; `trend_half_life_days` (>= 1, nullable, default 21) tunes `recency_weighted`; `exclude_detected_anomalies` (default false) drops/replaces resolved anomaly days before the baseline is computed. Baseline settings are ignored for `fill_backorders`.\n\nEach configuration carries an `is_default` boolean: true when it is the **calling user's** default. The default is per user and per tenant, so the same configuration can be `is_default: true` for one user and false for another.","body":{"mode":"raw","raw":"{\n  \"name\": \"Min Stock Replenishment — Main Warehouse (v2)\",\n  \"forecast_type\": \"target_stock_level\",\n  \"use_min_stock_level_as_target\": true,\n  \"use_case_pack\": true,\n  \"case_pack_rounding\": \"always_round_up\",\n  \"ignore_min_stock_level\": false,\n  \"baseline_method\": \"damped_trend\",\n  \"trend_damping_factor\": 0.8,\n  \"trend_max_growth_pct\": 75,\n  \"trend_half_life_days\": 28,\n  \"exclude_detected_anomalies\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/{configuration}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations",":configuration"],"variable":[{"key":"configuration","value":"1","description":"Forecast configuration ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Weekly Sales History — Main Warehouse (v2)\",\n    \"description\": \"Updated to enforce supplier minimums.\",\n    \"supplier_ids\": [\n      5,\n      12\n    ],\n    \"forecast_type\": \"sales_history\",\n    \"target_quantity\": null,\n    \"use_min_stock_level_as_target\": false,\n    \"sales_history_days\": 90,\n    \"sales_start_date\": null,\n    \"sales_end_date\": null,\n    \"target_stock_days\": 45,\n    \"use_leadtime\": true,\n    \"use_measured_lead_time\": false,\n    \"use_moq\": true,\n    \"enforce_minimum_order_value\": true,\n    \"enforce_minimum_order_quantity\": true,\n    \"use_case_pack\": false,\n    \"case_pack_rounding\": null,\n    \"ignore_min_stock_level\": false,\n    \"rounding_method\": \"round_half_up\",\n    \"destination_warehouse_id\": 1,\n    \"pricing_tier_id\": null,\n    \"product_filters\": [],\n    \"sales_filters\": [],\n    \"demand_modifiers\": [],\n    \"created_by\": {\n      \"id\": 7,\n      \"name\": \"Jane Doe\"\n    },\n    \"updated_by\": {\n      \"id\": 7,\n      \"name\": \"Jane Doe\"\n    },\n    \"is_default\": true,\n    \"created_at\": \"2024-01-15T10:30:00Z\",\n    \"updated_at\": \"2024-01-20T14:22:00Z\"\n  }\n}"}]},{"name":"Duplicate Forecast Configuration","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/{configuration}/duplicate","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations",":configuration","duplicate"],"variable":[{"key":"configuration","value":"1","description":"Forecast configuration ID to duplicate."}]},"description":"Creates a copy of an existing forecast configuration.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Optional body field:**\n- `name` (string, max 255) — name for the new copy. If omitted, defaults to `\"<Original Name> (Copy)\"`.\n\nAuthentication: Requires Bearer token.\n\n`is_default` is always false on the returned configuration — a newly created configuration is nobody's default until it is set via `POST /configurations/{configuration}/set-default`.","body":{"mode":"raw","raw":"{\n  \"name\": \"Weekly Sales History — Copy\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/{configuration}/duplicate","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations",":configuration","duplicate"],"variable":[{"key":"configuration","value":"1","description":"Forecast configuration ID to duplicate."}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 43,\n    \"name\": \"Weekly Sales History — Copy\",\n    \"description\": \"Standard 90-day sales history forecast.\",\n    \"supplier_ids\": [\n      5,\n      12\n    ],\n    \"forecast_type\": \"sales_history\",\n    \"target_quantity\": null,\n    \"use_min_stock_level_as_target\": false,\n    \"sales_history_days\": 90,\n    \"sales_start_date\": null,\n    \"sales_end_date\": null,\n    \"target_stock_days\": 30,\n    \"use_leadtime\": true,\n    \"use_measured_lead_time\": false,\n    \"use_moq\": true,\n    \"enforce_minimum_order_value\": false,\n    \"enforce_minimum_order_quantity\": false,\n    \"use_case_pack\": false,\n    \"case_pack_rounding\": null,\n    \"ignore_min_stock_level\": false,\n    \"rounding_method\": \"round_half_up\",\n    \"destination_warehouse_id\": 1,\n    \"pricing_tier_id\": null,\n    \"product_filters\": [],\n    \"sales_filters\": [],\n    \"demand_modifiers\": [],\n    \"created_by\": {\n      \"id\": 7,\n      \"name\": \"Jane Doe\"\n    },\n    \"is_default\": false,\n    \"created_at\": \"2024-01-20T14:22:00Z\",\n    \"updated_at\": \"2024-01-20T14:22:00Z\"\n  }\n}"}]},{"name":"Get Fulfillment Debt Report","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory/fulfillment-debt-report?page=1&per_page=25","host":["{{baseUrl}}"],"path":["api","inventory","fulfillment-debt-report"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"per_page","value":"25","description":"Items per page","disabled":true}]},"description":"Returns a paginated list of fulfillment debt lines — sales order lines the sales channel has already fulfilled that could not be recorded against stock. Includes both outstanding lines (units still owed with nothing reserved) and covered lines (debt fully netted by planned / awaiting-receipt / allocated inventory, which settles automatically when that stock lands). Lines are returned newest first.\nAuthentication: Requires Bearer token.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFilters:\n- filter[coverage]: 'outstanding' (net debt remains) or 'covered' (debt fully reserved against incoming inventory). Omit for both.\n- filter[order_number], filter[order_status], filter[fulfillment_status], filter[warehouse_name]: text filters.\n- filter[order_date.from] / filter[order_date.to]: date range.\n- filter[line_quantity], filter[processable_quantity], filter[fulfilled_quantity], filter[reserved_quantity], filter[debt_quantity]: numeric filters.\n- filter[search]: searches SKU, product name, and order number.\n\nWhy a line is here:\n- unrecorded_reason: `unmapped_product` (the channel SKU matches no product), `identity_tracked` (serial/lot stock), `pool_unavailable` (stock held by another commitment), or `failed` (the sync could not complete — see unrecorded_detail).\n- unrecorded_reason_label / unrecorded_remedy: display text for the above. An unmapped SKU needs no action and may stay in this state indefinitely.\n- unrecorded_quantity: units the channel shipped that could not be recorded.\n- is_settleable: false when the line names no product or warehouse. Those lines are real debt and are listed here, but a catch-up stock take cannot include them — there is nothing to adjust."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory/fulfillment-debt-report?page=1&per_page=25","host":["{{baseUrl}}"],"path":["api","inventory","fulfillment-debt-report"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"per_page","value":"25","description":"Items per page","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 500,\n      \"sales_order_id\": 12,\n      \"order_number\": \"SO-2024-0042\",\n      \"order_date\": \"2024-01-10T00:00:00Z\",\n      \"order_status\": \"open\",\n      \"fulfillment_status\": \"partial\",\n      \"integration_instance_name\": \"Shopify Store\",\n      \"product_id\": 42,\n      \"sku\": \"WIDGET-001\",\n      \"product_title\": \"Blue Widget\",\n      \"warehouse_id\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"line_quantity\": 10,\n      \"canceled_quantity\": 0,\n      \"externally_fulfilled_quantity\": 0,\n      \"processable_quantity\": 10,\n      \"fulfilled_quantity\": 6,\n      \"allocated_quantity\": 4,\n      \"reserved_quantity\": 4,\n      \"debt_quantity\": 4,\n      \"created_at\": \"2024-01-10T08:00:00Z\",\n      \"gross_debt_quantity\": 4,\n      \"is_covered\": false,\n      \"covering_statuses\": [],\n      \"unrecorded_quantity\": 4,\n      \"unrecorded_reason\": \"pool_unavailable\",\n      \"unrecorded_reason_label\": \"Stock held by another commitment\",\n      \"unrecorded_remedy\": \"Free the commitment holding the stock, or receive more, then re-run the sync.\",\n      \"unrecorded_detail\": null,\n      \"is_settleable\": true\n    },\n    {\n      \"id\": 501,\n      \"sales_order_id\": 12,\n      \"order_number\": \"SO-2024-0042\",\n      \"order_date\": \"2024-01-10T00:00:00Z\",\n      \"order_status\": \"open\",\n      \"fulfillment_status\": \"partial\",\n      \"integration_instance_name\": \"Shopify Store\",\n      \"product_id\": null,\n      \"sku\": null,\n      \"product_title\": null,\n      \"warehouse_id\": null,\n      \"warehouse_name\": null,\n      \"line_quantity\": 10,\n      \"canceled_quantity\": 0,\n      \"externally_fulfilled_quantity\": 0,\n      \"processable_quantity\": 10,\n      \"fulfilled_quantity\": 6,\n      \"allocated_quantity\": 4,\n      \"reserved_quantity\": 4,\n      \"debt_quantity\": 0,\n      \"created_at\": \"2024-01-10T08:00:00Z\",\n      \"gross_debt_quantity\": 1,\n      \"is_covered\": true,\n      \"covering_statuses\": [\n        \"awaiting_receipt\"\n      ],\n      \"unrecorded_quantity\": 2,\n      \"unrecorded_reason\": \"unmapped_product\",\n      \"unrecorded_reason_label\": \"Unmapped product\",\n      \"unrecorded_remedy\": \"Nothing required. Map the channel SKU to a product if you want these units on the ledger; leaving it unmapped keeps the shipment recorded here and adjusts no stock.\",\n      \"unrecorded_detail\": null,\n      \"is_settleable\": false\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 2,\n  \"per_page\": 25,\n  \"total\": 2\n}"}]},{"name":"Get Fulfillment Debt Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory/fulfillment-debt-report/summary","host":["{{baseUrl}}"],"path":["api","inventory","fulfillment-debt-report","summary"]},"description":"Returns aggregated summary totals for the fulfillment debt report header cards. The headline totals count OUTSTANDING debt only; covered_lines counts the additional lines whose debt is fully reserved against incoming inventory and will settle automatically.\nAuthentication: Requires Bearer token.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe summary reports BOTH halves of the debt. `total_debt_*` are the legacy sales-channel lines; `claims` is the inventory-debt ledger; `combined_debt_lines` and `combined_debt_quantity` are the two added together. `clearing_balance` is money (posted minus relieved), not quantity x estimate: the two diverge once a claim settles in slices, and it is the money figure that has to agree with the Inventory Debt Clearing account. `claims.has_any_claims` says whether this tenant has ever raised a claim in any status, which is distinct from having one outstanding now. Note that not every `combined_*` field is a sum: `combined_debt_lines` and `combined_debt_quantity` add the two halves, but `combined_affected_products` and `combined_affected_warehouses` are a DISTINCT count across both halves, because a product can owe on a claim and on a channel line at the same time and is one affected product either way. Do not add the per-half `affected_products` values together, and do not take the larger of them — both answers are wrong."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory/fulfillment-debt-report/summary","host":["{{baseUrl}}"],"path":["api","inventory","fulfillment-debt-report","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_debt_lines\": 38,\n    \"total_debt_quantity\": 142,\n    \"affected_products\": 25,\n    \"affected_warehouses\": 3,\n    \"covered_lines\": 14,\n    \"claims\": {\n      \"has_any_claims\": true,\n      \"outstanding_claims\": 6,\n      \"outstanding_units\": 21.5,\n      \"affected_products\": 5,\n      \"affected_warehouses\": 2,\n      \"clearing_posted\": 742.15,\n      \"clearing_relieved\": 180.4,\n      \"clearing_balance\": 561.75,\n      \"oldest_age_days\": 34,\n      \"aged_over_30\": 1,\n      \"settled_units_this_month\": 9,\n      \"variance_this_month\": -12.6\n    },\n    \"combined_debt_quantity\": 163.5,\n    \"combined_debt_lines\": 44,\n    \"combined_affected_products\": 26,\n    \"combined_affected_warehouses\": 3,\n    \"clearing_balance\": 561.75,\n    \"oldest_age_days\": 34,\n    \"aged_over_30\": 1\n  }\n}"}]},{"name":"Create Catch-Up Stock Takes","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory/fulfillment-debt-report/create-stock-take","host":["{{baseUrl}}"],"path":["api","inventory","fulfillment-debt-report","create-stock-take"]},"description":"Creates adjustment stock takes in DRAFT status to catch up fulfillment debt. Only OUTSTANDING debt lines are included — lines whose debt is already covered by planned / awaiting-receipt / allocated inventory settle automatically when that stock lands and are excluded to avoid double-counting.\nAuthentication: Requires Bearer token.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields:\n- warehouse_id (nullable): Specific warehouse to create stock takes for. If omitted, creates for all warehouses.\n- sales_order_line_ids (nullable array): Restrict to a selected subset of debt line IDs.","body":{"mode":"raw","raw":"{\n  \"warehouse_id\": 1\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory/fulfillment-debt-report/create-stock-take","host":["{{baseUrl}}"],"path":["api","inventory","fulfillment-debt-report","create-stock-take"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"stock_takes\": [\n      {\n        \"id\": 55,\n        \"warehouse_id\": 1,\n        \"warehouse_name\": \"Main Warehouse\",\n        \"item_count\": 12,\n        \"status\": \"draft\"\n      }\n    ],\n    \"message\": \"1 adjustment stock take(s) created in DRAFT status. Review and finalize to apply.\"\n  }\n}"}]},{"name":"List Inventory Holds","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory/holds?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default: 10)","disabled":true}]},"description":"Returns paginated inventory holds (reservations). By default shows active holds (not released, not expired).\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nStatus filter (resolved from filter_groups, filter[status], or top-level status param):\n- active: Not released and not expired (default)\n- partially_released: Has released_quantity > 0 but not fully released yet\n- released: Fully released holds\n- expired: Expired holds\n- expiring_soon: Expiring within the next 7 days\n- all: All holds regardless of status\n\nAvailable sorts: id, quantity, released_quantity, created_at, expires_at, released_at (default: -created_at)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory/holds?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default: 10)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"product_id\": 42,\n      \"product\": {\n        \"id\": 42,\n        \"sku\": \"WIDGET-001\",\n        \"name\": \"Premium Widget\"\n      },\n      \"warehouse_id\": 1,\n      \"warehouse\": {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      },\n      \"quantity\": 50,\n      \"released_quantity\": 0,\n      \"reason\": \"quality_hold\",\n      \"reason_label\": \"Quality Hold\",\n      \"notes\": \"Pending QA inspection\",\n      \"expires_at\": \"2024-02-01T00:00:00.000000Z\",\n      \"released_at\": null,\n      \"released_by\": null,\n      \"release_reason\": null,\n      \"created_by\": {\n        \"id\": 5,\n        \"name\": \"Jane Smith\"\n      },\n      \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2024-01-15T10:00:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Create Inventory Hold","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory/holds","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds"]},"description":"Creates a single inventory hold (reservation) for a product in a specific warehouse. Deducts from available inventory.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- product_id (required, integer): Product to hold\n- warehouse_id (required, integer): Warehouse where inventory is held\n- quantity (required, numeric, min 0.0001): Quantity to hold\n- reason (required, string, max 255): Reason for the hold\n- notes (optional, string): Additional notes\n- expires_at (optional, date, must be future): Expiration date for the hold\n\n**Requires permission:** `inventory.adjust`\n\n`notes` is **required** when `reason` is `other`; optional otherwise.","body":{"mode":"raw","raw":"{\n  \"product_id\": 42,\n  \"warehouse_id\": 1,\n  \"quantity\": 50,\n  \"reason\": \"quality_hold\",\n  \"notes\": \"Pending QA inspection\",\n  \"expires_at\": \"2024-02-01T00:00:00Z\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory/holds","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"product_id\": 42,\n    \"product\": {\n      \"id\": 42,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Premium Widget\"\n    },\n    \"warehouse_id\": 1,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"quantity\": 50,\n    \"released_quantity\": 0,\n    \"reason\": \"quality_hold\",\n    \"reason_label\": \"Quality Hold\",\n    \"notes\": \"Pending QA inspection\",\n    \"expires_at\": \"2024-02-01T00:00:00.000000Z\",\n    \"released_at\": null,\n    \"created_by\": {\n      \"id\": 5,\n      \"name\": \"Jane Smith\"\n    },\n    \"created_at\": \"2024-01-20T10:00:00.000000Z\",\n    \"updated_at\": \"2024-01-20T10:00:00.000000Z\"\n  },\n  \"message\": \"Inventory reservation created successfully.\"\n}"}]},{"name":"Bulk Create Inventory Holds","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/bulk","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds","bulk"]},"description":"Creates inventory holds for multiple products in the same warehouse in a single request. Up to 100 items per request.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- warehouse_id (required, integer): Warehouse where all items are held\n- reason (required, string, max 255): Reason applied to all holds\n- notes (optional, string): Notes applied to all holds\n- expires_at (optional, date, must be future): Expiration applied to all holds\n- items (required, array, 1-100): Array of items to hold\n  - items.*.product_id (required, integer): Product ID\n  - items.*.quantity (required, numeric, min 0.0001): Quantity to hold\n\n**Requires permission:** `inventory.adjust`\n\n`notes` is **required** when `reason` is `other`; optional otherwise.","body":{"mode":"raw","raw":"{\n  \"warehouse_id\": 1,\n  \"reason\": \"quality_hold\",\n  \"notes\": \"Batch QA hold\",\n  \"expires_at\": \"2024-03-01T00:00:00Z\",\n  \"items\": [\n    {\n      \"product_id\": 42,\n      \"quantity\": 10\n    },\n    {\n      \"product_id\": 43,\n      \"quantity\": 5\n    },\n    {\n      \"product_id\": 44,\n      \"quantity\": 20\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/bulk","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds","bulk"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 6,\n      \"product_id\": 42,\n      \"warehouse_id\": 1,\n      \"quantity\": 10,\n      \"reason\": \"quality_hold\",\n      \"created_at\": \"2024-01-20T10:00:00.000000Z\"\n    }\n  ],\n  \"errors\": [],\n  \"message\": \"1 reservation(s) created.\"\n}"}]},{"name":"Get Inventory Hold","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/{reservation}","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds",":reservation"],"variable":[{"key":"reservation","value":"1","description":"Inventory hold ID"}]},"description":"Returns a single inventory hold with full product, warehouse, creator, and releaser details.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/{reservation}","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds",":reservation"],"variable":[{"key":"reservation","value":"1","description":"Inventory hold ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"product_id\": 42,\n    \"product\": {\n      \"id\": 42,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Premium Widget\"\n    },\n    \"warehouse_id\": 1,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"quantity\": 30,\n    \"released_quantity\": 20,\n    \"reason\": \"quality_hold\",\n    \"reason_label\": \"Quality Hold\",\n    \"notes\": \"Pending QA inspection\",\n    \"expires_at\": \"2024-02-01T00:00:00.000000Z\",\n    \"released_at\": null,\n    \"released_by\": null,\n    \"release_reason\": null,\n    \"created_by\": {\n      \"id\": 5,\n      \"name\": \"Jane Smith\"\n    },\n    \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2024-01-15T10:00:00.000000Z\"\n  }\n}"}]},{"name":"Update Inventory Hold","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/{reservation}","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds",":reservation"],"variable":[{"key":"reservation","value":"1","description":"Inventory hold ID"}]},"description":"Updates an inventory hold's quantity, notes, or expiration date.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- quantity (sometimes, numeric, min 0.0001): New quantity\n- notes (nullable, string): Updated notes\n- expires_at (nullable, date, must be future): New expiration date\n\n**Requires permission:** `inventory.adjust`","body":{"mode":"raw","raw":"{\n  \"quantity\": 30,\n  \"notes\": \"Updated notes\",\n  \"expires_at\": \"2024-03-01T00:00:00Z\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/{reservation}","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds",":reservation"],"variable":[{"key":"reservation","value":"1","description":"Inventory hold ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"product_id\": 42,\n    \"quantity\": 30,\n    \"reason\": \"quality_hold\",\n    \"notes\": \"Updated notes\"\n  },\n  \"message\": \"Inventory reservation updated successfully.\"\n}"}]},{"name":"Delete Inventory Hold","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/{reservation}","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds",":reservation"],"variable":[{"key":"reservation","value":"1","description":"Inventory hold ID"}]},"description":"Permanently deletes an inventory hold and returns the quantity to available inventory.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.adjust`"},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/{reservation}","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds",":reservation"],"variable":[{"key":"reservation","value":"1","description":"Inventory hold ID"}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":""}]},{"name":"Release Inventory Hold","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/{reservation}/release","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds",":reservation","release"],"variable":[{"key":"reservation","value":"1","description":"Inventory hold ID"}]},"description":"Releases inventory from a hold. Supports both partial and full release.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nPartial release (quantity provided AND less than current held quantity):\n- The hold's `quantity` is reduced by the released amount\n- `released_quantity` accumulates the cumulative total released across all release events\n- `released_at` remains null — the hold is still active\n\nFull release (quantity omitted, null, 0, or equal to current held quantity):\n- `released_at`, `released_by`, and `release_reason` are populated\n- The remaining `quantity` is added to `released_quantity` so it reflects the cumulative total released\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- quantity (nullable, numeric, min 0.0001, max=current held quantity): Quantity to release. Omit, send null, or send 0 to fully release. Cannot exceed the current held quantity (returns 422)\n- release_reason (nullable, string, max 500): Reason for the release\n\n**Requires permission:** `inventory.adjust`","body":{"mode":"raw","raw":"{\n  \"quantity\": 20,\n  \"release_reason\": \"QA inspection passed for partial batch\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/{reservation}/release","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds",":reservation","release"],"variable":[{"key":"reservation","value":"1","description":"Inventory hold ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"quantity\": 30,\n    \"released_quantity\": 20,\n    \"released_at\": null,\n    \"released_by\": null,\n    \"release_reason\": \"QA passed for partial batch\"\n  },\n  \"message\": \"Inventory reservation released successfully.\"\n}"}]},{"name":"Bulk Release Inventory Holds","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/bulk-release","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds","bulk-release"]},"description":"Releases multiple inventory holds at once.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- ids (required, array): Array of inventory hold IDs to release\n- ids.* (integer): Must exist in inventory_holds table\n- release_reason (nullable, string, max 500): Reason applied to all released holds\n\n**Requires permission:** `inventory.adjust`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ],\n  \"release_reason\": \"Batch QA pass\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/bulk-release","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds","bulk-release"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 hold(s) released successfully.\",\n  \"released\": 3,\n  \"skipped\": 0\n}"}]},{"name":"Bulk Delete Inventory Holds","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/bulk-delete","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds","bulk-delete"]},"description":"Permanently deletes multiple inventory holds.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- ids (required, array): Array of inventory hold IDs to delete\n- ids.* (integer): Must exist in inventory_holds table\n\n**Requires permission:** `inventory.adjust`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    4,\n    5,\n    6\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/bulk-delete","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds","bulk-delete"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 hold(s) deleted successfully.\",\n  \"deleted\": 2\n}"}]},{"name":"Export Inventory Holds","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/export?format=xlsx&scope=filtered","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds","export"],"query":[{"key":"format","value":"xlsx","description":"Export format: xlsx or csv (default: xlsx)","disabled":true},{"key":"scope","value":"filtered","description":"Export scope: filtered (default) or selected","disabled":true}]},"description":"Exports inventory holds as XLSX or CSV file. Columns include: ID, SKU, Product Name, Warehouse, Quantity, Reason, Notes, Created By, Created At, Expires At, Released At, Archived At.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nResponse: File download (binary), not JSON.\n\n**Requires permission:** `inventory.export`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/export?format=xlsx&scope=filtered","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds","export"],"query":[{"key":"format","value":"xlsx","description":"Export format: xlsx or csv (default: xlsx)","disabled":true},{"key":"scope","value":"filtered","description":"Export scope: filtered (default) or selected","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"[Binary XLSX file download]"}]},{"name":"Get Hold Reasons","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/reasons","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds","reasons"]},"description":"Returns all distinct non-null reasons used across inventory holds, sorted alphabetically. Useful for populating filter dropdowns.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/reasons","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds","reasons"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    \"damaged\",\n    \"other\",\n    \"quality_hold\",\n    \"reserved_for_event\"\n  ]\n}"}]},{"name":"Get Holds Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/summary","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds","summary"]},"description":"Returns aggregated summary statistics for inventory holds.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/summary","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_active\": 24,\n    \"total_quantity_held\": 1250,\n    \"expiring_soon\": 3,\n    \"expired\": 1\n  }\n}"}]},{"name":"Check Warehouse Availability","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/warehouse-availability","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds","warehouse-availability"]},"description":"Returns the available inventory for a batch of products in a specific warehouse. Used by the Reserve Stock modal browse drawer.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- product_ids (required, array, max 100): Product IDs to check availability for\n- product_ids.* (integer): Product ID\n- warehouse_id (required, integer): Warehouse to check availability in","body":{"mode":"raw","raw":"{\n  \"product_ids\": [\n    42,\n    43,\n    44\n  ],\n  \"warehouse_id\": 1\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory/holds/warehouse-availability","host":["{{baseUrl}}"],"path":["api","v2","inventory","holds","warehouse-availability"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"42\": {\n      \"available\": 150\n    },\n    \"43\": {\n      \"available\": 0\n    }\n  }\n}"}]},{"name":"List Location Quantities","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory/location-quantities?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","v2","inventory","location-quantities"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default: 10, max: 100)","disabled":true}]},"description":"Paginated per-(product, warehouse, location) on-hand balances from the incrementally-maintained inventory_location_quantities cache (stock by location).\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nScope: inventory (read/write).\nRequires the tenant to have warehouse locations enabled.\n\nFilters (Spatie QueryBuilder `filter[...]`):\n- warehouse_id (exact)\n- warehouse_location_id (exact)\n- product_id (exact)\n- location_type (exact or comma-separated; one of: staging, primary_pick, reserve, storage, shipping, returns, quarantine, damaged)\n- search (product sku/name + location code)\n\nAvailable sorts: id, quantity, product_id, warehouse_id, warehouse_location_id, updated_at (default: -quantity).\nper_page is clamped to a max of 100 (default 10)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory/location-quantities?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","v2","inventory","location-quantities"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default: 10, max: 100)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 501,\n      \"product\": {\n        \"id\": 42,\n        \"sku\": \"WIDGET-001\",\n        \"name\": \"Premium Widget\"\n      },\n      \"warehouse\": {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      },\n      \"location\": {\n        \"id\": 12,\n        \"code\": \"A1-B2-S3-BIN4\",\n        \"name\": \"Aisle 1 Bay 2 Shelf 3 Bin 4\",\n        \"type\": \"storage\",\n        \"type_label\": \"Storage\"\n      },\n      \"quantity\": 42\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Export Location Quantities","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory/location-quantities/export?filter[warehouse_id]=1","host":["{{baseUrl}}"],"path":["api","v2","inventory","location-quantities","export"],"query":[{"key":"filter[warehouse_id]","value":"1","description":"Warehouse ID to export. Required unless filter[product_id] is supplied — the export must be scoped to a warehouse or a product.","disabled":true}]},"description":"Export the filtered per-location balances as a CSV or XLSX file download (columns: SKU, Product Name, Warehouse, Location Code, Location Name, Location Type, Quantity). Applies the same filter pipeline as the index endpoint. Returns a binary file, NOT JSON.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe export must be scoped to a warehouse or a product: supply filter[warehouse_id] or filter[product_id] (either one satisfies the requirement). A request with neither returns 422 Unprocessable Entity.\n\nAuthentication: Requires Bearer token.\nScope: inventory (read/write).\nAdditional permission required: `inventory.export`.\n\nQuery params:\n- filter[warehouse_id] (required unless filter[product_id] given)\n- filter[product_id] (required unless filter[warehouse_id] given)\n- format (optional: csv | xlsx, default csv)\n- plus any other filter[...] accepted by the index endpoint\n\n**Requires permission:** `inventory.export`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory/location-quantities/export?filter[warehouse_id]=1","host":["{{baseUrl}}"],"path":["api","v2","inventory","location-quantities","export"],"query":[{"key":"filter[warehouse_id]","value":"1","description":"Warehouse ID to export. Required unless filter[product_id] is supplied — the export must be scoped to a warehouse or a product.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"[Binary CSV file download: location-quantities-YYYY-MM-DD-HH-mm-ss.csv]"}]},{"name":"List Square Inventory Counts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/square/inventory","host":["{{baseUrl}}"],"path":["api","square","inventory"]},"description":"Paginated list of Square inventory counts across integration instances.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.inventory.index"}},{"name":"Sync Square Inventory","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/square/instances/{squareIntegrationInstance}/inventory/sync","host":["{{baseUrl}}"],"path":["api","square","instances",":squareIntegrationInstance","inventory","sync"],"variable":[{"key":"squareIntegrationInstance","value":"1","description":"Square integration instance ID"}]},"description":"Fetch and upsert Square inventory counts for a specific instance. Queued.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.inventory.sync"}}]},{"name":"Discount Codes","item":[{"name":"List Discount Codes","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/discount-codes","host":["{{baseUrl}}"],"path":["api","discount-codes"]},"description":"List all discount codes. Uses standard list query parameters supporting search, sort, and pagination.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nAllowed sorts: code, type, scope, is_active, created_at, updated_at"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/discount-codes","host":["{{baseUrl}}"],"path":["api","discount-codes"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"code\": \"SUMMER10\",\n      \"description\": \"10% off for summer sale\",\n      \"type\": \"percentage\",\n      \"rate\": 10,\n      \"amount\": null,\n      \"scope\": \"order\",\n      \"apply_before_tax\": false,\n      \"is_active\": true,\n      \"created_at\": \"2024-06-01T00:00:00.000000Z\",\n      \"updated_at\": \"2024-06-01T00:00:00.000000Z\"\n    },\n    {\n      \"id\": 2,\n      \"code\": \"FLAT5\",\n      \"description\": \"$5 off any order\",\n      \"type\": \"fixed_amount\",\n      \"rate\": null,\n      \"amount\": 5,\n      \"scope\": \"order\",\n      \"apply_before_tax\": true,\n      \"is_active\": true,\n      \"created_at\": \"2024-01-01T00:00:00.000000Z\",\n      \"updated_at\": \"2024-01-01T00:00:00.000000Z\"\n    }\n  ],\n  \"total\": 2,\n  \"per_page\": 10,\n  \"current_page\": 1,\n  \"last_page\": 1\n}"}]},{"name":"Create Discount Code","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/discount-codes","host":["{{baseUrl}}"],"path":["api","discount-codes"]},"description":"Create a new discount code.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFields:\n- code (required): Unique discount code string (max 255 chars)\n- description (optional): Description of the discount (max 500 chars)\n- type (required): Discount value type. One of: percentage, fixed_amount\n- rate (optional): Percentage rate (0–100). Used when type is 'percentage'\n- amount (optional): Fixed amount. Used when type is 'fixed_amount'\n- scope (required): What the discount applies to. One of: line, order, both\n- apply_before_tax (optional): Whether discount is applied before tax calculation\n- is_active (optional): Whether the code is active\n\n**Requires permission:** `discount_codes.create`","body":{"mode":"raw","raw":"{\n  \"code\": \"SUMMER10\",\n  \"description\": \"10% off for summer sale\",\n  \"type\": \"percentage\",\n  \"rate\": 10,\n  \"amount\": null,\n  \"scope\": \"order\",\n  \"apply_before_tax\": false,\n  \"is_active\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/discount-codes","host":["{{baseUrl}}"],"path":["api","discount-codes"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"code\": \"SUMMER10\",\n    \"description\": \"10% off for summer sale\",\n    \"type\": \"percentage\",\n    \"rate\": 10,\n    \"amount\": null,\n    \"scope\": \"order\",\n    \"apply_before_tax\": false,\n    \"is_active\": true,\n    \"created_at\": \"2024-06-01T00:00:00.000000Z\",\n    \"updated_at\": \"2024-06-01T00:00:00.000000Z\"\n  },\n  \"message\": \"discount code created successfully.\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Discount Code","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/discount-codes/{discountCode}","host":["{{baseUrl}}"],"path":["api","discount-codes",":discountCode"],"variable":[{"key":"discountCode","value":"","description":"The discountCode identifier"}]},"description":"Retrieve a single discount code by ID.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/discount-codes/{discountCode}","host":["{{baseUrl}}"],"path":["api","discount-codes",":discountCode"],"variable":[{"key":"discountCode","value":"","description":"The discountCode identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"code\": \"SUMMER10\",\n    \"description\": \"10% off for summer sale\",\n    \"type\": \"percentage\",\n    \"rate\": 10,\n    \"amount\": null,\n    \"scope\": \"order\",\n    \"apply_before_tax\": false,\n    \"is_active\": true,\n    \"created_at\": \"2024-06-01T00:00:00.000000Z\",\n    \"updated_at\": \"2024-06-01T00:00:00.000000Z\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Update Discount Code","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/discount-codes/{discountCode}","host":["{{baseUrl}}"],"path":["api","discount-codes",":discountCode"],"variable":[{"key":"discountCode","value":"","description":"The discountCode identifier"}]},"description":"Update an existing discount code.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nSame fields as create. Unique code validation ignores the current record.\n\n**Requires permission:** `discount_codes.update`","body":{"mode":"raw","raw":"{\n  \"code\": \"SUMMER15\",\n  \"description\": \"15% off for summer sale\",\n  \"type\": \"percentage\",\n  \"rate\": 15,\n  \"scope\": \"order\",\n  \"is_active\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/discount-codes/{discountCode}","host":["{{baseUrl}}"],"path":["api","discount-codes",":discountCode"],"variable":[{"key":"discountCode","value":"","description":"The discountCode identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"code\": \"SUMMER15\",\n    \"description\": \"15% off for summer sale\",\n    \"type\": \"percentage\",\n    \"rate\": 15,\n    \"amount\": null,\n    \"scope\": \"order\",\n    \"apply_before_tax\": false,\n    \"is_active\": true,\n    \"created_at\": \"2024-06-01T00:00:00.000000Z\",\n    \"updated_at\": \"2024-06-15T00:00:00.000000Z\"\n  },\n  \"message\": \"discount code SUMMER15 updated successfully.\",\n  \"status\": \"success\"\n}"}]},{"name":"Delete Discount Code","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/discount-codes/{discountCode}","host":["{{baseUrl}}"],"path":["api","discount-codes",":discountCode"],"variable":[{"key":"discountCode","value":"","description":"The discountCode identifier"}]},"description":"Soft-delete a discount code. Soft-deleted codes are excluded from the unique code constraint for future codes with the same name.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `discount_codes.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/discount-codes/{discountCode}","host":["{{baseUrl}}"],"path":["api","discount-codes",":discountCode"],"variable":[{"key":"discountCode","value":"","description":"The discountCode identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"discount code SUMMER10 deleted successfully.\",\n  \"status\": \"success\"\n}"}]}]},{"name":"Custom Fields","item":[{"name":"List Custom Fields","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/custom-fields?page=1&limit=15","host":["{{baseUrl}}"],"path":["api","custom-fields"],"query":[{"key":"page","value":"1","description":"Page number for pagination","disabled":true},{"key":"limit","value":"15","description":"Number of results per page","disabled":true}]},"description":"Returns a paginated list of custom fields. Custom fields can be attached to different entity types (link_type).\n\nAuthentication: Requires Bearer token.\n\nQuery parameters:\n- page — Page number (default: 1)\n- limit — Results per page (default: 15)\n- search — Full-text search by name\n- sort — Sort field, prefix with - for descending\n- archived — 0 = active, 1 = archived (soft-deleted)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/custom-fields?page=1&limit=15","host":["{{baseUrl}}"],"path":["api","custom-fields"],"query":[{"key":"page","value":"1","description":"Page number for pagination","disabled":true},{"key":"limit","value":"15","description":"Number of results per page","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Purchase Order Notes\",\n      \"link_type\": \"App\\\\Models\\\\PurchaseOrder\",\n      \"archived_at\": null,\n      \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2024-01-15T10:00:00.000000Z\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Supplier Rating\",\n      \"link_type\": \"App\\\\Models\\\\Supplier\",\n      \"archived_at\": null,\n      \"created_at\": \"2024-02-01T09:00:00.000000Z\",\n      \"updated_at\": \"2024-02-01T09:00:00.000000Z\"\n    }\n  ],\n  \"total\": 12,\n  \"per_page\": 15,\n  \"current_page\": 1,\n  \"last_page\": 1\n}"}]},{"name":"Create Custom Field","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/custom-fields","host":["{{baseUrl}}"],"path":["api","custom-fields"]},"description":"Creates a new custom field definition. Custom fields are then attached to records via the custom-field-values API.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFields:\n- name (required) — Display name of the custom field. Must be unique within the same link_type.\n- link_type (required) — Fully qualified model class name the field applies to (e.g., App\\Models\\PurchaseOrder)\n\nUniqueness: The (name, link_type) combination must be unique. Attempting to create a duplicate returns 422 with a friendly error message instead of a 500.","body":{"mode":"raw","raw":"{\n  \"name\": \"Internal Reference\",\n  \"link_type\": \"App\\\\Models\\\\PurchaseOrder\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/custom-fields","host":["{{baseUrl}}"],"path":["api","custom-fields"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 3,\n  \"name\": \"Internal Reference\",\n  \"link_type\": \"App\\\\Models\\\\PurchaseOrder\",\n  \"archived_at\": null,\n  \"created_at\": \"2024-06-01T09:00:00.000000Z\",\n  \"updated_at\": \"2024-06-01T09:00:00.000000Z\"\n}"}]},{"name":"Get Custom Field","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/custom-fields/{customField}","host":["{{baseUrl}}"],"path":["api","custom-fields",":customField"],"variable":[{"key":"customField","value":"","description":"The customField identifier"}]},"description":"Returns a single custom field by ID.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/custom-fields/{customField}","host":["{{baseUrl}}"],"path":["api","custom-fields",":customField"],"variable":[{"key":"customField","value":"","description":"The customField identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Purchase Order Notes\",\n    \"link_type\": \"App\\\\Models\\\\PurchaseOrder\",\n    \"archived_at\": null,\n    \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2024-01-15T10:00:00.000000Z\"\n  }\n}"}]},{"name":"Update Custom Field","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/custom-fields/{customField}","host":["{{baseUrl}}"],"path":["api","custom-fields",":customField"],"variable":[{"key":"customField","value":"","description":"The customField identifier"}]},"description":"Updates an existing custom field. All fields are optional.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFields (all optional):\n- name (optional) — Display name of the custom field. Must be unique within the same link_type (the record's own existing name is allowed).\n- link_type (optional) — Fully qualified model class name\n- archived_at (optional, nullable, date) — Set to a date to archive, null to unarchive\n\nUniqueness: The (name, link_type) combination must be unique. Renaming to a name that already exists for the same link_type returns 422 with a friendly error message instead of a 500.","body":{"mode":"raw","raw":"{\n  \"name\": \"Purchase Order Internal Notes\",\n  \"link_type\": \"App\\\\Models\\\\PurchaseOrder\",\n  \"archived_at\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/custom-fields/{customField}","host":["{{baseUrl}}"],"path":["api","custom-fields",":customField"],"variable":[{"key":"customField","value":"","description":"The customField identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 1,\n  \"name\": \"Purchase Order Internal Notes\",\n  \"link_type\": \"App\\\\Models\\\\PurchaseOrder\",\n  \"archived_at\": null,\n  \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n  \"updated_at\": \"2024-06-01T11:00:00.000000Z\"\n}"}]},{"name":"Delete Custom Field","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/custom-fields/{customField}","host":["{{baseUrl}}"],"path":["api","custom-fields",":customField"],"variable":[{"key":"customField","value":"","description":"The customField identifier"}]},"description":"Deletes a custom field definition by ID.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/custom-fields/{customField}","host":["{{baseUrl}}"],"path":["api","custom-fields",":customField"],"variable":[{"key":"customField","value":"","description":"The customField identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"customField Purchase Order Notes was successfully deleted\"\n}"}]},{"name":"List Custom Field Values","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/custom-field-values","host":["{{baseUrl}}"],"path":["api","custom-field-values"]},"description":"Returns all custom field values. Each value links a custom field definition to a specific record (link_type + link_id).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/custom-field-values","host":["{{baseUrl}}"],"path":["api","custom-field-values"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"[\n  {\n    \"id\": 1,\n    \"custom_field_id\": 1,\n    \"link_type\": \"App\\\\Models\\\\PurchaseOrder\",\n    \"link_id\": 45,\n    \"value\": \"Please ship with fragile label\",\n    \"created_at\": \"2024-03-10T14:00:00.000000Z\",\n    \"updated_at\": \"2024-03-10T14:00:00.000000Z\"\n  },\n  {\n    \"id\": 2,\n    \"custom_field_id\": 2,\n    \"link_type\": \"App\\\\Models\\\\Supplier\",\n    \"link_id\": 12,\n    \"value\": \"5 stars\",\n    \"created_at\": \"2024-04-01T09:00:00.000000Z\",\n    \"updated_at\": \"2024-04-01T09:00:00.000000Z\"\n  }\n]"}]},{"name":"Create Custom Field Value","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/custom-field-values","host":["{{baseUrl}}"],"path":["api","custom-field-values"]},"description":"Creates a new custom field value, attaching a custom field to a specific record. The link_type is automatically inferred from the custom field definition.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFields:\n- custom_field_id (required, integer, exists:custom_fields,id) — ID of the custom field definition\n- link_id (required, integer) — ID of the record to attach the value to\n- value (required) — The value to store","body":{"mode":"raw","raw":"{\n  \"custom_field_id\": 1,\n  \"link_id\": 45,\n  \"value\": \"Please ship with fragile label\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/custom-field-values","host":["{{baseUrl}}"],"path":["api","custom-field-values"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 3,\n  \"custom_field_id\": 1,\n  \"link_type\": \"App\\\\Models\\\\PurchaseOrder\",\n  \"link_id\": 45,\n  \"value\": \"Please ship with fragile label\",\n  \"created_at\": \"2024-06-01T10:00:00.000000Z\",\n  \"updated_at\": \"2024-06-01T10:00:00.000000Z\"\n}"}]},{"name":"Get Custom Field Value","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/custom-field-values/{custom_field_value}","host":["{{baseUrl}}"],"path":["api","custom-field-values",":custom_field_value"],"variable":[{"key":"custom_field_value","value":"1","description":"Custom field value ID"}]},"description":"Returns a single custom field value by ID.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/custom-field-values/{custom_field_value}","host":["{{baseUrl}}"],"path":["api","custom-field-values",":custom_field_value"],"variable":[{"key":"custom_field_value","value":"1","description":"Custom field value ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 1,\n  \"custom_field_id\": 1,\n  \"link_type\": \"App\\\\Models\\\\PurchaseOrder\",\n  \"link_id\": 45,\n  \"value\": \"Please ship with fragile label\",\n  \"created_at\": \"2024-03-10T14:00:00.000000Z\",\n  \"updated_at\": \"2024-03-10T14:00:00.000000Z\"\n}"}]},{"name":"Update Custom Field Value","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/custom-field-values/{custom_field_value}","host":["{{baseUrl}}"],"path":["api","custom-field-values",":custom_field_value"],"variable":[{"key":"custom_field_value","value":"1","description":"Custom field value ID"}]},"description":"Updates the value of an existing custom field value. The custom_field_id, link_id, and link_type are preserved from the existing record.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFields:\n- value (required) — The new value to store","body":{"mode":"raw","raw":"{\n  \"value\": \"Handle with extreme care — fragile electronics\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/custom-field-values/{custom_field_value}","host":["{{baseUrl}}"],"path":["api","custom-field-values",":custom_field_value"],"variable":[{"key":"custom_field_value","value":"1","description":"Custom field value ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 1,\n  \"custom_field_id\": 1,\n  \"link_type\": \"App\\\\Models\\\\PurchaseOrder\",\n  \"link_id\": 45,\n  \"value\": \"Handle with extreme care — fragile electronics\",\n  \"created_at\": \"2024-03-10T14:00:00.000000Z\",\n  \"updated_at\": \"2024-06-01T12:00:00.000000Z\"\n}"}]},{"name":"Delete Custom Field Value","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/custom-field-values/{custom_field_value}","host":["{{baseUrl}}"],"path":["api","custom-field-values",":custom_field_value"],"variable":[{"key":"custom_field_value","value":"1","description":"Custom field value ID"}]},"description":"Deletes a custom field value by ID.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/custom-field-values/{custom_field_value}","host":["{{baseUrl}}"],"path":["api","custom-field-values",":custom_field_value"],"variable":[{"key":"custom_field_value","value":"1","description":"Custom field value ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{}"}]}]},{"name":"Sales Reps","item":[{"name":"List Sales Reps","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sales-reps","host":["{{baseUrl}}"],"path":["api","sales-reps"]},"description":"List all sales reps with pagination, filtering, and sorting via Spatie QueryBuilder.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nAllowed sorts: name, email, phone, active, created_at, updated_at (default: name)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/sales-reps","host":["{{baseUrl}}"],"path":["api","sales-reps"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Jane Smith\",\n      \"email\": \"jane@example.com\",\n      \"active\": true,\n      \"sales_orders_count\": 42\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 10,\n  \"total\": 28\n}"}]},{"name":"Create Sales Rep","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/sales-reps","host":["{{baseUrl}}"],"path":["api","sales-reps"]},"description":"Create a new sales rep. Defaults active to true if not provided.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFields:\n- name (required): Sales rep display name (max 255 chars)\n- email (optional): Unique email address\n- phone (optional): Phone number\n- user_id (optional): Link to a user account\n- active (optional): Whether the sales rep is active (default: true)","body":{"mode":"raw","raw":"{\n  \"name\": \"Jane Smith\",\n  \"email\": \"jane@example.com\",\n  \"phone\": \"555-123-4567\",\n  \"user_id\": 5,\n  \"active\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/sales-reps","host":["{{baseUrl}}"],"path":["api","sales-reps"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Jane Smith\",\n    \"active\": true\n  },\n  \"message\": \"Sales rep created successfully.\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Sales Rep","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sales-reps/{id}","host":["{{baseUrl}}"],"path":["api","sales-reps",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Get a single sales rep by ID, including their linked user account and sales order count.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/sales-reps/{id}","host":["{{baseUrl}}"],"path":["api","sales-reps",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Jane Smith\",\n    \"active\": true,\n    \"sales_orders_count\": 42\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Update Sales Rep","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/sales-reps/{id}","host":["{{baseUrl}}"],"path":["api","sales-reps",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Update an existing sales rep.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"name\": \"Jane Smith-Johnson\",\n  \"email\": \"jane.johnson@example.com\",\n  \"phone\": \"555-999-0000\",\n  \"active\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/sales-reps/{id}","host":["{{baseUrl}}"],"path":["api","sales-reps",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Jane Smith-Johnson\",\n    \"active\": true\n  },\n  \"message\": \"Sales rep updated successfully.\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Sales Rep Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sales-reps/{salesRep}/activity-log","host":["{{baseUrl}}"],"path":["api","sales-reps",":salesRep","activity-log"],"variable":[{"key":"salesRep","value":"1","description":"Sales rep ID"}]},"description":"Get the activity log for a specific sales rep. Returns a paginated list of audit events with the causer and changed properties.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/sales-reps/{salesRep}/activity-log","host":["{{baseUrl}}"],"path":["api","sales-reps",":salesRep","activity-log"],"variable":[{"key":"salesRep","value":"1","description":"Sales rep ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1001,\n      \"event\": \"updated\",\n      \"causer_name\": \"Admin User\",\n      \"created_at\": \"2024-06-15T10:30:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"total\": 22\n}"}]},{"name":"Get Sales Reps for Dropdown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sales-reps/list","host":["{{baseUrl}}"],"path":["api","sales-reps","list"]},"description":"Get a simplified list of all sales reps (id and name only) for use in dropdown/combo box lookups. Returns all reps ordered by name without pagination.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/sales-reps/list","host":["{{baseUrl}}"],"path":["api","sales-reps","list"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Jane Smith\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Bob Johnson\"\n    }\n  ],\n  \"status\": \"success\"\n}"}]}]},{"name":"Warehouses","item":[{"name":"List Warehouse Locations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/warehouse-locations/{warehouse}","host":["{{baseUrl}}"],"path":["api","warehouse-locations",":warehouse"],"variable":[{"key":"warehouse","value":"3","description":"Warehouse ID whose locations to list"}]},"description":"List all storage locations defined for a warehouse. The path parameter is the WAREHOUSE ID (not a location ID).\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA location is a physical slot addressed by aisle / bay / shelf / bin. At most one location per warehouse is flagged `is_default` — new stock is placed there unless a location is specified.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/warehouse-locations/{warehouse}","host":["{{baseUrl}}"],"path":["api","warehouse-locations",":warehouse"],"variable":[{"key":"warehouse","value":"3","description":"Warehouse ID whose locations to list"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"warehouse_id\": 3,\n      \"aisle\": \"A\",\n      \"bay\": \"1\",\n      \"shelf\": \"2\",\n      \"bin\": \"B\",\n      \"is_default\": true\n    },\n    {\n      \"id\": 2,\n      \"warehouse_id\": 3,\n      \"aisle\": \"B\",\n      \"bay\": \"4\",\n      \"shelf\": null,\n      \"bin\": null,\n      \"is_default\": false\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Update Warehouse Location","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouse-locations/{warehouse}","host":["{{baseUrl}}"],"path":["api","warehouse-locations",":warehouse"],"variable":[{"key":"warehouse","value":"","description":"The warehouse identifier"}]},"description":"Update a storage location. All fields are optional — send only the fields to change.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields:\n- `aisle`, `bay`, `shelf`, `bin` (max 255 each).\n- `is_default` (boolean) — when true, this location becomes the warehouse's default and the flag is cleared from every other location in that warehouse.\n\nNote: empty, null, and false values are ignored by this endpoint — it only applies non-empty values. To clear a location's default flag, set `is_default: true` on another location in the same warehouse instead of sending false here.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"aisle\": \"C\",\n  \"bay\": \"3\",\n  \"shelf\": \"1\",\n  \"bin\": \"A\",\n  \"is_default\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouse-locations/{warehouse}","host":["{{baseUrl}}"],"path":["api","warehouse-locations",":warehouse"],"variable":[{"key":"warehouse","value":"","description":"The warehouse identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"The warehouse location '7' updated successfully\",\n  \"data\": {\n    \"id\": 7,\n    \"warehouse_id\": 3,\n    \"aisle\": \"C\",\n    \"bay\": \"3\",\n    \"shelf\": \"1\",\n    \"bin\": \"A\",\n    \"is_default\": true\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Create Warehouse Location","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouse-locations","host":["{{baseUrl}}"],"path":["api","warehouse-locations"]},"description":"Create a storage location inside a warehouse.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields:\n- `warehouse_id` (required) — must be an existing warehouse.\n- `aisle` (required, max 255).\n- `bay`, `shelf`, `bin` (optional, max 255 each).\n- `is_default` (optional boolean) — when true, this location becomes the warehouse's default and the default flag is automatically cleared from every other location in that warehouse.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"warehouse_id\": 3,\n  \"aisle\": \"C\",\n  \"bay\": \"2\",\n  \"shelf\": \"1\",\n  \"bin\": \"A\",\n  \"is_default\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/warehouse-locations","host":["{{baseUrl}}"],"path":["api","warehouse-locations"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"The warehouse location created successfully\",\n  \"data\": {\n    \"warehouse_id\": 3,\n    \"aisle\": \"C\",\n    \"bay\": \"2\",\n    \"shelf\": \"1\",\n    \"bin\": \"A\",\n    \"is_default\": false,\n    \"id\": 7\n  },\n  \"status\": \"success\"\n}"}]},{"name":"List Warehouses","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouses","host":["{{baseUrl}}"],"path":["api","v2","warehouses"]},"description":"Returns all warehouses.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContract-manufacturer warehouses are direct warehouses with subtype = \"contractor\" and a non-null contractor_supplier_id (the linked supplier). Their stock is real owned inventory, but they are excluded from channel available-to-sell and are never auto-routed for fulfillment (auto_routing_enabled is always false)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouses","host":["{{baseUrl}}"],"path":["api","v2","warehouses"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"display_name\": \"Main Warehouse\",\n      \"type\": \"direct\",\n      \"subtype\": null,\n      \"contractor_supplier_id\": null,\n      \"supplier_id\": null,\n      \"supplier_name\": null,\n      \"integration_instance_id\": null,\n      \"country_code\": \"US\",\n      \"address_id\": 14,\n      \"order_fulfillment\": \"auto\",\n      \"nominal_code_id\": null,\n      \"nominal_code_name\": null,\n      \"dropship_enabled\": false,\n      \"locations_enabled\": true,\n      \"direct_returns\": true,\n      \"customer_returns\": true,\n      \"auto_routing_enabled\": true,\n      \"auto_dispatch_enabled\": false,\n      \"partial_dispatch_enabled\": false,\n      \"archived_at\": null,\n      \"is_archivable\": true,\n      \"archive_blocked_reason\": null,\n      \"created_at\": \"2026-01-01T00:00:00.000000Z\",\n      \"updated_at\": \"2026-06-01T00:00:00.000000Z\",\n      \"city\": \"Austin\",\n      \"province\": \"Texas\",\n      \"country\": \"United States\",\n      \"zip\": \"78701\"\n    },\n    {\n      \"id\": 9,\n      \"name\": \"Acme Assembly Co (Contractor)\",\n      \"display_name\": \"Acme Assembly Co (Contractor)\",\n      \"type\": \"direct\",\n      \"subtype\": \"contractor\",\n      \"contractor_supplier_id\": 12,\n      \"supplier_id\": null,\n      \"supplier_name\": null,\n      \"integration_instance_id\": null,\n      \"country_code\": \"US\",\n      \"address_id\": 31,\n      \"order_fulfillment\": \"manual\",\n      \"nominal_code_id\": null,\n      \"nominal_code_name\": null,\n      \"dropship_enabled\": false,\n      \"locations_enabled\": true,\n      \"direct_returns\": false,\n      \"customer_returns\": false,\n      \"auto_routing_enabled\": false,\n      \"auto_dispatch_enabled\": false,\n      \"partial_dispatch_enabled\": false,\n      \"archived_at\": null,\n      \"is_archivable\": true,\n      \"archive_blocked_reason\": null,\n      \"created_at\": \"2026-03-15T00:00:00.000000Z\",\n      \"updated_at\": \"2026-06-01T00:00:00.000000Z\",\n      \"city\": null,\n      \"province\": null,\n      \"country\": null,\n      \"zip\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"https://example.test/api/v2/warehouses?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://example.test/api/v2/warehouses?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://example.test/api/v2/warehouses\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 2,\n  \"total\": 2\n}"}]},{"name":"Show Warehouse","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouses/{warehouse}","host":["{{baseUrl}}"],"path":["api","v2","warehouses",":warehouse"],"variable":[{"key":"warehouse","value":"2","description":"Warehouse ID"}]},"description":"Returns a single warehouse.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContract-manufacturer warehouses are direct warehouses with subtype = \"contractor\" and a non-null contractor_supplier_id (the linked supplier). Their stock is real owned inventory, but they are excluded from channel available-to-sell and are never auto-routed for fulfillment (auto_routing_enabled is always false)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouses/{warehouse}","host":["{{baseUrl}}"],"path":["api","v2","warehouses",":warehouse"],"variable":[{"key":"warehouse","value":"2","description":"Warehouse ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 2,\n    \"name\": \"Main Warehouse\",\n    \"display_name\": \"Main Warehouse\",\n    \"type\": \"direct\",\n    \"subtype\": null,\n    \"contractor_supplier_id\": null,\n    \"supplier_id\": null,\n    \"supplier_name\": null,\n    \"integration_instance_id\": null,\n    \"country_code\": \"US\",\n    \"address_id\": 14,\n    \"order_fulfillment\": \"auto\",\n    \"nominal_code_id\": null,\n    \"nominal_code_name\": null,\n    \"dropship_enabled\": false,\n    \"locations_enabled\": true,\n    \"direct_returns\": true,\n    \"customer_returns\": true,\n    \"auto_routing_enabled\": true,\n    \"auto_dispatch_enabled\": false,\n    \"partial_dispatch_enabled\": false,\n    \"archived_at\": null,\n    \"is_archivable\": true,\n    \"archive_blocked_reason\": null,\n    \"created_at\": \"2026-01-01T00:00:00.000000Z\",\n    \"updated_at\": \"2026-06-01T00:00:00.000000Z\",\n    \"city\": \"Austin\",\n    \"province\": \"Texas\",\n    \"country\": \"United States\",\n    \"zip\": \"78701\"\n  }\n}"}]},{"name":"Create Warehouse","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses","host":["{{baseUrl}}"],"path":["api","warehouses"]},"description":"Creates a new warehouse.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequired fields: name (unique, max 255), type (one of: direct, 3pl, supplier, amazon_fba, virtual, amazon_awd, walmart_wfs).\n\nOptional fields: subtype (only \"decorator\" may be set here; integration-managed subtypes are assigned automatically), address_name, company, email, phone, fax, address1, address2, address3, city, province, province_code, zip, country, country_code, supplier_id (links a supplier-owned warehouse), order_fulfillment, dropship_enabled, direct_returns, customer_returns, auto_routing_enabled, auto_dispatch_enabled, partial_dispatch_enabled, is_default, default_location (object with aisle, bay, shelf, bin), nominal_code_id, transfer_destination_warehouse_id.\n\nThe warehouse name must be unique (per supplier for supplier warehouses).\n\nAuthentication: Requires Bearer token.\n\nAlso accepts (3PL warehouses): fba_inbound_reminders_enabled (boolean) and fba_inbound_reminder_days (array of positive integer day-offsets, e.g. [3,7,14]) — controls automatic FBA-inbound turnaround reminders to this 3PL.\n\nWFS inbound-reminder settings (opt-in turnaround nudges for WFS inbound shipments sourced from this 3PL/warehouse):\n- wfs_inbound_reminders_enabled (boolean, optional): enable per-warehouse WFS inbound turnaround reminders.\n- wfs_inbound_reminder_days (int[], optional, nullable, each 1–365): days-after-send cadence to remind on (default [3, 7, 14] when reminders are enabled and none provided).\n\n`negative_bin_policy` (optional; one of `block`, `warn`, `allow`; default `warn`) controls how the warehouse reacts when an outbound movement would drive a specific bin's on-hand below zero: `block` rejects the movement, `warn` permits it but flags the shortfall, and `allow` permits it silently. The warehouse's default catch-all location is always exempt.\n\n`negative_inventory_policy` (string|null: `block`|`warn`|`allow`, optional): whether a sale may ship from this warehouse when it would take the warehouse below zero on hand, recording the shortfall as inventory debt. `null` inherits the company setting. Distinct from `negative_bin_policy`, which shares the same vocabulary but governs a single bin's count rather than the warehouse's stock position.","body":{"mode":"raw","raw":"{\n  \"name\": \"Main Warehouse\",\n  \"type\": \"direct\",\n  \"company\": \"Acme Corp\",\n  \"email\": \"warehouse@example.com\",\n  \"phone\": \"+1 555 0100\",\n  \"address1\": \"100 Commerce Way\",\n  \"address2\": null,\n  \"city\": \"Austin\",\n  \"province\": \"Texas\",\n  \"province_code\": \"TX\",\n  \"zip\": \"78701\",\n  \"country\": \"United States\",\n  \"country_code\": \"US\",\n  \"order_fulfillment\": \"manual\",\n  \"dropship_enabled\": false,\n  \"direct_returns\": false,\n  \"customer_returns\": true,\n  \"auto_routing_enabled\": true,\n  \"auto_dispatch_enabled\": false,\n  \"partial_dispatch_enabled\": false,\n  \"negative_bin_policy\": \"warn\",\n  \"default_location\": {\n    \"aisle\": \"Default\",\n    \"bay\": null,\n    \"shelf\": null,\n    \"bin\": null\n  },\n  \"nominal_code_id\": null,\n  \"transfer_destination_warehouse_id\": null,\n  \"wfs_inbound_reminders_enabled\": true,\n  \"wfs_inbound_reminder_days\": [\n    3,\n    7,\n    14\n  ],\n  \"exclude_from_planning\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/warehouses","host":["{{baseUrl}}"],"path":["api","warehouses"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse created successfully.\",\n  \"data\": {\n    \"name\": \"Main Warehouse\",\n    \"display_name\": \"Main Warehouse\",\n    \"id\": 12,\n    \"type\": \"direct\",\n    \"subtype\": null,\n    \"supplier_id\": null,\n    \"contractor_supplier_id\": null,\n    \"dropship_enabled\": false,\n    \"created_at\": \"2026-01-10T09:00:00.000000Z\",\n    \"updated_at\": \"2026-06-01T14:30:00.000000Z\",\n    \"archived_at\": null,\n    \"address_name\": \"Main Warehouse\",\n    \"order_fulfillment\": \"manual\",\n    \"address_id\": 301,\n    \"label\": null,\n    \"email\": \"warehouse@example.com\",\n    \"phone\": \"+1 555 0100\",\n    \"fax\": null,\n    \"company\": \"Acme Corp\",\n    \"address1\": \"100 Commerce Way\",\n    \"address2\": null,\n    \"address3\": null,\n    \"city\": \"Austin\",\n    \"province\": \"Texas\",\n    \"province_code\": \"TX\",\n    \"zip\": \"78701\",\n    \"country\": \"United States\",\n    \"country_code\": \"US\",\n    \"address_redacted\": false,\n    \"warehouse_locations\": [\n      {\n        \"id\": 55,\n        \"warehouse_id\": 12,\n        \"aisle\": \"Default\",\n        \"bay\": null,\n        \"shelf\": null,\n        \"bin\": null,\n        \"is_default\": true\n      }\n    ],\n    \"shipping_automated\": null,\n    \"is_archivable\": true,\n    \"archive_blocked_reason\": null,\n    \"auto_routing_enabled\": true,\n    \"auto_dispatch_enabled\": false,\n    \"partial_dispatch_enabled\": false,\n    \"negative_bin_policy\": \"warn\",\n    \"integration_instance_id\": null,\n    \"nominal_code_id\": null,\n    \"transfer_destination_warehouse_id\": null,\n    \"is_amazon_fba_warehouse\": false,\n    \"externally_managed\": false,\n    \"channel_fulfilled\": false\n  }\n}"}]},{"name":"Bulk Delete Warehouses","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses","host":["{{baseUrl}}"],"path":["api","warehouses"]},"description":"Deletes multiple warehouses. Provide either ids (array of warehouse IDs) or apply_to_all=true with an optional filters object. Warehouses referenced by other records are skipped and counted in failCount. Use Check Warehouses Deletable first to verify which records can be removed.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/warehouses","host":["{{baseUrl}}"],"path":["api","warehouses"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"totalCount\": 3,\n    \"failCount\": 0\n  },\n  \"message\": \"3 records processed successfully.\"\n}"}]},{"name":"Update Warehouse","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/{warehouse}","host":["{{baseUrl}}"],"path":["api","warehouses",":warehouse"],"variable":[{"key":"warehouse","value":"12","description":"Warehouse ID"}]},"description":"Updates an existing warehouse. Accepts the same fields as Create Warehouse; all fields are optional on update.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAddress fields (address1, city, zip, country_code, etc.) update the warehouse's address record. Setting is_default on a supplier warehouse makes it that supplier's default warehouse. Toggling auto_routing_enabled adds or removes the warehouse from the automatic order-routing priority list.\n\nReturns 400 when disabling dropshipping on a warehouse that still has open dropship orders.\n\nAuthentication: Requires Bearer token.\n\nAlso accepts (3PL warehouses): fba_inbound_reminders_enabled (boolean) and fba_inbound_reminder_days (array of positive integer day-offsets, e.g. [3,7,14]) — controls automatic FBA-inbound turnaround reminders to this 3PL.\n\nWFS inbound-reminder settings (opt-in turnaround nudges for WFS inbound shipments sourced from this 3PL/warehouse):\n- wfs_inbound_reminders_enabled (boolean, optional): enable per-warehouse WFS inbound turnaround reminders.\n- wfs_inbound_reminder_days (int[], optional, nullable, each 1–365): days-after-send cadence to remind on (default [3, 7, 14] when reminders are enabled and none provided).\n\n`negative_bin_policy` (optional; one of `block`, `warn`, `allow`; default `warn`) controls how the warehouse reacts when an outbound movement would drive a specific bin's on-hand below zero: `block` rejects the movement, `warn` permits it but flags the shortfall, and `allow` permits it silently. The warehouse's default catch-all location is always exempt.\n\n`negative_inventory_policy` (string|null: `block`|`warn`|`allow`, optional): whether a sale may ship from this warehouse when it would take the warehouse below zero on hand, recording the shortfall as inventory debt. `null` inherits the company setting. Distinct from `negative_bin_policy`, which shares the same vocabulary but governs a single bin's count rather than the warehouse's stock position.","body":{"mode":"raw","raw":"{\n  \"name\": \"Main Warehouse East\",\n  \"auto_routing_enabled\": false,\n  \"order_fulfillment\": \"manual\",\n  \"wfs_inbound_reminders_enabled\": true,\n  \"wfs_inbound_reminder_days\": [\n    3,\n    7,\n    14\n  ],\n  \"negative_bin_policy\": \"block\",\n  \"exclude_from_planning\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/{warehouse}","host":["{{baseUrl}}"],"path":["api","warehouses",":warehouse"],"variable":[{"key":"warehouse","value":"12","description":"Warehouse ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse Main Warehouse East updated successfully.\",\n  \"data\": {\n    \"name\": \"Main Warehouse\",\n    \"display_name\": \"Main Warehouse\",\n    \"id\": 12,\n    \"type\": \"direct\",\n    \"subtype\": null,\n    \"supplier_id\": null,\n    \"contractor_supplier_id\": null,\n    \"dropship_enabled\": false,\n    \"created_at\": \"2026-01-10T09:00:00.000000Z\",\n    \"updated_at\": \"2026-06-01T14:30:00.000000Z\",\n    \"archived_at\": null,\n    \"address_name\": \"Main Warehouse\",\n    \"order_fulfillment\": \"manual\",\n    \"address_id\": 301,\n    \"label\": null,\n    \"email\": \"warehouse@example.com\",\n    \"phone\": \"+1 555 0100\",\n    \"fax\": null,\n    \"company\": \"Acme Corp\",\n    \"address1\": \"100 Commerce Way\",\n    \"address2\": null,\n    \"address3\": null,\n    \"city\": \"Austin\",\n    \"province\": \"Texas\",\n    \"province_code\": \"TX\",\n    \"zip\": \"78701\",\n    \"country\": \"United States\",\n    \"country_code\": \"US\",\n    \"address_redacted\": false,\n    \"warehouse_locations\": [\n      {\n        \"id\": 55,\n        \"warehouse_id\": 12,\n        \"aisle\": \"Default\",\n        \"bay\": null,\n        \"shelf\": null,\n        \"bin\": null,\n        \"is_default\": true\n      }\n    ],\n    \"shipping_automated\": null,\n    \"is_archivable\": true,\n    \"archive_blocked_reason\": null,\n    \"auto_routing_enabled\": true,\n    \"auto_dispatch_enabled\": false,\n    \"partial_dispatch_enabled\": false,\n    \"negative_bin_policy\": \"block\",\n    \"integration_instance_id\": null,\n    \"nominal_code_id\": null,\n    \"transfer_destination_warehouse_id\": null,\n    \"is_amazon_fba_warehouse\": false,\n    \"externally_managed\": false,\n    \"channel_fulfilled\": false\n  }\n}"}]},{"name":"Delete Warehouse","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/{warehouse}","host":["{{baseUrl}}"],"path":["api","warehouses",":warehouse"],"variable":[{"key":"warehouse","value":"12","description":"Warehouse ID"}]},"description":"Deletes a warehouse. A warehouse still referenced by other records cannot be deleted: sales order lines and fulfillments, fulfillment orders, purchase orders, inbound shipments and receipts, inventory movements and FIFO layers, inventory adjustments and assemblies, stock takes, manufacturing orders, warehouse transfers, RMAs and return receipts, sales credits, vendor credit shipments, vendor returns and their shipments, POS register sessions, Amazon pending inbounds, suppliers using it as their default warehouse, or a connected integration. The 400 response lists each blocking record type under `errors`. If the database still refuses the delete for any other linked record, the endpoint returns 400 and nothing is deleted. Use Check Warehouses Deletable first to see every blocker.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/warehouses/{warehouse}","host":["{{baseUrl}}"],"path":["api","warehouses",":warehouse"],"variable":[{"key":"warehouse","value":"12","description":"Warehouse ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse Main Warehouse deleted successfully.\"\n}"}]},{"name":"Archive Warehouse","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/{warehouse}/archive","host":["{{baseUrl}}"],"path":["api","warehouses",":warehouse","archive"],"variable":[{"key":"warehouse","value":"12","description":"Warehouse ID"}]},"description":"Archives a warehouse, hiding it from active listings. Integration-managed warehouses (e.g. Amazon FBA) and warehouses that still hold available stock cannot be archived.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/{warehouse}/archive","host":["{{baseUrl}}"],"path":["api","warehouses",":warehouse","archive"],"variable":[{"key":"warehouse","value":"12","description":"Warehouse ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse Main Warehouse archived successfully.\",\n  \"data\": {\n    \"name\": \"Main Warehouse\",\n    \"display_name\": \"Main Warehouse\",\n    \"id\": 12,\n    \"type\": \"direct\",\n    \"subtype\": null,\n    \"supplier_id\": null,\n    \"contractor_supplier_id\": null,\n    \"dropship_enabled\": false,\n    \"created_at\": \"2026-01-10T09:00:00.000000Z\",\n    \"updated_at\": \"2026-06-01T14:30:00.000000Z\",\n    \"archived_at\": null,\n    \"address_name\": \"Main Warehouse\",\n    \"order_fulfillment\": \"manual\",\n    \"address_id\": 301,\n    \"label\": null,\n    \"email\": \"warehouse@example.com\",\n    \"phone\": \"+1 555 0100\",\n    \"fax\": null,\n    \"company\": \"Acme Corp\",\n    \"address1\": \"100 Commerce Way\",\n    \"address2\": null,\n    \"address3\": null,\n    \"city\": \"Austin\",\n    \"province\": \"Texas\",\n    \"province_code\": \"TX\",\n    \"zip\": \"78701\",\n    \"country\": \"United States\",\n    \"country_code\": \"US\",\n    \"address_redacted\": false,\n    \"warehouse_locations\": [\n      {\n        \"id\": 55,\n        \"warehouse_id\": 12,\n        \"aisle\": \"Default\",\n        \"bay\": null,\n        \"shelf\": null,\n        \"bin\": null,\n        \"is_default\": true\n      }\n    ],\n    \"shipping_automated\": null,\n    \"is_archivable\": true,\n    \"archive_blocked_reason\": null,\n    \"auto_routing_enabled\": true,\n    \"auto_dispatch_enabled\": false,\n    \"partial_dispatch_enabled\": false,\n    \"integration_instance_id\": null,\n    \"nominal_code_id\": null,\n    \"transfer_destination_warehouse_id\": null,\n    \"is_amazon_fba_warehouse\": false,\n    \"externally_managed\": false,\n    \"channel_fulfilled\": false\n  }\n}"}]},{"name":"Unarchive Warehouse","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/{warehouse}/unarchived","host":["{{baseUrl}}"],"path":["api","warehouses",":warehouse","unarchived"],"variable":[{"key":"warehouse","value":"12","description":"Warehouse ID"}]},"description":"Restores an archived warehouse to active status. Integration-managed warehouses (e.g. Amazon FBA) cannot be modified through this endpoint.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/{warehouse}/unarchived","host":["{{baseUrl}}"],"path":["api","warehouses",":warehouse","unarchived"],"variable":[{"key":"warehouse","value":"12","description":"Warehouse ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse Main Warehouse unarchived successfully.\",\n  \"data\": {\n    \"name\": \"Main Warehouse\",\n    \"display_name\": \"Main Warehouse\",\n    \"id\": 12,\n    \"type\": \"direct\",\n    \"subtype\": null,\n    \"supplier_id\": null,\n    \"contractor_supplier_id\": null,\n    \"dropship_enabled\": false,\n    \"created_at\": \"2026-01-10T09:00:00.000000Z\",\n    \"updated_at\": \"2026-06-01T14:30:00.000000Z\",\n    \"archived_at\": null,\n    \"address_name\": \"Main Warehouse\",\n    \"order_fulfillment\": \"manual\",\n    \"address_id\": 301,\n    \"label\": null,\n    \"email\": \"warehouse@example.com\",\n    \"phone\": \"+1 555 0100\",\n    \"fax\": null,\n    \"company\": \"Acme Corp\",\n    \"address1\": \"100 Commerce Way\",\n    \"address2\": null,\n    \"address3\": null,\n    \"city\": \"Austin\",\n    \"province\": \"Texas\",\n    \"province_code\": \"TX\",\n    \"zip\": \"78701\",\n    \"country\": \"United States\",\n    \"country_code\": \"US\",\n    \"address_redacted\": false,\n    \"warehouse_locations\": [\n      {\n        \"id\": 55,\n        \"warehouse_id\": 12,\n        \"aisle\": \"Default\",\n        \"bay\": null,\n        \"shelf\": null,\n        \"bin\": null,\n        \"is_default\": true\n      }\n    ],\n    \"shipping_automated\": null,\n    \"is_archivable\": true,\n    \"archive_blocked_reason\": null,\n    \"auto_routing_enabled\": true,\n    \"auto_dispatch_enabled\": false,\n    \"partial_dispatch_enabled\": false,\n    \"integration_instance_id\": null,\n    \"nominal_code_id\": null,\n    \"transfer_destination_warehouse_id\": null,\n    \"is_amazon_fba_warehouse\": false,\n    \"externally_managed\": false,\n    \"channel_fulfilled\": false\n  }\n}"}]},{"name":"Bulk Archive Warehouses","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/archive","host":["{{baseUrl}}"],"path":["api","warehouses","archive"]},"description":"Archives multiple warehouses. Provide either ids (array of warehouse IDs) or apply_to_all=true with an optional filters object to target all warehouses matching the current listing filters.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nWarehouses that cannot be archived (integration-managed, or holding stock) are skipped and counted in failCount, with a per-record warning in the response.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    12,\n    13,\n    14\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/archive","host":["{{baseUrl}}"],"path":["api","warehouses","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"totalCount\": 3,\n    \"failCount\": 0\n  },\n  \"message\": \"3 records processed successfully.\"\n}"}]},{"name":"Bulk Unarchive Warehouses","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/unarchive","host":["{{baseUrl}}"],"path":["api","warehouses","unarchive"]},"description":"Restores multiple archived warehouses. Provide either ids (array of warehouse IDs) or apply_to_all=true with an optional filters object.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    12,\n    13,\n    14\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/unarchive","host":["{{baseUrl}}"],"path":["api","warehouses","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"totalCount\": 3,\n    \"failCount\": 0\n  },\n  \"message\": \"3 records processed successfully.\"\n}"}]},{"name":"Check Warehouses Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/deletable","host":["{{baseUrl}}"],"path":["api","warehouses","deletable"]},"description":"Checks whether the given warehouses can be deleted. Provide ids (array of warehouse IDs), or scope=\"all\" to check every warehouse matching the current listing filters.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nEach result carries `deletable`, plus — when it is blocked — `reason` (a message per blocking relation), `reason_counts` (how many records each relation holds) and `reason_details` (a preview of the linked records, available for some relations only). Use `reason_counts` for the number of blocking records; `reason_details` does not cover every relation.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    12,\n    13\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/warehouses/deletable","host":["{{baseUrl}}"],"path":["api","warehouses","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"name\": \"Main Warehouse\",\n      \"deletable\": false,\n      \"reason\": {\n        \"purchaseOrders\": \"There are 4 purchase orders associated with this warehouse(Main Warehouse).\",\n        \"inventoryMovements\": \"There are 45 inventory movements associated with this warehouse(Main Warehouse).\"\n      },\n      \"reason_counts\": {\n        \"purchaseOrders\": 4,\n        \"inventoryMovements\": 45\n      },\n      \"reason_details\": {\n        \"purchaseOrders\": [\n          {\n            \"id\": 88,\n            \"label\": \"PO-1042\"\n          }\n        ]\n      }\n    },\n    {\n      \"id\": 13,\n      \"name\": \"Overflow Warehouse\",\n      \"deletable\": true,\n      \"reason\": null,\n      \"reason_counts\": null,\n      \"reason_details\": null\n    }\n  ]\n}"}]},{"name":"Check Warehouses Archivable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/is-archivable","host":["{{baseUrl}}"],"path":["api","warehouses","is-archivable"]},"description":"Checks whether the given warehouses can be archived. Each result includes archivable (boolean) and reason (why archiving is blocked, or null). Integration-managed warehouses and warehouses holding stock are not archivable.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    12,\n    13\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/warehouses/is-archivable","host":["{{baseUrl}}"],"path":["api","warehouses","is-archivable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"name\": \"Main Warehouse\",\n      \"archivable\": false,\n      \"reason\": \"Warehouse has stock available\"\n    },\n    {\n      \"id\": 13,\n      \"name\": \"Overflow Warehouse\",\n      \"archivable\": true,\n      \"reason\": null\n    }\n  ]\n}"}]},{"name":"Preview Warehouse Import","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/import/preview","host":["{{baseUrl}}"],"path":["api","warehouses","import","preview"]},"description":"Parses an uploaded CSV file and returns a preview before importing: parsed records, detected file headers, the list of importable fields, unmatched columns (difference), missing required columns (required_difference) and columns that cannot be imported (not_importable).\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequired fields: original_name (the file's original name), stored_name (the stored upload reference returned by the file upload). Optional: mapping (array of {file_field, expected_field} pairs to map CSV columns to warehouse fields), separator, escape, meta, task_id.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"original_name\": \"warehouses.csv\",\n  \"stored_name\": \"uploads/abc123-warehouses.csv\",\n  \"separator\": \",\",\n  \"escape\": \"\\\"\",\n  \"mapping\": [\n    {\n      \"file_field\": \"Warehouse Name\",\n      \"expected_field\": \"name\"\n    },\n    {\n      \"file_field\": \"Type\",\n      \"expected_field\": \"type\"\n    },\n    {\n      \"file_field\": \"City\",\n      \"expected_field\": \"city\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/warehouses/import/preview","host":["{{baseUrl}}"],"path":["api","warehouses","import","preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"records\": [\n      {\n        \"name\": \"Main Warehouse\",\n        \"type\": \"direct\",\n        \"city\": \"Austin\"\n      }\n    ],\n    \"file_headers\": [\n      \"Warehouse Name\",\n      \"Type\",\n      \"City\"\n    ],\n    \"importable_fields\": [\n      \"name\",\n      \"type\",\n      \"city\",\n      \"province\",\n      \"country_code\",\n      \"zip\"\n    ],\n    \"difference\": [],\n    \"required_difference\": [],\n    \"not_importable\": [],\n    \"task_id\": null\n  }\n}"}]},{"name":"Import Warehouses","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/import","host":["{{baseUrl}}"],"path":["api","warehouses","import"]},"description":"Imports warehouses from an uploaded CSV file. Accepts the same payload as Preview Warehouse Import. If required columns are missing, the preview payload is returned instead so the mapping can be corrected. Large files are processed in the background and the response indicates the import will complete shortly.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"original_name\": \"warehouses.csv\",\n  \"stored_name\": \"uploads/abc123-warehouses.csv\",\n  \"separator\": \",\",\n  \"escape\": \"\\\"\",\n  \"mapping\": [\n    {\n      \"file_field\": \"Warehouse Name\",\n      \"expected_field\": \"name\"\n    },\n    {\n      \"file_field\": \"Type\",\n      \"expected_field\": \"type\"\n    },\n    {\n      \"file_field\": \"City\",\n      \"expected_field\": \"city\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/warehouses/import","host":["{{baseUrl}}"],"path":["api","warehouses","import"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": \"Data import was successful.\"\n}"}]},{"name":"Export Warehouses","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouses/export","host":["{{baseUrl}}"],"path":["api","v2","warehouses","export"]},"description":"Exports warehouses matching the current filters as a CSV or XLSX file download.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nColumns: Sr No., id, name, type, city, province, province_code, country, country_code, zip, created_at, updated_at. Archived warehouses are excluded unless filter[archived] is provided (values: only, all).\n\nColumn filters accept operator suffixes in the form filter[column.operator], e.g. filter[name.contains]=main or filter[created_at.between]=2026-01-01,2026-06-30. Text operators: is, is_not, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, is_one_of. Numeric operators: is, is_not, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between, is_empty, is_not_empty. Date operators: is, is_not, on_or_before, on_or_after, between, is_empty, is_not_empty.\n\nAuthentication: Requires Bearer token.\n\nOptional. When `include_contacts=1`, the export flattens to one row per contact with the parent columns repeated (parent-only row when a record has no contacts), appending Contact ID/Name/Email/Phone/Role/Title/Primary/Notes columns."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouses/export","host":["{{baseUrl}}"],"path":["api","v2","warehouses","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"Sr No.,id,name,type,city,province,province_code,country,country_code,zip,created_at,updated_at\n1,12,Main Warehouse,direct,Austin,Texas,TX,United States,US,78701,2026-01-10T09:00:00.000000Z,2026-06-01T14:30:00.000000Z\n"}]},{"name":"List Fulfillment Dispatch Settings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/fulfillment-dispatch-settings","host":["{{baseUrl}}"],"path":["api","warehouses","fulfillment-dispatch-settings"]},"description":"Returns the fulfillment dispatch configuration for every non-supplier warehouse: the fulfillment channel (order_fulfillment), whether fulfillments are automatically dispatched to the connected shipping provider (auto_dispatch_enabled) and whether partial dispatch is allowed (partial_dispatch_enabled).\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe response also includes shippingProviders (the available order_fulfillment values: \"manual\" plus the names of connected shipping-provider integrations) and shippingProviderDispatchEnabled (whether real provider dispatch is on for this account) and shippingProviderDispatchEditable (whether that setting can be toggled here — true outside production, where dispatch is a per-account opt-in; false in production, where it is always on).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/warehouses/fulfillment-dispatch-settings","host":["{{baseUrl}}"],"path":["api","warehouses","fulfillment-dispatch-settings"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"name\": \"Main Warehouse\",\n      \"order_fulfillment\": \"manual\",\n      \"auto_dispatch_enabled\": false,\n      \"partial_dispatch_enabled\": false,\n      \"type\": \"direct\",\n      \"integration_instance_id\": null,\n      \"integration_name\": null,\n      \"integration_sub_type\": null\n    },\n    {\n      \"id\": 5,\n      \"name\": \"West Coast 3PL\",\n      \"order_fulfillment\": \"shipstation\",\n      \"auto_dispatch_enabled\": true,\n      \"partial_dispatch_enabled\": true,\n      \"type\": \"3pl\",\n      \"integration_instance_id\": 9,\n      \"integration_name\": \"ShipStation\",\n      \"integration_sub_type\": \"shipping_provider\"\n    }\n  ],\n  \"shippingProviders\": [\n    \"manual\",\n    \"shipstation\"\n  ],\n  \"shippingProviderDispatchEnabled\": true,\n  \"shippingProviderDispatchEditable\": false\n}"}]},{"name":"Bulk Update Warehouse Dispatch Settings","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/bulk-update","host":["{{baseUrl}}"],"path":["api","warehouses","bulk-update"]},"description":"Updates fulfillment dispatch settings for multiple warehouses in one request.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach entry requires id, auto_dispatch_enabled and partial_dispatch_enabled; order_fulfillment (the fulfillment channel, \"manual\" or a connected shipping-provider name) is optional.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"warehouses\": [\n    {\n      \"id\": 12,\n      \"auto_dispatch_enabled\": true,\n      \"partial_dispatch_enabled\": false,\n      \"order_fulfillment\": \"shipstation\"\n    },\n    {\n      \"id\": 5,\n      \"auto_dispatch_enabled\": true,\n      \"partial_dispatch_enabled\": true,\n      \"order_fulfillment\": \"shipstation\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/warehouses/bulk-update","host":["{{baseUrl}}"],"path":["api","warehouses","bulk-update"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Success\"\n}"}]},{"name":"List Product Warehouse Locations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/product-warehouse-locations?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","v2","product-warehouse-locations"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default: 10)","disabled":true}]},"description":"Paginated preferred-location (bin) assignments for products, with the location, warehouse and product relations loaded for display. Filter by product_id + warehouse_id to fetch a single product's ordered bin list.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nScope: warehouses (read/write).\n\nAvailable sorts: id, created_at, is_default, priority (default: -is_default, then priority, then id — i.e. resolution order)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/product-warehouse-locations?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","v2","product-warehouse-locations"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default: 10)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 7,\n      \"product_id\": 42,\n      \"warehouse_id\": 1,\n      \"warehouse_location_id\": 12,\n      \"is_default\": true,\n      \"priority\": 0,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"product\": {\n        \"id\": 42,\n        \"sku\": \"WIDGET-001\",\n        \"name\": \"Premium Widget\"\n      },\n      \"warehouse_location\": {\n        \"id\": 12,\n        \"code\": \"A1-B2-S3-BIN4\",\n        \"name\": \"Aisle 1 Bay 2 Shelf 3 Bin 4\",\n        \"type\": \"storage\",\n        \"type_label\": \"Storage\",\n        \"display_name\": \"A1-B2-S3-BIN4 — Aisle 1 Bay 2 Shelf 3 Bin 4\",\n        \"deprecated_at\": null\n      },\n      \"created_at\": \"2026-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2026-01-15T10:00:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Create Product Warehouse Location","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/product-warehouse-locations","host":["{{baseUrl}}"],"path":["api","v2","product-warehouse-locations"]},"description":"Assign a preferred storage bin to a product in a warehouse. The location must be an active put-away destination. The first assignment for a product+warehouse automatically becomes the default; new assignments append to the end of the ordered list unless `priority` is given.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody fields:\n- product_id (required) — product to assign.\n- warehouse_id (required) — warehouse the bin lives in.\n- warehouse_location_id (required) — the bin; must belong to the warehouse and be a put-away destination.\n- is_default (optional, boolean) — mark as the product+warehouse default.\n- priority (optional, integer ≥ 0) — explicit position in the ordered list; omitted assignments append to the end.\n\nAuthentication: Requires Bearer token.\nScope: warehouses (read/write). Requires the warehouses.manage_locations permission.","body":{"mode":"raw","raw":"{\n  \"product_id\": 42,\n  \"warehouse_id\": 1,\n  \"warehouse_location_id\": 12,\n  \"is_default\": true,\n  \"priority\": 2\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/product-warehouse-locations","host":["{{baseUrl}}"],"path":["api","v2","product-warehouse-locations"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 7,\n    \"product_id\": 42,\n    \"warehouse_id\": 1,\n    \"warehouse_location_id\": 12,\n    \"is_default\": true,\n    \"priority\": 0,\n    \"warehouse_name\": \"Main Warehouse\",\n    \"product\": {\n      \"id\": 42,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Premium Widget\"\n    },\n    \"warehouse_location\": {\n      \"id\": 12,\n      \"code\": \"A1-B2-S3-BIN4\",\n      \"name\": \"Aisle 1 Bay 2 Shelf 3 Bin 4\",\n      \"type\": \"storage\",\n      \"type_label\": \"Storage\",\n      \"display_name\": \"A1-B2-S3-BIN4 — Aisle 1 Bay 2 Shelf 3 Bin 4\",\n      \"deprecated_at\": null\n    },\n    \"created_at\": \"2026-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2026-01-15T10:00:00.000000Z\"\n  },\n  \"message\": \"Preferred location assigned.\"\n}"}]},{"name":"Update Product Warehouse Location","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/product-warehouse-locations/{productWarehouseLocation}","host":["{{baseUrl}}"],"path":["api","v2","product-warehouse-locations",":productWarehouseLocation"],"variable":[{"key":"productWarehouseLocation","value":"7","description":"Product warehouse location (preferred bin) ID"}]},"description":"Update a preferred-bin assignment. All fields optional (`sometimes`).\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nScope: warehouses (read/write).\n\nBody fields (all optional):\n- warehouse_location_id (integer, exists, must be a put-away destination (is_putaway_destination enabled), unique per product+warehouse)\n- is_default (boolean)\n\n**Requires permission:** `warehouses.manage_locations`","body":{"mode":"raw","raw":"{\n  \"warehouse_location_id\": 13,\n  \"is_default\": true,\n  \"priority\": 1\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/product-warehouse-locations/{productWarehouseLocation}","host":["{{baseUrl}}"],"path":["api","v2","product-warehouse-locations",":productWarehouseLocation"],"variable":[{"key":"productWarehouseLocation","value":"7","description":"Product warehouse location (preferred bin) ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 7,\n    \"product_id\": 42,\n    \"warehouse_id\": 1,\n    \"warehouse_location_id\": 12,\n    \"is_default\": true,\n    \"priority\": 0,\n    \"warehouse_name\": \"Main Warehouse\",\n    \"product\": {\n      \"id\": 42,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Premium Widget\"\n    },\n    \"warehouse_location\": {\n      \"id\": 12,\n      \"code\": \"A1-B2-S3-BIN4\",\n      \"name\": \"Aisle 1 Bay 2 Shelf 3 Bin 4\",\n      \"type\": \"storage\",\n      \"type_label\": \"Storage\",\n      \"display_name\": \"A1-B2-S3-BIN4 — Aisle 1 Bay 2 Shelf 3 Bin 4\",\n      \"deprecated_at\": null\n    },\n    \"created_at\": \"2026-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2026-01-15T10:00:00.000000Z\"\n  },\n  \"message\": \"Preferred location updated.\"\n}"}]},{"name":"Delete Product Warehouse Location","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/product-warehouse-locations/{productWarehouseLocation}","host":["{{baseUrl}}"],"path":["api","v2","product-warehouse-locations",":productWarehouseLocation"],"variable":[{"key":"productWarehouseLocation","value":"7","description":"Product warehouse location (preferred bin) ID"}]},"description":"Remove a preferred-bin assignment.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nScope: warehouses (read/write).\n\n**Requires permission:** `warehouses.manage_locations`"},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/product-warehouse-locations/{productWarehouseLocation}","host":["{{baseUrl}}"],"path":["api","v2","product-warehouse-locations",":productWarehouseLocation"],"variable":[{"key":"productWarehouseLocation","value":"7","description":"Product warehouse location (preferred bin) ID"}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":""}]},{"name":"Set Default Preferred Bin","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/v2/product-warehouse-locations/{productWarehouseLocation}/set-default","host":["{{baseUrl}}"],"path":["api","v2","product-warehouse-locations",":productWarehouseLocation","set-default"],"variable":[{"key":"productWarehouseLocation","value":"7","description":"Product warehouse location (preferred bin) ID"}]},"description":"Promote a preferred-bin assignment to the product+warehouse default. The swap is atomic — the previous default for the same product+warehouse is unset.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nScope: warehouses (read/write).\n\n**Requires permission:** `warehouses.manage_locations`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/product-warehouse-locations/{productWarehouseLocation}/set-default","host":["{{baseUrl}}"],"path":["api","v2","product-warehouse-locations",":productWarehouseLocation","set-default"],"variable":[{"key":"productWarehouseLocation","value":"7","description":"Product warehouse location (preferred bin) ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 7,\n    \"product_id\": 42,\n    \"warehouse_id\": 1,\n    \"warehouse_location_id\": 12,\n    \"is_default\": true,\n    \"priority\": 0,\n    \"warehouse_name\": \"Main Warehouse\",\n    \"product\": {\n      \"id\": 42,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Premium Widget\"\n    },\n    \"warehouse_location\": {\n      \"id\": 12,\n      \"code\": \"A1-B2-S3-BIN4\",\n      \"name\": \"Aisle 1 Bay 2 Shelf 3 Bin 4\",\n      \"type\": \"storage\",\n      \"type_label\": \"Storage\",\n      \"display_name\": \"A1-B2-S3-BIN4 — Aisle 1 Bay 2 Shelf 3 Bin 4\",\n      \"deprecated_at\": null\n    },\n    \"created_at\": \"2026-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2026-01-15T10:00:00.000000Z\"\n  },\n  \"message\": \"Default preferred location updated.\"\n}"}]},{"name":"Reorder Preferred Bins","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/product-warehouse-locations/reorder","host":["{{baseUrl}}"],"path":["api","v2","product-warehouse-locations","reorder"]},"description":"Reorder a product's preferred bins in a warehouse to match the given id order (top to bottom) and return the fresh ordered list. Priorities are rewritten 0-based and the new top bin becomes the default, so the ordered list matches the resolution order used by put-away and receiving.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody fields:\n- product_id (required) — product whose bins are reordered.\n- warehouse_id (required) — warehouse the bins live in.\n- ids (required, array of integers) — assignment ids in the desired order; must be exactly the product+warehouse's current assignments.\n\nAuthentication: Requires Bearer token.\nScope: warehouses (read/write). Requires the warehouses.manage_locations permission.","body":{"mode":"raw","raw":"{\n  \"product_id\": 42,\n  \"warehouse_id\": 1,\n  \"ids\": [\n    9,\n    7,\n    8\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/product-warehouse-locations/reorder","host":["{{baseUrl}}"],"path":["api","v2","product-warehouse-locations","reorder"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 9,\n      \"product_id\": 42,\n      \"warehouse_id\": 1,\n      \"warehouse_location_id\": 15,\n      \"is_default\": true,\n      \"priority\": 0,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"product\": {\n        \"id\": 42,\n        \"sku\": \"WIDGET-001\",\n        \"name\": \"Premium Widget\"\n      },\n      \"warehouse_location\": {\n        \"id\": 15,\n        \"code\": \"C1-B1-S1-BIN1\",\n        \"name\": null,\n        \"type\": \"storage\",\n        \"type_label\": \"Storage\",\n        \"display_name\": \"C1-B1-S1-BIN1\",\n        \"deprecated_at\": null\n      },\n      \"created_at\": \"2026-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2026-01-15T10:00:00.000000Z\"\n    },\n    {\n      \"id\": 7,\n      \"product_id\": 42,\n      \"warehouse_id\": 1,\n      \"warehouse_location_id\": 12,\n      \"is_default\": false,\n      \"priority\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"product\": {\n        \"id\": 42,\n        \"sku\": \"WIDGET-001\",\n        \"name\": \"Premium Widget\"\n      },\n      \"warehouse_location\": {\n        \"id\": 12,\n        \"code\": \"A1-B2-S3-BIN4\",\n        \"name\": null,\n        \"type\": \"storage\",\n        \"type_label\": \"Storage\",\n        \"display_name\": \"A1-B2-S3-BIN4\",\n        \"deprecated_at\": null\n      },\n      \"created_at\": \"2026-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2026-01-15T10:00:00.000000Z\"\n    },\n    {\n      \"id\": 8,\n      \"product_id\": 42,\n      \"warehouse_id\": 1,\n      \"warehouse_location_id\": 13,\n      \"is_default\": false,\n      \"priority\": 2,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"product\": {\n        \"id\": 42,\n        \"sku\": \"WIDGET-001\",\n        \"name\": \"Premium Widget\"\n      },\n      \"warehouse_location\": {\n        \"id\": 13,\n        \"code\": \"B1-B1-S1-BIN1\",\n        \"name\": null,\n        \"type\": \"storage\",\n        \"type_label\": \"Storage\",\n        \"display_name\": \"B1-B1-S1-BIN1\",\n        \"deprecated_at\": null\n      },\n      \"created_at\": \"2026-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2026-01-15T10:00:00.000000Z\"\n    }\n  ],\n  \"message\": \"Preferred bins reordered.\"\n}"}]},{"name":"List LPNs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/lpns","host":["{{baseUrl}}"],"path":["api","v2","lpns"]},"description":"Paginated pallets/totes (LPNs) with their bin and content count.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nFilters: filter[status] (open|closed), filter[warehouse_id], filter[warehouse_location_id], filter[search] (number or barcode). Sorts: id, number, status, created_at. Pagination: page, per_page (default 10)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/lpns","host":["{{baseUrl}}"],"path":["api","v2","lpns"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3,\n      \"number\": \"LPN-000003\",\n      \"barcode\": \"PLT-123\",\n      \"warehouse_id\": 1,\n      \"warehouse\": {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      },\n      \"warehouse_location_id\": 15,\n      \"location\": {\n        \"id\": 15,\n        \"code\": \"ST-01\",\n        \"name\": \"Storage 1\"\n      },\n      \"status\": \"open\",\n      \"status_label\": \"Open\",\n      \"notes\": null,\n      \"contents_count\": 1,\n      \"created_at\": \"2026-07-10T09:00:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1\n}"}]},{"name":"Create LPN","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/lpns","host":["{{baseUrl}}"],"path":["api","v2","lpns"]},"description":"Create an open LPN (pallet/tote), optionally placed in a bin.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Only on warehouses with locations enabled.","body":{"mode":"raw","raw":"{\n  \"warehouse_id\": 1,\n  \"warehouse_location_id\": 15,\n  \"barcode\": \"PLT-123\",\n  \"notes\": \"Pallet A\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/lpns","host":["{{baseUrl}}"],"path":["api","v2","lpns"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"number\": \"LPN-000003\",\n    \"barcode\": \"PLT-123\",\n    \"warehouse_id\": 1,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"warehouse_location_id\": 15,\n    \"location\": {\n      \"id\": 15,\n      \"code\": \"ST-01\",\n      \"name\": \"Storage 1\"\n    },\n    \"status\": \"open\",\n    \"status_label\": \"Open\",\n    \"notes\": null,\n    \"contents_count\": 1,\n    \"contents\": [\n      {\n        \"id\": 8,\n        \"product_id\": 42,\n        \"sku\": \"WIDGET-1\",\n        \"name\": \"Blue Widget\",\n        \"quantity\": 20\n      }\n    ],\n    \"created_at\": \"2026-07-10T09:00:00.000000Z\"\n  },\n  \"message\": \"LPN created.\"\n}"}]},{"name":"Get LPN","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/lpns/{warehouseLpn}","host":["{{baseUrl}}"],"path":["api","v2","lpns",":warehouseLpn"],"variable":[{"key":"warehouseLpn","value":"","description":"The warehouse lpn ID."}]},"description":"A single LPN with its full content manifest.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/lpns/{warehouseLpn}","host":["{{baseUrl}}"],"path":["api","v2","lpns",":warehouseLpn"],"variable":[{"key":"warehouseLpn","value":"","description":"The warehouse lpn ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"number\": \"LPN-000003\",\n    \"barcode\": \"PLT-123\",\n    \"warehouse_id\": 1,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"warehouse_location_id\": 15,\n    \"location\": {\n      \"id\": 15,\n      \"code\": \"ST-01\",\n      \"name\": \"Storage 1\"\n    },\n    \"status\": \"open\",\n    \"status_label\": \"Open\",\n    \"notes\": null,\n    \"contents_count\": 1,\n    \"contents\": [\n      {\n        \"id\": 8,\n        \"product_id\": 42,\n        \"sku\": \"WIDGET-1\",\n        \"name\": \"Blue Widget\",\n        \"quantity\": 20\n      }\n    ],\n    \"created_at\": \"2026-07-10T09:00:00.000000Z\"\n  }\n}"}]},{"name":"Add Stock to LPN","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/lpns/{warehouseLpn}/contents","host":["{{baseUrl}}"],"path":["api","v2","lpns",":warehouseLpn","contents"],"variable":[{"key":"warehouseLpn","value":"","description":"The warehouse lpn ID."}]},"description":"Nest stock onto an open LPN. The LPN's bin must physically hold at least the resulting quantity of the product.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"product_id\": 42,\n  \"quantity\": 20\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/lpns/{warehouseLpn}/contents","host":["{{baseUrl}}"],"path":["api","v2","lpns",":warehouseLpn","contents"],"variable":[{"key":"warehouseLpn","value":"","description":"The warehouse lpn ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"number\": \"LPN-000003\",\n    \"barcode\": \"PLT-123\",\n    \"warehouse_id\": 1,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"warehouse_location_id\": 15,\n    \"location\": {\n      \"id\": 15,\n      \"code\": \"ST-01\",\n      \"name\": \"Storage 1\"\n    },\n    \"status\": \"open\",\n    \"status_label\": \"Open\",\n    \"notes\": null,\n    \"contents_count\": 1,\n    \"contents\": [\n      {\n        \"id\": 8,\n        \"product_id\": 42,\n        \"sku\": \"WIDGET-1\",\n        \"name\": \"Blue Widget\",\n        \"quantity\": 20\n      }\n    ],\n    \"created_at\": \"2026-07-10T09:00:00.000000Z\"\n  },\n  \"message\": \"Stock added to the LPN.\"\n}"}]},{"name":"Bulk Import Stock to LPN","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/lpns/{warehouseLpn}/contents/bulk","host":["{{baseUrl}}"],"path":["api","v2","lpns",":warehouseLpn","contents","bulk"],"variable":[{"key":"warehouseLpn","value":"","description":"The warehouse lpn ID."}]},"description":"Nest many products onto an open LPN in one call. Each item is matched by SKU (or barcode) or product id, aggregated per product, and checked against the on-hand quantity in the LPN's bin. Returns a per-row report (status: ok, insufficient, not_found, invalid) plus a summary. Set dry_run=true to validate without persisting; dry_run=false nests every valid product in a single transaction and returns the updated LPN. Items referencing the same product are combined.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"dry_run\": false,\n  \"items\": [\n    {\n      \"sku\": \"WIDGET-1\",\n      \"quantity\": 20\n    },\n    {\n      \"sku\": \"WIDGET-2\",\n      \"quantity\": 5\n    },\n    {\n      \"product_id\": 99,\n      \"quantity\": 12\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/lpns/{warehouseLpn}/contents/bulk","host":["{{baseUrl}}"],"path":["api","v2","lpns",":warehouseLpn","contents","bulk"],"variable":[{"key":"warehouseLpn","value":"","description":"The warehouse lpn ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"results\": [\n    {\n      \"row\": 1,\n      \"input\": \"WIDGET-1\",\n      \"quantity\": 20,\n      \"product_id\": 42,\n      \"sku\": \"WIDGET-1\",\n      \"name\": \"Blue Widget\",\n      \"status\": \"ok\",\n      \"message\": null\n    },\n    {\n      \"row\": 2,\n      \"input\": \"WIDGET-2\",\n      \"quantity\": 5,\n      \"product_id\": 43,\n      \"sku\": \"WIDGET-2\",\n      \"name\": \"Red Widget\",\n      \"status\": \"insufficient\",\n      \"message\": \"The bin holds 2 of this product — not enough to nest 5.\"\n    },\n    {\n      \"row\": 3,\n      \"input\": \"NOPE-1\",\n      \"quantity\": 1,\n      \"product_id\": null,\n      \"sku\": null,\n      \"name\": null,\n      \"status\": \"not_found\",\n      \"message\": \"No product matches this SKU or barcode.\"\n    }\n  ],\n  \"summary\": {\n    \"ok\": 1,\n    \"not_found\": 1,\n    \"insufficient\": 1,\n    \"invalid\": 0,\n    \"units\": 20\n  },\n  \"lpn\": null,\n  \"message\": \"Validated import.\"\n}"}]},{"name":"Move LPN","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/lpns/{warehouseLpn}/move","host":["{{baseUrl}}"],"path":["api","v2","lpns",":warehouseLpn","move"],"variable":[{"key":"warehouseLpn","value":"","description":"The warehouse lpn ID."}]},"description":"Move the whole LPN to another bin — relocating every content line's stock in one transfer.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"warehouse_location_id\": 16\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/lpns/{warehouseLpn}/move","host":["{{baseUrl}}"],"path":["api","v2","lpns",":warehouseLpn","move"],"variable":[{"key":"warehouseLpn","value":"","description":"The warehouse lpn ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"number\": \"LPN-000003\",\n    \"barcode\": \"PLT-123\",\n    \"warehouse_id\": 1,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"warehouse_location_id\": 16,\n    \"location\": {\n      \"id\": 16,\n      \"code\": \"ST-02\",\n      \"name\": \"Storage 2\"\n    },\n    \"status\": \"open\",\n    \"status_label\": \"Open\",\n    \"notes\": null,\n    \"contents_count\": 1,\n    \"contents\": [\n      {\n        \"id\": 8,\n        \"product_id\": 42,\n        \"sku\": \"WIDGET-1\",\n        \"name\": \"Blue Widget\",\n        \"quantity\": 20\n      }\n    ],\n    \"created_at\": \"2026-07-10T09:00:00.000000Z\"\n  },\n  \"message\": \"LPN moved.\"\n}"}]},{"name":"Close LPN","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/v2/lpns/{warehouseLpn}/close","host":["{{baseUrl}}"],"path":["api","v2","lpns",":warehouseLpn","close"],"variable":[{"key":"warehouseLpn","value":"","description":"The warehouse lpn ID."}]},"description":"Close an LPN — it can no longer take stock or move.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/lpns/{warehouseLpn}/close","host":["{{baseUrl}}"],"path":["api","v2","lpns",":warehouseLpn","close"],"variable":[{"key":"warehouseLpn","value":"","description":"The warehouse lpn ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"number\": \"LPN-000003\",\n    \"barcode\": \"PLT-123\",\n    \"warehouse_id\": 1,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"warehouse_location_id\": 15,\n    \"location\": {\n      \"id\": 15,\n      \"code\": \"ST-01\",\n      \"name\": \"Storage 1\"\n    },\n    \"status\": \"closed\",\n    \"status_label\": \"Closed\",\n    \"notes\": null,\n    \"contents_count\": 1,\n    \"contents\": [\n      {\n        \"id\": 8,\n        \"product_id\": 42,\n        \"sku\": \"WIDGET-1\",\n        \"name\": \"Blue Widget\",\n        \"quantity\": 20\n      }\n    ],\n    \"created_at\": \"2026-07-10T09:00:00.000000Z\"\n  },\n  \"message\": \"LPN closed.\"\n}"}]},{"name":"List Warehouse Locations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default: 10)","disabled":true}]},"description":"Paginated warehouse locations (bins) with the cached per-location on-hand units (`on_hand`). Deprecated locations are hidden unless `include_deprecated` (top-level or inside `filter[]`) is truthy.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nScope: warehouses (read/write).\nRequires the tenant to have warehouse locations enabled (`warehouse.locations` middleware).\n\nLocation types: staging, primary_pick, reserve, storage, shipping, returns, quarantine, damaged.\nAvailable sorts: code (default), type, created_at (prefix with - for descending)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default: 10)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"warehouse_id\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"name\": \"Aisle 1 Bay 2 Shelf 3 Bin 4\",\n      \"code\": \"A1-B2-S3-BIN4\",\n      \"zone\": \"A\",\n      \"aisle\": \"1\",\n      \"bay\": \"2\",\n      \"shelf\": \"3\",\n      \"bin\": \"4\",\n      \"type\": \"storage\",\n      \"type_label\": \"Storage\",\n      \"is_receivable\": true,\n      \"is_pickable\": true,\n      \"is_putaway_source\": false,\n      \"is_putaway_destination\": true,\n      \"barcode\": \"LOC-A1-B2-S3-BIN4\",\n      \"sort_order\": 100,\n      \"is_default\": false,\n      \"deprecated_at\": null,\n      \"display_name\": \"A1-B2-S3-BIN4 — Aisle 1 Bay 2 Shelf 3 Bin 4\",\n      \"on_hand\": 42,\n      \"created_at\": \"2026-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2026-01-15T10:00:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Create Warehouse Location","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations"]},"description":"Create a single warehouse location (bin). Provide either an explicit `code` or at least one of the segment fields (`zone`, `aisle`, `bay`, `shelf`, `bin`) from which a code is derived.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nScope: warehouses (read/write).\n\nBody fields:\n- warehouse_id (required, integer, exists) — parent warehouse\n- code (string max 64, required_without_all:zone,aisle,bay,shelf,bin)\n- name (nullable, string max 255)\n- zone (nullable, string max 32)\n- aisle / bay / shelf / bin (nullable, string max 255)\n- type (required, enum: staging, primary_pick, reserve, storage, shipping, returns, quarantine, damaged)\n- is_receivable (optional, boolean) — bin can receive stock on inbound receipts\n- is_pickable (optional, boolean) — bin is eligible for order picking\n- is_putaway_source (optional, boolean) — stock can be drawn from this bin during put-away\n- is_putaway_destination (optional, boolean) — stock can be put away into this bin\n  When a capability flag is omitted it is seeded from the location `type` defaults; once set, receiving, picking and put-away routing follow the flags, not the type.\n- barcode (nullable, string max 64)\n- sort_order (nullable, integer min 0)\n- is_default (boolean)\n\nReturns 201 with the created location; 422 if the tenant does not have locations enabled or the code/barcode conflicts.\n\n**Requires permission:** `warehouses.manage_locations`","body":{"mode":"raw","raw":"{\n  \"warehouse_id\": 1,\n  \"code\": \"A1-B2-S3-BIN4\",\n  \"name\": \"Aisle 1 Bay 2 Shelf 3 Bin 4\",\n  \"zone\": \"A\",\n  \"aisle\": \"1\",\n  \"bay\": \"2\",\n  \"shelf\": \"3\",\n  \"bin\": \"4\",\n  \"type\": \"storage\",\n  \"is_receivable\": true,\n  \"is_pickable\": true,\n  \"is_putaway_source\": false,\n  \"is_putaway_destination\": true,\n  \"barcode\": \"LOC-A1-B2-S3-BIN4\",\n  \"sort_order\": 100,\n  \"is_default\": false,\n  \"is_sellable\": true,\n  \"capacity_qty\": 100,\n  \"abc_class\": \"A\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"warehouse_id\": 1,\n    \"warehouse_name\": \"Main Warehouse\",\n    \"name\": \"Aisle 1 Bay 2 Shelf 3 Bin 4\",\n    \"code\": \"A1-B2-S3-BIN4\",\n    \"zone\": \"A\",\n    \"aisle\": \"1\",\n    \"bay\": \"2\",\n    \"shelf\": \"3\",\n    \"bin\": \"4\",\n    \"type\": \"storage\",\n    \"type_label\": \"Storage\",\n    \"is_receivable\": true,\n    \"is_pickable\": true,\n    \"is_putaway_source\": false,\n    \"is_putaway_destination\": true,\n    \"barcode\": \"LOC-A1-B2-S3-BIN4\",\n    \"sort_order\": 100,\n    \"is_default\": false,\n    \"deprecated_at\": null,\n    \"display_name\": \"A1-B2-S3-BIN4 — Aisle 1 Bay 2 Shelf 3 Bin 4\",\n    \"on_hand\": 42,\n    \"created_at\": \"2026-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2026-01-15T10:00:00.000000Z\"\n  },\n  \"message\": \"Location created successfully.\"\n}"}]},{"name":"Bulk Generate Warehouse Locations","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/bulk-generate","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations","bulk-generate"]},"description":"Generate a range of locations from segment patterns. With `preview: true` the response returns `{codes_sample, total, conflicts}` without persisting; otherwise the non-conflicting codes are inserted and the response returns `{created, skipped_codes}`.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nScope: warehouses (read/write).\n\nBody fields:\n- warehouse_id (required, integer, exists)\n- type (required, enum: staging, primary_pick, reserve, storage, shipping, returns, quarantine, damaged)\n- Generated bins seed their capability flags (is_receivable, is_pickable, is_putaway_source, is_putaway_destination) from the chosen `type`; adjust per bin afterwards via Update.\n- zone (nullable, string max 32)\n- segments (required array, 1-6 items). Each segment:\n    - kind (required, one of: literal | letters | numbers)\n    - value (required when kind=literal, string max 32)\n    - from / to (required unless kind=literal) — range bounds\n    - pad (boolean) — zero/left pad range values to equal width\n- preview (boolean) — dry-run without persisting\n\n**Requires permission:** `warehouses.manage_locations`","body":{"mode":"raw","raw":"{\n  \"warehouse_id\": 1,\n  \"type\": \"storage\",\n  \"zone\": \"A\",\n  \"segments\": [\n    {\n      \"kind\": \"literal\",\n      \"value\": \"A\"\n    },\n    {\n      \"kind\": \"numbers\",\n      \"from\": 1,\n      \"to\": 10,\n      \"pad\": true\n    },\n    {\n      \"kind\": \"letters\",\n      \"from\": \"A\",\n      \"to\": \"C\"\n    }\n  ],\n  \"preview\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/bulk-generate","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations","bulk-generate"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"codes_sample\": [\n      \"A-01-A\",\n      \"A-01-B\",\n      \"A-01-C\",\n      \"A-02-A\"\n    ],\n    \"total\": 30,\n    \"conflicts\": [\n      \"A-01-A\"\n    ]\n  }\n}"}]},{"name":"Import Warehouse Locations","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/import","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations","import"]},"description":"CSV import of locations, upserting by the (warehouse, code) natural key. Small files run synchronously and return `{created, updated, skipped}`; large files are queued and return `{queued: true, tracked_job_log_id}` for the job list.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nScope: warehouses (read/write).\n\nBody (multipart/form-data):\n- warehouse_id (required, integer, exists)\n- file (required, CSV/TXT upload, max 10 MB)\n\n**Requires permission:** `warehouses.manage_locations`","body":{"mode":"formdata","formdata":[{"key":"warehouse_id","type":"text","value":""},{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/import","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations","import"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"queued\": false,\n    \"created\": 12,\n    \"updated\": 3,\n    \"skipped\": []\n  },\n  \"message\": \"Imported 12 new, updated 3.\"\n}"}]},{"name":"Generate Location Labels","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/labels","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations","labels"]},"description":"Generate a printable labels PDF (Code-128 barcode + code + type) for the selected locations. Returns a PDF/HTML label sheet as a binary download, NOT JSON.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nScope: warehouses (read/write).\n\nBody fields:\n- ids (required array, 1-500 items) — warehouse_location IDs to print","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    12,\n    13,\n    14\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/labels","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations","labels"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"[Binary PDF label sheet download]"}]},{"name":"Get Warehouse Location","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/{warehouseLocation}","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations",":warehouseLocation"],"variable":[{"key":"warehouseLocation","value":"12","description":"Warehouse location ID"}]},"description":"Single warehouse location with its warehouse and the cached on-hand summary (`on_hand`).\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nScope: warehouses (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/{warehouseLocation}","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations",":warehouseLocation"],"variable":[{"key":"warehouseLocation","value":"12","description":"Warehouse location ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"warehouse_id\": 1,\n    \"warehouse_name\": \"Main Warehouse\",\n    \"name\": \"Aisle 1 Bay 2 Shelf 3 Bin 4\",\n    \"code\": \"A1-B2-S3-BIN4\",\n    \"zone\": \"A\",\n    \"aisle\": \"1\",\n    \"bay\": \"2\",\n    \"shelf\": \"3\",\n    \"bin\": \"4\",\n    \"type\": \"storage\",\n    \"type_label\": \"Storage\",\n    \"is_receivable\": true,\n    \"is_pickable\": true,\n    \"is_putaway_source\": false,\n    \"is_putaway_destination\": true,\n    \"barcode\": \"LOC-A1-B2-S3-BIN4\",\n    \"sort_order\": 100,\n    \"is_default\": false,\n    \"deprecated_at\": null,\n    \"display_name\": \"A1-B2-S3-BIN4 — Aisle 1 Bay 2 Shelf 3 Bin 4\",\n    \"on_hand\": 42,\n    \"created_at\": \"2026-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2026-01-15T10:00:00.000000Z\"\n  }\n}"}]},{"name":"Update Warehouse Location","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/{warehouseLocation}","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations",":warehouseLocation"],"variable":[{"key":"warehouseLocation","value":"12","description":"Warehouse location ID"}]},"description":"Update a warehouse location. All fields are optional (`sometimes`); only provided fields are changed.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nScope: warehouses (read/write).\n\nBody fields (all optional):\n- code (nullable, string max 64)\n- name (nullable, string max 255)\n- zone (nullable, string max 32)\n- aisle / bay / shelf / bin (nullable, string max 255)\n- type (enum: staging, primary_pick, reserve, storage, shipping, returns, quarantine, damaged)\n- is_receivable (optional, boolean) — bin can receive stock on inbound receipts\n- is_pickable (optional, boolean) — bin is eligible for order picking\n- is_putaway_source (optional, boolean) — stock can be drawn from this bin during put-away\n- is_putaway_destination (optional, boolean) — stock can be put away into this bin\n  When a capability flag is omitted it is seeded from the location `type` defaults; once set, receiving, picking and put-away routing follow the flags, not the type.\n- barcode (nullable, string max 64)\n- sort_order (nullable, integer min 0)\n- is_default (boolean)\n\n**Requires permission:** `warehouses.manage_locations`","body":{"mode":"raw","raw":"{\n  \"name\": \"Aisle 1 Bay 2 Shelf 3 Bin 4 (renamed)\",\n  \"sort_order\": 120,\n  \"is_pickable\": false,\n  \"is_default\": true,\n  \"is_sellable\": true,\n  \"capacity_qty\": 100,\n  \"abc_class\": \"A\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/{warehouseLocation}","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations",":warehouseLocation"],"variable":[{"key":"warehouseLocation","value":"12","description":"Warehouse location ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"warehouse_id\": 1,\n    \"warehouse_name\": \"Main Warehouse\",\n    \"name\": \"Aisle 1 Bay 2 Shelf 3 Bin 4\",\n    \"code\": \"A1-B2-S3-BIN4\",\n    \"zone\": \"A\",\n    \"aisle\": \"1\",\n    \"bay\": \"2\",\n    \"shelf\": \"3\",\n    \"bin\": \"4\",\n    \"type\": \"storage\",\n    \"type_label\": \"Storage\",\n    \"is_receivable\": true,\n    \"is_pickable\": true,\n    \"is_putaway_source\": false,\n    \"is_putaway_destination\": true,\n    \"barcode\": \"LOC-A1-B2-S3-BIN4\",\n    \"sort_order\": 100,\n    \"is_default\": false,\n    \"deprecated_at\": null,\n    \"display_name\": \"A1-B2-S3-BIN4 — Aisle 1 Bay 2 Shelf 3 Bin 4\",\n    \"on_hand\": 42,\n    \"created_at\": \"2026-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2026-01-15T10:00:00.000000Z\"\n  },\n  \"message\": \"Location updated successfully.\"\n}"}]},{"name":"Delete Warehouse Location","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/{warehouseLocation}","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations",":warehouseLocation"],"variable":[{"key":"warehouseLocation","value":"12","description":"Warehouse location ID"}]},"description":"Hard delete a warehouse location. Only allowed when the location has never been referenced by an inventory movement; otherwise a 422 is returned suggesting deprecation instead.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nScope: warehouses (read/write).\n\n**Requires permission:** `warehouses.manage_locations`"},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/{warehouseLocation}","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations",":warehouseLocation"],"variable":[{"key":"warehouseLocation","value":"12","description":"Warehouse location ID"}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":""}]},{"name":"Deprecate Warehouse Location","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/{warehouseLocation}/deprecate","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations",":warehouseLocation","deprecate"],"variable":[{"key":"warehouseLocation","value":"12","description":"Warehouse location ID"}]},"description":"Deprecate (soft-retire) a location so it is hidden from pickers and datatables but retains its history. Returns 422 if it is the last active storage location in the warehouse.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nScope: warehouses (read/write).\n\n**Requires permission:** `warehouses.manage_locations`"},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/{warehouseLocation}/deprecate","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations",":warehouseLocation","deprecate"],"variable":[{"key":"warehouseLocation","value":"12","description":"Warehouse location ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"warehouse_id\": 1,\n    \"warehouse_name\": \"Main Warehouse\",\n    \"name\": \"Aisle 1 Bay 2 Shelf 3 Bin 4\",\n    \"code\": \"A1-B2-S3-BIN4\",\n    \"zone\": \"A\",\n    \"aisle\": \"1\",\n    \"bay\": \"2\",\n    \"shelf\": \"3\",\n    \"bin\": \"4\",\n    \"type\": \"storage\",\n    \"type_label\": \"Storage\",\n    \"barcode\": \"LOC-A1-B2-S3-BIN4\",\n    \"sort_order\": 100,\n    \"is_default\": false,\n    \"deprecated_at\": \"2026-01-20T12:00:00.000000Z\",\n    \"display_name\": \"A1-B2-S3-BIN4 — Aisle 1 Bay 2 Shelf 3 Bin 4\",\n    \"on_hand\": 42,\n    \"created_at\": \"2026-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2026-01-15T10:00:00.000000Z\"\n  },\n  \"message\": \"Location deprecated.\"\n}"}]},{"name":"Restore Warehouse Location","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/{warehouseLocation}/restore","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations",":warehouseLocation","restore"],"variable":[{"key":"warehouseLocation","value":"12","description":"Warehouse location ID"}]},"description":"Restore a previously deprecated location (clears `deprecated_at`).\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nScope: warehouses (read/write).\n\n**Requires permission:** `warehouses.manage_locations`"},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/{warehouseLocation}/restore","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations",":warehouseLocation","restore"],"variable":[{"key":"warehouseLocation","value":"12","description":"Warehouse location ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"warehouse_id\": 1,\n    \"warehouse_name\": \"Main Warehouse\",\n    \"name\": \"Aisle 1 Bay 2 Shelf 3 Bin 4\",\n    \"code\": \"A1-B2-S3-BIN4\",\n    \"zone\": \"A\",\n    \"aisle\": \"1\",\n    \"bay\": \"2\",\n    \"shelf\": \"3\",\n    \"bin\": \"4\",\n    \"type\": \"storage\",\n    \"type_label\": \"Storage\",\n    \"barcode\": \"LOC-A1-B2-S3-BIN4\",\n    \"sort_order\": 100,\n    \"is_default\": false,\n    \"deprecated_at\": null,\n    \"display_name\": \"A1-B2-S3-BIN4 — Aisle 1 Bay 2 Shelf 3 Bin 4\",\n    \"on_hand\": 42,\n    \"created_at\": \"2026-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2026-01-15T10:00:00.000000Z\"\n  },\n  \"message\": \"Location restored.\"\n}"}]},{"name":"Lookup Warehouse Locations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/lookup/warehouse-locations?warehouse_id=1","host":["{{baseUrl}}"],"path":["api","lookup","warehouse-locations"],"query":[{"key":"warehouse_id","value":"1","description":"Required. Warehouse to list locations for.","disabled":true}]},"description":"Decorated location options for select/autocomplete components. Returns active (non-deprecated) locations of a warehouse in pick-path order (sort_order nulls-last, then code). Returns an empty list when the warehouse does not have locations enabled.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nWhen `product_id` is supplied each option is decorated with the live `available_quantity` from the location-quantity cache and a `display_text` (\"CODE (Type) — N units\").\n\nAuthentication: Requires Bearer token.\n\nQuery params:\n- warehouse_id (required, integer, exists)\n- capability (optional: receivable | pickable | put-away-to | put-away-from) — filters types via the capability matrix\n- type / type[] (optional, comma-separated or array) — explicit type filter, intersected with capability\n- search (optional, string max 255) — matches code, name or barcode\n- product_id (optional, integer, exists) — decorate options with available_quantity + display_text\n- limit (optional, integer 1-500, default 50)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/lookup/warehouse-locations?warehouse_id=1","host":["{{baseUrl}}"],"path":["api","lookup","warehouse-locations"],"query":[{"key":"warehouse_id","value":"1","description":"Required. Warehouse to list locations for.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"code\": \"A1-B2-S3-BIN4\",\n      \"name\": \"Aisle 1 Bay 2 Shelf 3 Bin 4\",\n      \"type\": \"storage\",\n      \"type_label\": \"Storage\",\n      \"display_name\": \"A1-B2-S3-BIN4 — Aisle 1 Bay 2 Shelf 3 Bin 4\",\n      \"barcode\": \"LOC-A1-B2-S3-BIN4\",\n      \"is_default\": false,\n      \"available_quantity\": 42,\n      \"display_text\": \"A1-B2-S3-BIN4 (Storage) — 42 units\"\n    }\n  ]\n}"}]},{"name":"Get Slotting Suggestions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/slotting-suggestions?warehouse_id=1","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations","slotting-suggestions"],"query":[{"key":"warehouse_id","value":"1","description":"Warehouse to analyse (required).","disabled":true}]},"description":"Velocity-aware slotting suggestions for a warehouse: stock whose bin doesn't match its ABC sales-velocity class, or that overfills a bin's capacity. Read-only — proposes moves, never makes them.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-locations/slotting-suggestions?warehouse_id=1","host":["{{baseUrl}}"],"path":["api","v2","warehouse-locations","slotting-suggestions"],"query":[{"key":"warehouse_id","value":"1","description":"Warehouse to analyse (required).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 42,\n      \"sku\": \"WIDGET-1\",\n      \"product_name\": \"Blue Widget\",\n      \"abc_classification\": \"A\",\n      \"warehouse_location_id\": 15,\n      \"bin_code\": \"ST-01\",\n      \"bin_type\": \"storage\",\n      \"quantity\": 80,\n      \"kind\": \"promote_to_pick_face\",\n      \"severity\": \"info\",\n      \"message\": \"Fast mover (class A) is not on a Primary Pick face — move it forward to speed picking.\"\n    },\n    {\n      \"product_id\": 57,\n      \"sku\": \"SLOW-9\",\n      \"product_name\": \"Slow Mover\",\n      \"abc_classification\": \"C\",\n      \"warehouse_location_id\": 9,\n      \"bin_code\": \"PP-02\",\n      \"bin_type\": \"primary_pick\",\n      \"quantity\": 4,\n      \"kind\": \"demote_from_pick_face\",\n      \"severity\": \"info\",\n      \"message\": \"Slow mover (class C) is on a Primary Pick face — free the face for a faster mover.\"\n    }\n  ]\n}"}]}]},{"name":"FIFO Layers","item":[{"name":"List FIFO Layers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers?sort=-fifo_layer_date&per_page=10&page=1","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers"],"query":[{"key":"sort","value":"-fifo_layer_date","description":"Sort field. Prefix with - for descending. Options: id, fifo_layer_date, original_quantity, fulfilled_quantity, available_quantity, avg_cost, total_cost, condition, created_at, updated_at, warehouse_name, sku, product_name","disabled":true},{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"page","value":"1","description":"Page number (default 1).","disabled":true}]},"description":"Returns a paginated list of FIFO inventory layers. Supports GroupedFilterSupport filter trees and sorting.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nSort options: id, fifo_layer_date, original_quantity, fulfilled_quantity, available_quantity, avg_cost, total_cost, condition, created_at, updated_at, warehouse_name, sku, product_name\n\nArchived layers are excluded by default.\n\n`cost_version` is the revaluation generation of the layer's current costs — it starts at 1 and advances by one with each posted inventory revaluation (e.g. a landed cost applied after receipt)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers?sort=-fifo_layer_date&per_page=10&page=1","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers"],"query":[{"key":"sort","value":"-fifo_layer_date","description":"Sort field. Prefix with - for descending. Options: id, fifo_layer_date, original_quantity, fulfilled_quantity, available_quantity, avg_cost, total_cost, condition, created_at, updated_at, warehouse_name, sku, product_name","disabled":true},{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"page","value":"1","description":"Page number (default 1).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 42,\n      \"product_id\": 15,\n      \"fifo_layer_date\": \"2024-03-01\",\n      \"sku\": \"WIDGET-001\",\n      \"product_name\": \"Blue Widget\",\n      \"product_barcode\": \"123456789\",\n      \"original_quantity\": 100,\n      \"fulfilled_quantity\": 35,\n      \"avg_cost\": \"4.5000\",\n      \"total_cost\": \"450.0000\",\n      \"cost_version\": 2,\n      \"origin\": {\n        \"link_type\": \"purchase_order\",\n        \"link_id\": 8,\n        \"name\": \"PO-0008\",\n        \"parent_link_id\": null,\n        \"detail_link_id\": null,\n        \"reference\": \"REF-123\"\n      },\n      \"warehouse\": {\n        \"id\": 2,\n        \"name\": \"Main Warehouse\"\n      },\n      \"is_fba\": false,\n      \"condition\": \"new\",\n      \"integration_unique_id\": null,\n      \"allocated_quantity\": 10,\n      \"available_quantity\": 55,\n      \"archived_at\": null,\n      \"created_at\": \"2024-03-01T10:00:00.000000Z\",\n      \"updated_at\": \"2024-03-15T14:30:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 5,\n  \"per_page\": 10,\n  \"total\": 48,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"Search FIFO Layers","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers"]},"description":"Returns the same paginated list of FIFO cost layers as the GET version of this endpoint, but accepts filters as a JSON request body instead of the query string. Use this POST variant when the filter set is too large or too complex to fit in a URL.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nProvide advanced filters under `filter_groups`, a tree of conditions combined with a `conjunction` of `and` or `or`. Each condition names a `column`, an `operator`, and a `value`. A free-text `search` term matches against layer id, SKU, product name, and warehouse.\n\nText columns (`batch_number`, `product_name`, `sku`, `condition`, `source_type`, `link_type`) accept: is, is_not, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, is_one_of, is_not_one_of.\nNumeric and date columns (`id`, `original_quantity`, `fulfilled_quantity`, `avg_cost`, `total_cost`, `fifo_layer_date`, `created_at`, `updated_at`, `manufacture_date`, `expiry_date`) accept: is, is_not, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between, is_empty, is_not_empty. Additional filters include `warehouse`, `availability`, `expiring_within_days`, and `archived`.\n\nSortable columns (via `sort`, prefix with `-` for descending): id, fifo_layer_date, original_quantity, fulfilled_quantity, available_quantity, avg_cost, total_cost, condition, batch_number, manufacture_date, expiry_date, created_at, updated_at, warehouse_name, sku, product_name. Results default to `-fifo_layer_date`.","body":{"mode":"raw","raw":"{\n  \"per_page\": 25,\n  \"page\": 1,\n  \"sort\": \"-fifo_layer_date\",\n  \"filter_groups\": {\n    \"conjunction\": \"and\",\n    \"children\": [\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"sku\",\n          \"operator\": \"is_one_of\",\n          \"value\": [\n            \"WIDGET-BLUE-01\",\n            \"WIDGET-RED-02\"\n          ]\n        }\n      },\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"avg_cost\",\n          \"operator\": \"between\",\n          \"value\": [\n            5,\n            20\n          ]\n        }\n      }\n    ]\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 90312,\n      \"product_id\": 3312,\n      \"fifo_layer_date\": \"2026-05-01T00:00:00.000000Z\",\n      \"sku\": \"WIDGET-BLUE-01\",\n      \"product_name\": \"Blue Widget\",\n      \"product_barcode\": \"8801234567890\",\n      \"stock_uom_code\": \"EA\",\n      \"batch_number\": \"LOT-2026-05\",\n      \"manufacture_date\": \"2026-04-20\",\n      \"expiry_date\": \"2027-04-20\",\n      \"days_to_expiry\": 288,\n      \"lot_notes\": null,\n      \"original_quantity\": 20,\n      \"fulfilled_quantity\": 0,\n      \"avg_cost\": 13.75,\n      \"total_cost\": 275,\n      \"origin\": {\n        \"link_type\": \"App\\\\Models\\\\PurchaseOrderShipmentReceiptLine\",\n        \"link_id\": 55210,\n        \"name\": \"PO-1042\",\n        \"parent_link_id\": 1042,\n        \"detail_link_id\": null,\n        \"reference\": \"PO-1042\",\n        \"parent_reference\": \"PO-1042\"\n      },\n      \"warehouse\": {\n        \"id\": 2,\n        \"name\": \"Main Warehouse\"\n      },\n      \"is_fba\": false,\n      \"condition\": \"new\",\n      \"allocated_quantity\": 0,\n      \"available_quantity\": 20,\n      \"archived_at\": null,\n      \"created_at\": \"2026-05-01T10:22:00.000000Z\",\n      \"updated_at\": \"2026-05-01T10:22:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"per_page\": 25,\n  \"from\": 1,\n  \"to\": 1,\n  \"last_page\": 40,\n  \"total\": 987,\n  \"next_page_url\": \"https://api.example.com/api/v2/fifo-layers?page=2\",\n  \"prev_page_url\": null\n}"}]},{"name":"Show FIFO Layer","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer"],"variable":[{"key":"fifoLayer","value":"42","description":"FIFO Layer ID"}]},"description":"Returns a single FIFO layer with product, warehouse, source-document link, and origin-movement relations loaded.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe `origin` block identifies the source document that created the layer and now includes `origin.movement` — the metadata of the inventory movement that created the layer (id, date, type, source link, quantity, unit_cost, inventory_status, reference, batch_number, expiry_date, warehouse, and warehouse_location). That origin movement is excluded from the layer's usage-movements listing by default, so its details are surfaced here instead.\n\nLot fields (`batch_number`, `manufacture_date`, `expiry_date`, `days_to_expiry`, `lot_notes`) are always included; `days_to_expiry` is a signed whole-day countdown (negative = already expired, null when the lot has no expiry).\n\nAuthentication: Requires Bearer token.\n\n`cost_version` is the revaluation generation of the layer's current costs (starts at 1, advances with each posted revaluation). The origin movement's `cost_version` is the generation its `unit_cost` snapshot was recorded against — origin movements are immutable history, so a layer `cost_version` greater than the origin movement's means the layer has been revalued since receipt and the deltas are recorded as inventory revaluations.\n\nThe response includes `review_status` when the lot has been flagged or dispositioned — `status` (`open`, `acknowledged`, `written_off`, `quarantined`), a display `label`, the `notes` explaining why, the `inventory_hold_id` of any hold it placed, and `reviewed_at`. It is null for a lot that has never been flagged.\n\nNote that `available_quantity` is layer accounting — original quantity minus fulfilled quantity — and is NOT available-to-promise. A quarantined lot keeps reporting its units here while the hold keeps them off sale, so read `review_status` alongside it.\n\n`cost_history` is the layer's ordered cost progression: a `base` entry carrying the unit cost the layer was booked at, one `adjustment` entry for every cost change recorded since (with `unit_cost_delta`, `new_unit_cost`, `effective_at`, the `reason`, the `resolution` that dated it — `restate` back-dates the change to the original event, `prospective` dates it from when the change was made — plus cross-links to the `source` document and the `journal_entry` it posted), and a final `current` entry holding today's unit cost. A layer produced by a manufacturing order also carries a `conversion` entry directly after the base: the base is the materials cost booked at receipt, and the conversion entry is the operation, overhead and contractor cost capitalized onto the finished goods at production (`unit_cost_delta`, `new_unit_cost`, `effective_at`, `reason_notes`, and a `source` linking the producing manufacturing order; `journal_entry` is always null). It is not a revaluation, so it does not advance `cost_version`. The base, conversion and adjustment steps always sum to the current cost. `source` is null on adjustments recorded before the causing document was tracked; `journal_entry` is null when no live entry exists. Only returned by this endpoint — the list endpoint omits it.\n\n`pending_cost_changes` lists staged cost changes that would move this layer's cost but have not been accepted yet, each with the triggering document `reference` and the `unit_cost_delta` it would apply. An empty array means nothing is outstanding. The costs elsewhere in this response do NOT include these amounts."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer"],"variable":[{"key":"fifoLayer","value":"42","description":"FIFO Layer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"product_id\": 15,\n    \"fifo_layer_date\": \"2024-03-01\",\n    \"sku\": \"WIDGET-001\",\n    \"product_name\": \"Blue Widget\",\n    \"product_barcode\": \"123456789\",\n    \"stock_uom_code\": \"EA\",\n    \"batch_number\": \"LOT-2024-03-001\",\n    \"manufacture_date\": \"2024-02-28\",\n    \"expiry_date\": \"2025-02-28\",\n    \"days_to_expiry\": 226,\n    \"lot_notes\": null,\n    \"original_quantity\": 100,\n    \"fulfilled_quantity\": 35,\n    \"avg_cost\": \"4.5000\",\n    \"total_cost\": \"450.0000\",\n    \"cost_version\": 2,\n    \"cost_breakdown\": null,\n    \"cost_history\": [\n      {\n        \"type\": \"base\",\n        \"label\": \"Original cost\",\n        \"unit_cost\": 4.055,\n        \"effective_at\": \"2024-03-01T10:00:00.000000Z\",\n        \"cost_version\": 1\n      },\n      {\n        \"type\": \"adjustment\",\n        \"label\": \"Landed costs added\",\n        \"unit_cost_delta\": 0.445,\n        \"new_unit_cost\": 4.5,\n        \"effective_at\": \"2024-03-01T10:00:00.000000Z\",\n        \"cost_version\": 2,\n        \"reason\": \"landed_cost_added\",\n        \"reason_label\": \"Landed Cost Added\",\n        \"reason_notes\": null,\n        \"resolution\": \"restate\",\n        \"source\": {\n          \"type\": \"App\\\\Models\\\\Bill\",\n          \"id\": 77,\n          \"type_label\": \"Bill\",\n          \"reference\": \"FRT-2091\",\n          \"route\": \"/purchases/bills/77\"\n        },\n        \"journal_entry\": {\n          \"id\": 901,\n          \"reference\": \"JE-00412\",\n          \"route\": \"/ledger/entries/901\"\n        }\n      },\n      {\n        \"type\": \"current\",\n        \"label\": \"Current cost\",\n        \"unit_cost\": 4.5,\n        \"cost_version\": 2\n      }\n    ],\n    \"pending_cost_changes\": [\n      {\n        \"id\": 12,\n        \"trigger_label\": \"Landed Cost Bill\",\n        \"reference\": \"FRT-2140\",\n        \"unit_cost_delta\": 0.12,\n        \"route\": \"/inventory/cost-changes\"\n      }\n    ],\n    \"origin\": {\n      \"link_type\": \"purchase_order\",\n      \"link_id\": 8,\n      \"name\": \"PO-0008\",\n      \"parent_link_id\": null,\n      \"detail_link_id\": null,\n      \"reference\": \"REF-123\",\n      \"parent_reference\": null,\n      \"movement\": {\n        \"id\": 187,\n        \"date\": \"2024-03-01T10:00:00.000000Z\",\n        \"type\": \"purchase_order_received\",\n        \"link_type\": \"purchase_order_shipment_item\",\n        \"link_id\": 91,\n        \"quantity\": 100,\n        \"unit_cost\": \"4.0550\",\n        \"cost_version\": 1,\n        \"inventory_status\": \"active\",\n        \"reference\": \"REF-123\",\n        \"batch_number\": \"LOT-2024-03-001\",\n        \"expiry_date\": \"2025-02-28\",\n        \"warehouse\": {\n          \"id\": 2,\n          \"name\": \"Main Warehouse\"\n        },\n        \"warehouse_location\": {\n          \"id\": 7,\n          \"code\": \"A-01-02\",\n          \"name\": \"Aisle A Bay 1 Shelf 2\"\n        }\n      }\n    },\n    \"warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\"\n    },\n    \"is_fba\": false,\n    \"is_consignment\": false,\n    \"condition\": \"new\",\n    \"integration_unique_id\": null,\n    \"allocated_quantity\": 10,\n    \"available_quantity\": 65,\n    \"archived_at\": null,\n    \"created_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"updated_at\": \"2024-03-15T14:30:00.000000Z\",\n    \"review_status\": {\n      \"status\": \"quarantined\",\n      \"label\": \"Quarantined\",\n      \"notes\": \"COA result out of specification.\",\n      \"inventory_hold_id\": 1,\n      \"reviewed_at\": \"2026-08-19T21:54:43.000000Z\"\n    }\n  }\n}"}]},{"name":"FIFO Layer Allocations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/allocations","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","allocations"],"variable":[{"key":"fifoLayer","value":"42","description":"FIFO Layer ID"}]},"description":"Returns sales order line allocations for this FIFO layer — which sales orders have reserved quantity from this layer.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/allocations","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","allocations"],"variable":[{"key":"fifoLayer","value":"42","description":"FIFO Layer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 101,\n      \"quantity\": 10,\n      \"sales_order_line_id\": 55,\n      \"sales_order\": {\n        \"id\": 30,\n        \"order_number\": \"SO-0030\",\n        \"customer_id\": 7,\n        \"customer_name\": \"Acme Corp\",\n        \"order_status\": \"processing\"\n      },\n      \"product\": {\n        \"id\": 15,\n        \"sku\": \"WIDGET-001\",\n        \"name\": \"Blue Widget\"\n      },\n      \"allocation_status\": \"allocated\",\n      \"created_at\": \"2024-03-10T09:00:00.000000Z\"\n    }\n  ]\n}"}]},{"name":"FIFO Layer Inventory Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/inventory-movements","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","inventory-movements"],"variable":[{"key":"fifoLayer","value":"42","description":"FIFO Layer ID"}]},"description":"Paginated usage movements for a FIFO layer (standard paginated response: `data[]` plus `current_page`, `last_page`, `per_page`, `total`, ...).\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe movement that originally created the layer is **excluded by default** — it is presented in the layer's `origin` block on the single-layer endpoint instead of as a usage. Pass `exclude_origin=0` to include it (e.g. to tally the layer's full movement history).\n\nFilters (server-side):\n- `filter[type]` — exact movement type match. Dotted operator variants are also available (`filter[type.is]`, `filter[type.contains]`, ...).\n- `filter[link_type]` — exact source-document type match.\n- `filter[inventory_status]` — defaults to `active`; pass `all` for no status restriction. Dotted operator variants (`filter[inventory_status.is]`, ...) are also available.\n- `filter[reference]` — text operator filters (`filter[reference.contains]`, ...).\n- `filter[quantity]` — numeric operator filters (`filter[quantity.gte]`, ...).\n- `filter[inventory_movement_date]`, `filter[created_at]` — date operator filters (`.on`, `.before`, `.after`, `.between`).\n- `filter[search]` — searches ID (exact), Reference, SKU, and Product Name.\n\nAllowed sorts: id, inventory_movement_date, type, inventory_status, quantity, reference, created_at (prefix with - for descending; default: -inventory_movement_date).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/inventory-movements","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","inventory-movements"],"variable":[{"key":"fifoLayer","value":"42","description":"FIFO Layer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 201,\n      \"inventory_movement_date\": \"2024-03-12T11:00:00.000000Z\",\n      \"product_id\": 15,\n      \"sku\": \"WIDGET-001\",\n      \"barcode\": \"123456789\",\n      \"product_name\": \"Blue Widget\",\n      \"stock_uom_code\": \"EA\",\n      \"type\": {\n        \"name\": \"fulfillment\",\n        \"link_type\": \"sales_order_fulfillment_line\",\n        \"link_id\": 3411,\n        \"parent_link_id\": 812,\n        \"detail_link_id\": null\n      },\n      \"inventory_status\": \"active\",\n      \"quantity\": -5,\n      \"warehouse\": {\n        \"id\": 2,\n        \"name\": \"Main Warehouse\"\n      },\n      \"layer\": {\n        \"fifo_layer_id\": 42,\n        \"name\": \"PO-0008\"\n      },\n      \"fifo_layer_id\": 42,\n      \"reference\": \"SO-1042\",\n      \"created_at\": \"2024-03-12T11:00:00.000000Z\",\n      \"updated_at\": \"2024-03-12T11:00:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 15,\n  \"total\": 35,\n  \"from\": 1,\n  \"to\": 15\n}"}]},{"name":"Archive FIFO Layer","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/archive","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","archive"],"variable":[{"key":"fifoLayer","value":"42","description":"FIFO Layer ID"}]},"description":"Archives a single FIFO layer. Only fully utilized layers (available_quantity = 0) can be archived.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/archive","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","archive"],"variable":[{"key":"fifoLayer","value":"42","description":"FIFO Layer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"FIFO Layer 42 has been archived.\",\n  \"data\": {\n    \"id\": 42,\n    \"archived_at\": \"2024-03-20T10:00:00.000000Z\"\n  }\n}"}]},{"name":"Unarchive FIFO Layer","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/unarchive","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","unarchive"],"variable":[{"key":"fifoLayer","value":"42","description":"FIFO Layer ID"}]},"description":"Unarchives a previously archived FIFO layer.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/unarchive","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","unarchive"],"variable":[{"key":"fifoLayer","value":"42","description":"FIFO Layer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"FIFO Layer 42 has been unarchived.\",\n  \"data\": {\n    \"id\": 42,\n    \"archived_at\": null\n  }\n}"}]},{"name":"Bulk Archive FIFO Layers","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/archive","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers","archive"]},"description":"Archives multiple FIFO layers. Only fully utilized layers (available_quantity = 0) can be archived. Use apply_to_all with filters to archive all matching layers.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- ids (required_without:apply_to_all, array): Array of FIFO layer IDs\n- apply_to_all (nullable, boolean): If true, archive all layers matching filters\n- filters (nullable, array): Filter criteria when using apply_to_all","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    42,\n    43,\n    44\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/archive","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 of 2 fifo layers have been archived successfully.\"\n}"}]},{"name":"Bulk Unarchive FIFO Layers","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/unarchive","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers","unarchive"]},"description":"Unarchives multiple FIFO layers.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- ids (required_without:apply_to_all, array): Array of FIFO layer IDs\n- apply_to_all (nullable, boolean): If true, unarchive all matching layers\n- filters (nullable, array): Filter criteria when using apply_to_all","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    42,\n    43\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/unarchive","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 of 2 fifo layers have been un-archived successfully.\"\n}"}]}]},{"name":"Customers","item":[{"name":"List Customers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/customers?per_page=10&page=1","host":["{{baseUrl}}"],"path":["api","v2","customers"],"query":[{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"page","value":"1","description":"Page number (default 1).","disabled":true}]},"description":"Returns a paginated list of customers.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/customers?per_page=10&page=1","host":["{{baseUrl}}"],"path":["api","v2","customers"],"query":[{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"page","value":"1","description":"Page number (default 1).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 7,\n      \"name\": \"Acme Corp\",\n      \"email\": \"orders@acme.com\",\n      \"phone\": \"+1-555-0100\",\n      \"created_at\": \"2023-06-15T08:00:00.000000Z\",\n      \"updated_at\": \"2024-01-10T12:00:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 10,\n  \"total\": 25\n}"}]},{"name":"Show Customer","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/customers/{customer}","host":["{{baseUrl}}"],"path":["api","v2","customers",":customer"],"variable":[{"key":"customer","value":"7","description":"Customer ID"}]},"description":"Returns a single customer.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nUse the `with` query parameter (comma-separated) to include relations: `salesChannelOrigin`, `productPricingTier`, `store`, `shippingMethodDomestic`, `shippingMethodInternational`, `warehouse`, `defaultSalesRep`, `defaultIncoterm`, `integrationInstance`, `shippingAddress`, `billingAddress`, `addresses`.\n\n`merged_into` is returned only for an archived customer that was merged into another one while the merge can still be undone (30 days): `{merge_log_id, customer_id, customer_name, merged_at, undo_available_until}`, or `null` once the window has passed. Undo it with `POST /api/v2/customers/merge-logs/{merge_log_id}/undo`.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/customers/{customer}","host":["{{baseUrl}}"],"path":["api","v2","customers",":customer"],"variable":[{"key":"customer","value":"7","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 7,\n    \"name\": \"Acme Corp\",\n    \"email\": \"orders@acme.com\",\n    \"phone\": \"+1-555-0100\",\n    \"created_at\": \"2023-06-15T08:00:00.000000Z\",\n    \"updated_at\": \"2024-01-10T12:00:00.000000Z\",\n    \"archived_at\": \"2024-01-12T09:30:00.000000Z\",\n    \"merged_into\": {\n      \"merge_log_id\": 31,\n      \"customer_id\": 4,\n      \"customer_name\": \"Acme Corporation\",\n      \"merged_at\": \"2024-01-12T09:30:00.000000Z\",\n      \"undo_available_until\": \"2024-02-11T09:30:00.000000Z\"\n    }\n  }\n}"}]},{"name":"Customer Lookup (Quick Search)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/lookup","host":["{{baseUrl}}"],"path":["api","customers","lookup"]},"description":"Lightweight customer quick-search used by the POS register to attach a customer at checkout. Returns a compact card per customer including the live store-credit wallet balance (summed in one join, N+1-safe), the assigned pricing tier, and tax-exemption status. Archived customers are excluded.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nQuery params: `q` (optional; matches name/company/email/phone), `limit` (optional; 1–50, default 15).\n\nAuthentication: Requires Bearer token (PAT)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/lookup","host":["{{baseUrl}}"],"path":["api","customers","lookup"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 501,\n      \"name\": \"Ada Lovelace\",\n      \"company\": \"Analytical Engines\",\n      \"email\": \"ada@example.com\",\n      \"phone\": \"5550100\",\n      \"tax_exempt\": false,\n      \"tax_exemption_reference\": null,\n      \"product_pricing_tier_id\": 3,\n      \"pricing_tier_name\": \"Wholesale\",\n      \"store_credit_balance\": 42.5,\n      \"loyalty_points_balance\": 250,\n      \"house_account_enabled\": true,\n      \"credit_limit\": 5000,\n      \"house_account_balance\": 1200.5,\n      \"billing_email\": \"ap@acme-wholesale.com\",\n      \"payment_terms_days\": 30\n    },\n    {\n      \"id\": 502,\n      \"name\": \"Ada Byron\",\n      \"company\": null,\n      \"email\": \"byron@example.com\",\n      \"phone\": \"5550111\",\n      \"tax_exempt\": true,\n      \"tax_exemption_reference\": \"RESALE-12345\",\n      \"product_pricing_tier_id\": null,\n      \"pricing_tier_name\": null,\n      \"store_credit_balance\": 0,\n      \"loyalty_points_balance\": 250,\n      \"house_account_enabled\": true,\n      \"credit_limit\": 5000,\n      \"house_account_balance\": 1200.5,\n      \"billing_email\": \"ap@acme-wholesale.com\",\n      \"payment_terms_days\": 30\n    }\n  ]\n}"}]},{"name":"List Duplicate Customer Pairs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/customers/duplicate-runs/{trackedJobLog}/pairs","host":["{{baseUrl}}"],"path":["api","v2","customers","duplicate-runs",":trackedJobLog","pairs"],"variable":[{"key":"trackedJobLog","value":"9451","description":"Detection run ID - the tracked_job_log_id returned when the run was started"}]},"description":"List the candidate duplicate pairs produced by a completed detection run, hydrated with each customer's identifying details for side-by-side comparison. Pairs are ordered by match score (highest first) and paginated; pairs that have since been marked 'not a duplicate' are excluded from the results.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach pair contains: customer_a and customer_b detail cards, the 0-100 match score, a bucket ('auto' = score of 90 or higher, 'review' = 60 or higher, 'hidden' otherwise), and the per-field scoring signals (field, score, weight, contribution, note).\n\nFor runs with scope 'sku' the cards include the sales order count and last order date. For scope 'qbo' the cards include the QuickBooks customer details (display name, company, given/family name, linked customer ID, active flag).\n\nIf the run has not completed or produced no results, an empty list is returned with meta.total = 0.\n\nPagination: page (default 1) and per_page (default 25).\n\nAuthentication: Requires Bearer token with the customers read/write scope and the customer merge permission."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/customers/duplicate-runs/{trackedJobLog}/pairs","host":["{{baseUrl}}"],"path":["api","v2","customers","duplicate-runs",":trackedJobLog","pairs"],"variable":[{"key":"trackedJobLog","value":"9451","description":"Detection run ID - the tracked_job_log_id returned when the run was started"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"customer_a\": {\n        \"id\": 101,\n        \"name\": \"John Smith\",\n        \"company\": \"Acme Corp\",\n        \"email\": \"john@acmecorp.com\",\n        \"phone\": \"+1-555-0101\",\n        \"address\": \"500 Industrial Way, 91201\",\n        \"archived_at\": null,\n        \"sales_orders_count\": 14,\n        \"last_order_date\": \"2026-06-12\"\n      },\n      \"customer_b\": {\n        \"id\": 2384,\n        \"name\": \"John A. Smith\",\n        \"company\": \"Acme Corporation\",\n        \"email\": \"john@acmecorp.com\",\n        \"phone\": null,\n        \"address\": null,\n        \"archived_at\": null,\n        \"sales_orders_count\": 2,\n        \"last_order_date\": \"2025-11-03\"\n      },\n      \"score\": 91,\n      \"bucket\": \"auto\",\n      \"signals\": [\n        {\n          \"field\": \"email\",\n          \"score\": 1,\n          \"weight\": 50,\n          \"contribution\": 50,\n          \"note\": null\n        },\n        {\n          \"field\": \"company\",\n          \"score\": 0.97,\n          \"weight\": 30,\n          \"contribution\": 29.1,\n          \"note\": \"fuzzy_company_high\"\n        },\n        {\n          \"field\": \"name\",\n          \"score\": 0.8,\n          \"weight\": 15,\n          \"contribution\": 12,\n          \"note\": null\n        },\n        {\n          \"field\": \"address\",\n          \"score\": 0,\n          \"weight\": 3,\n          \"contribution\": 0,\n          \"note\": null\n        },\n        {\n          \"field\": \"phone\",\n          \"score\": 0,\n          \"weight\": 2,\n          \"contribution\": 0,\n          \"note\": null\n        }\n      ]\n    }\n  ],\n  \"meta\": {\n    \"total\": 37,\n    \"pairs_count\": 40,\n    \"page\": 1,\n    \"per_page\": 25,\n    \"last_page\": 2,\n    \"scope\": \"sku\",\n    \"integration_instance_id\": null\n  }\n}"}]},{"name":"Export Customers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/customers/export","host":["{{baseUrl}}"],"path":["api","v2","customers","export"]},"description":"Download all customers matching the current filters as a spreadsheet file (CSV by default, XLSX when format=xlsx). The file contains the columns: Sr No., id, name, company, email, sales_channel_origin_name, integration_instance_name, created_at, updated_at, phone, then the default shipping address (shipping_name, shipping_company, shipping_address1, shipping_address2, shipping_address3, shipping_city, shipping_province, shipping_zip, shipping_country, shipping_phone) and the default billing address (billing_name, billing_company, billing_address1, billing_address2, billing_address3, billing_city, billing_province, billing_zip, billing_country, billing_phone). Address columns are blank when the customer has no default address of that type. Rows are ordered by customer ID ascending and the export is not paginated - every matching customer is included.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nPrivacy: customers originating from Amazon are redacted - the row keeps its serial number but every other column is blank.\n\nTimestamps in the file are converted to the account's local timezone (matching on-screen values).\n\nFiltering uses operator-style parameters: filter[column.operator]=value. A bare filter[column]=value acts as the 'is' operator.\n\nText columns (name, email, company, phone, city, province, country) support: contains, does_not_contain, is, is_not, is_one_of, is_not_one_of, starts_with, does_not_start_with, ends_with, does_not_end_with, is_empty, is_not_empty. The city, province, and country filters match the customer's default shipping address.\n\nDate columns (created_at, updated_at, archived_at) support: is, is_not, before, after, on_or_before, on_or_after, between (two comma-separated dates), is_empty, is_not_empty, today, yesterday, tomorrow, past_week, past_month, past_year, next_week, next_month, next_year, days_ago, days_from_now, past_days, next_days. Dates use YYYY-MM-DD format and are interpreted in the account's local timezone.\n\nArchived customers are excluded unless filter[archived] is provided ('only' = archived customers only, 'all' = archived and active).\n\nThe response is a file download (Content-Type: text/csv or the XLSX MIME type), not JSON.\n\nAuthentication: Requires Bearer token with the customers read/write scope.\n\nOptional. When `include_contacts=1`, the export flattens to one row per contact with the parent columns repeated (parent-only row when a record has no contacts), appending Contact ID/Name/Email/Phone/Role/Title/Primary/Notes columns.\n\nWhen `content_mode=addresses`, the export flattens to one row per saved address with the customer columns repeated: the default shipping address, then the default billing address (one row labelled 'Default Shipping & Billing' when they are the same address), then every other address in the customer's address book. Appended columns: Address ID, Address Type (Default Shipping, Default Billing, Default Shipping & Billing, Additional), Address Label, Address Name, Address Company, Address Email, Address Phone, Address Line 1, Address Line 2, Address Line 3, Address City, Address Province, Address Zip, Address Country. Customers with no saved addresses keep a single row with the address columns blank."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/customers/export","host":["{{baseUrl}}"],"path":["api","v2","customers","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"Sr No.,id,name,company,email,sales_channel_origin_name,integration_instance_name,created_at,updated_at\n1,101,John Smith,Acme Corp,john@acmecorp.com,Shopify,Acme Shopify Store,2026-01-14 09:32:11,2026-06-02 15:04:27\n2,,,,,,,,\n3,214,Jane Rivera,Rivera Trading,jane@riveratrading.com,,,2026-02-20 11:05:44,2026-02-20 11:05:44\n\n(CSV file download named customers-2026-07-07-14-21-05.csv. Row 2 is an Amazon-origin customer redacted for privacy.)"}]},{"name":"Mark Customer Pair as Not Duplicate","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/customers/dedup-decisions","host":["{{baseUrl}}"],"path":["api","v2","customers","dedup-decisions"]},"description":"Record a decision that two customers are not duplicates of each other, so the pair stops appearing in duplicate detection results. The decision is order-independent (the pair is stored with the lower ID first) and idempotent - repeating the call for the same pair returns the existing decision.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFor scope 'qbo', the IDs refer to customer records synced from the connected QuickBooks Online instance identified by integration_instance_id.\n\nBody fields:\n- customer_id_a (integer, required): first customer of the pair.\n- customer_id_b (integer, required): second customer of the pair; must differ from customer_id_a.\n- scope (string, required): 'sku' or 'qbo'.\n- integration_instance_id (integer, optional): the connected integration instance the decision applies to (used with scope 'qbo'); must reference an existing integration instance.\n\nAuthentication: Requires Bearer token with the customers read/write scope and the customer merge permission.","body":{"mode":"raw","raw":"{\n  \"customer_id_a\": 101,\n  \"customer_id_b\": 2384,\n  \"scope\": \"sku\",\n  \"integration_instance_id\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/customers/dedup-decisions","host":["{{baseUrl}}"],"path":["api","v2","customers","dedup-decisions"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 55,\n    \"scope\": \"sku\"\n  },\n  \"message\": \"Marked as not duplicate.\"\n}"}]},{"name":"Start Customer Duplicate Detection Run","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/customers/duplicate-runs","host":["{{baseUrl}}"],"path":["api","v2","customers","duplicate-runs"]},"description":"Start an asynchronous duplicate-detection run over customer records. The work is dispatched to a background job and the endpoint returns immediately with a tracked_job_log_id. Once the run completes, fetch the scored candidate pairs from the List Duplicate Customer Pairs endpoint using that ID.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nScopes:\n- sku - scans the account's own customer records.\n- qbo - scans customer records synced from a connected QuickBooks Online instance (integration_instance_id is required for this scope).\n\nCandidates are grouped by normalized email and company, then each pair is scored 0-100 across weighted signals (email, company, name, address, phone). Only pairs scoring at or above the threshold (default 80) are kept, and pairs previously marked 'not a duplicate' are excluded.\n\nBody fields:\n- scope (string, required): 'sku' or 'qbo'.\n- integration_instance_id (integer, optional): required when scope is 'qbo'; must reference a connected integration instance.\n- threshold (integer, optional): minimum match score (0-100) for a pair to be included. Default: 80.\n\nAuthentication: Requires Bearer token with the customers read/write scope and the customer merge permission.","body":{"mode":"raw","raw":"{\n  \"scope\": \"sku\",\n  \"integration_instance_id\": null,\n  \"threshold\": 80\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/customers/duplicate-runs","host":["{{baseUrl}}"],"path":["api","v2","customers","duplicate-runs"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 9451\n  },\n  \"message\": \"Duplicate detection started\"\n}"}]},{"name":"Merge Customers","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/customers/merge","host":["{{baseUrl}}"],"path":["api","v2","customers","merge"]},"description":"Merge the secondary customer into the primary customer. This is a destructive operation performed in a single transaction:\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n- All related records are reassigned from the secondary to the primary customer: sales orders, sales credits, quotes, order document extractions, artwork, store credit entries, loyalty transactions, house-account (on-account) transactions, gift cards, saved payment methods, AR communications, customer SKU mappings, customer pricing, B2B price group membership, addresses, contacts, tags, portal buyers/saved lists/carts/approvals/feed keys/account applications, channel customer mappings, and QuickBooks, Xero, Shopify B2B location and Magento customer links. Store credit, loyalty and house-account balances are the sum of their ledgers, so the combined balance is preserved (running balance_after values are re-chained). Where the primary already holds a conflicting row (same customer SKU, same price tier, same address, same tag, an existing portal cart, etc.) the primary's row is kept and the secondary's stays on the archived customer; the only primary contact / default card stays the primary's.\n- Contact fields (email, company, phone, fax, tax number, default shipping/billing address) are copied from the secondary onto the primary only where the primary's value is empty; existing primary data is never overwritten.\n- The secondary customer is archived.\n- A merge log is written capturing a snapshot of both customers and the per-table reassignment counts (fk_moves).\n\nThe returned merge_log_id can be used to reverse the merge within 30 days via Undo Customer Merge. The request fails with 422 if the secondary customer is already archived or the primary customer is archived.\n\nBody fields:\n- primary_customer_id (integer, required): the customer to keep; must reference an existing customer.\n- secondary_customer_id (integer, required): the customer to merge away; must reference an existing customer and differ from primary_customer_id.\n\nAuthentication: Requires Bearer token with the customers read/write scope and the customer merge permission.","body":{"mode":"raw","raw":"{\n  \"primary_customer_id\": 101,\n  \"secondary_customer_id\": 2384\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/customers/merge","host":["{{baseUrl}}"],"path":["api","v2","customers","merge"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"merge_log_id\": 12,\n    \"fk_moves\": {\n      \"sales_orders\": 14,\n      \"sales_credits\": 2,\n      \"customer_sku_mappings\": 0,\n      \"sales_order_ocr_extractions\": 1,\n      \"qbo_customers\": 1\n    }\n  },\n  \"message\": \"Customers merged successfully.\"\n}"}]},{"name":"Undo Customer Merge","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/customers/merge-logs/{mergeLog}/undo","host":["{{baseUrl}}"],"path":["api","v2","customers","merge-logs",":mergeLog","undo"],"variable":[{"key":"mergeLog","value":"12","description":"Merge log ID returned by the merge endpoint"}]},"description":"Reverse a previous customer merge: un-archives the secondary customer and moves the records that were reassigned during the merge back to it.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA merge can only be reversed once, and only within 30 days of the original merge; attempting to reverse an already-reversed or expired merge fails. Use the merge_log_id returned by Merge Customers as the path parameter.\n\nAuthentication: Requires Bearer token with the customers read/write scope and the customer merge permission."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/customers/merge-logs/{mergeLog}/undo","host":["{{baseUrl}}"],"path":["api","v2","customers","merge-logs",":mergeLog","undo"],"variable":[{"key":"mergeLog","value":"12","description":"Merge log ID returned by the merge endpoint"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"merge_log_id\": 12,\n    \"reversed_at\": \"2026-07-07T14:21:05+00:00\"\n  },\n  \"message\": \"Merge reversed.\"\n}"}]},{"name":"Preview Customer Merge","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/customers/merge/preview","host":["{{baseUrl}}"],"path":["api","v2","customers","merge","preview"]},"description":"Preview the effect of merging one customer (secondary) into another (primary) without changing any data. Returns, per related record type, the number of records currently attached to the secondary customer that would be reassigned to the primary: sales orders (sales_orders), sales credits (sales_credits), customer SKU mappings (customer_sku_mappings), order document extractions (sales_order_ocr_extractions), and linked QuickBooks customer records (qbo_customers).\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nUse this before calling Merge Customers so the operator can review what will move.\n\nBody fields:\n- primary_customer_id (integer, required): the customer that will be kept; must reference an existing customer.\n- secondary_customer_id (integer, required): the customer that will be merged away; must reference an existing customer and differ from primary_customer_id.\n\nAuthentication: Requires Bearer token with the customers read/write scope and the customer merge permission.","body":{"mode":"raw","raw":"{\n  \"primary_customer_id\": 101,\n  \"secondary_customer_id\": 2384\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/customers/merge/preview","host":["{{baseUrl}}"],"path":["api","v2","customers","merge","preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"fk_moves\": {\n      \"sales_orders\": 14,\n      \"sales_credits\": 2,\n      \"customer_sku_mappings\": 0,\n      \"sales_order_ocr_extractions\": 1,\n      \"qbo_customers\": 1\n    }\n  }\n}"}]},{"name":"Create Customer","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers","host":["{{baseUrl}}"],"path":["api","customers"]},"description":"Create a new customer. Automatically creates a default shipping address from the root address fields. Optionally provide a separate billing address.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nOptional fields:\n- tags: Customer tags (array of tag names, max 64 chars each). Omit to leave existing tags unchanged; send an array to replace them. Unknown names are created automatically.\n\nValidation notes:\n- Email must be unique across all customer addresses\n- If a customer with the same name/zip/address1 already exists, returns a 422 with CODE_CUSTOMER_EXISTS","body":{"mode":"raw","raw":"{\n  \"name\": \"Jane Smith\",\n  \"company\": \"Smith Industries\",\n  \"email\": \"jane.smith@smithindustries.com\",\n  \"phone\": \"+1-555-0200\",\n  \"fax\": null,\n  \"address1\": \"456 Commerce Ave\",\n  \"address2\": \"Suite 100\",\n  \"address3\": null,\n  \"city\": \"Los Angeles\",\n  \"province\": \"California\",\n  \"province_code\": \"CA\",\n  \"zip\": \"90001\",\n  \"country\": \"United States\",\n  \"country_code\": \"US\",\n  \"integration_instance_id\": null,\n  \"default_sales_rep_id\": null,\n  \"default_billing_address\": {\n    \"name\": \"Jane Smith\",\n    \"company\": \"Smith Industries\",\n    \"email\": \"billing@smithindustries.com\",\n    \"phone\": \"+1-555-0201\",\n    \"address1\": \"789 Billing Blvd\",\n    \"city\": \"Los Angeles\",\n    \"province\": \"California\",\n    \"province_code\": \"CA\",\n    \"zip\": \"90002\",\n    \"country\": \"United States\",\n    \"country_code\": \"US\"\n  },\n  \"billing_email\": \"ap@acme-wholesale.com\",\n  \"payment_terms_days\": 30,\n  \"tags\": [\n    \"VIP\",\n    \"Wholesale\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/customers","host":["{{baseUrl}}"],"path":["api","customers"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"name\": \"Jane Smith\",\n    \"email\": \"jane.smith@smithindustries.com\",\n    \"company\": \"Smith Industries\"\n  },\n  \"message\": \"customer created successfully\"\n}"}]},{"name":"Bulk Delete Customers","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/customers","host":["{{baseUrl}}"],"path":["api","customers"]},"description":"Bulk delete customers by IDs or by applying to all filtered results.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/customers","host":["{{baseUrl}}"],"path":["api","customers"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 customers deleted successfully.\"\n}"}]},{"name":"Get Customer","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}","host":["{{baseUrl}}"],"path":["api","customers",":customer"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]},"description":"Get a single customer with all related data including addresses, billing/shipping addresses, default settings, and lifetime value.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Tax exemption:** `tax_exempt` (boolean) — whether sales to this customer are tax exempt; `tax_exemption_reference` (string or null) — the exemption certificate / resale number recorded for the customer.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/{customer}","host":["{{baseUrl}}"],"path":["api","customers",":customer"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"John Doe\",\n    \"email\": \"john.doe@example.com\",\n    \"lifetime_value\": 12500,\n    \"tax_exempt\": true,\n    \"tax_exemption_reference\": \"RESALE-CA-0048213\",\n    \"credit_hold\": false,\n    \"payment_term_id\": 4,\n    \"payment_term\": {\n      \"id\": 4,\n      \"name\": \"Net 30\",\n      \"generated_description\": \"2/10 Net 30\"\n    },\n    \"tags\": [\n      \"VIP\",\n      \"Wholesale\"\n    ]\n  }\n}"}]},{"name":"Update Customer","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers/{customer}","host":["{{baseUrl}}"],"path":["api","customers",":customer"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]},"description":"Update an existing customer. All fields are optional. Default settings (pricing tier, store, shipping methods, warehouse) can only be set on update.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nOptional fields:\n- tags: Customer tags (array of tag names, max 64 chars each). Omit to leave existing tags unchanged; send an array to replace them. Unknown names are created automatically.\n- payment_term_id: The customer's default payment term for sales orders (integer, nullable). Must reference a sales-applicable, non-archived payment term; send null to clear. New orders for this customer derive their payment due date from this term when the order has no explicit term of its own.\n- credit_hold: Manual credit hold (boolean). While true, every new unpaid order for this customer is held in Reserved until released.\n- tax_exempt: Whether sales to this customer are tax exempt (boolean). Omit to leave unchanged.\n- tax_exemption_reference: Exemption certificate or resale number (string, max 255, nullable) — send null to clear.\n\n**Permissions:** `credit_limit` and `credit_hold` are accepted only from users holding the customer credit management permission (`customers.manage_credit`); without it these fields are ignored.","body":{"mode":"raw","raw":"{\n  \"name\": \"John Doe Updated\",\n  \"company\": \"Acme Corp International\",\n  \"email\": \"john.doe@acmecorp.com\",\n  \"phone\": \"+1-555-0101\",\n  \"product_pricing_tier_id\": 2,\n  \"store_id\": 1,\n  \"shipping_method_domestic_id\": 3,\n  \"shipping_method_international_id\": 5,\n  \"warehouse_id\": 1,\n  \"tax_exempt\": true,\n  \"tax_exemption_reference\": \"RESALE-CA-0048213\",\n  \"billing_email\": \"ap@acme-wholesale.com\",\n  \"payment_terms_days\": 30,\n  \"payment_term_id\": 4,\n  \"credit_hold\": false,\n  \"tags\": [\n    \"VIP\",\n    \"Wholesale\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/customers/{customer}","host":["{{baseUrl}}"],"path":["api","customers",":customer"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"John Doe Updated\"\n  },\n  \"message\": \"customer John Doe Updated updated successfully\"\n}"}]},{"name":"Delete Customer","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}","host":["{{baseUrl}}"],"path":["api","customers",":customer"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]},"description":"Delete a customer. Returns 400 if the customer is linked to sales orders or other records.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/customers/{customer}","host":["{{baseUrl}}"],"path":["api","customers",":customer"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"customer deleted successfully\"\n}"}]},{"name":"Get Customer Addresses","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/addresses","host":["{{baseUrl}}"],"path":["api","customers",":customer","addresses"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]},"description":"Retrieve all addresses associated with a customer including default shipping, billing, and other addresses.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/{customer}/addresses","host":["{{baseUrl}}"],"path":["api","customers",":customer","addresses"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"John Doe\",\n    \"addresses\": []\n  }\n}"}]},{"name":"Add Customer Address","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/addresses","host":["{{baseUrl}}"],"path":["api","customers",":customer","addresses"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]},"description":"Add a new address to a customer. Address deduplication is applied.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.","body":{"mode":"raw","raw":"{\n  \"label\": \"Warehouse Address\",\n  \"name\": \"John Doe\",\n  \"company\": \"Acme Corp\",\n  \"email\": \"warehouse@acmecorp.com\",\n  \"phone\": \"+1-555-0102\",\n  \"address1\": \"500 Industrial Way\",\n  \"city\": \"Glendale\",\n  \"province\": \"California\",\n  \"province_code\": \"CA\",\n  \"zip\": \"91201\",\n  \"country\": \"United States\",\n  \"country_code\": \"US\",\n  \"is_default_shipping\": false,\n  \"is_default_billing\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/customers/{customer}/addresses","host":["{{baseUrl}}"],"path":["api","customers",":customer","addresses"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"John Doe\",\n    \"addresses\": []\n  }\n}"}]},{"name":"Set Default Shipping Address","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/set-default-shipping-address/{address_id}","host":["{{baseUrl}}"],"path":["api","customers",":customer","set-default-shipping-address",":address_id"],"variable":[{"key":"customer","value":"1","description":"Customer ID"},{"key":"address_id","value":"2","description":"Address ID"}]},"description":"Set a specific address as the customer's default shipping address.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/customers/{customer}/set-default-shipping-address/{address_id}","host":["{{baseUrl}}"],"path":["api","customers",":customer","set-default-shipping-address",":address_id"],"variable":[{"key":"customer","value":"1","description":"Customer ID"},{"key":"address_id","value":"2","description":"Address ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Default shipping address set.\"\n}"}]},{"name":"Set Default Billing Address","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/set-default-billing-address/{address_id}","host":["{{baseUrl}}"],"path":["api","customers",":customer","set-default-billing-address",":address_id"],"variable":[{"key":"customer","value":"1","description":"Customer ID"},{"key":"address_id","value":"2","description":"Address ID"}]},"description":"Set a specific address as the customer's default billing address.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/customers/{customer}/set-default-billing-address/{address_id}","host":["{{baseUrl}}"],"path":["api","customers",":customer","set-default-billing-address",":address_id"],"variable":[{"key":"customer","value":"1","description":"Customer ID"},{"key":"address_id","value":"2","description":"Address ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Default billing address set.\"\n}"}]},{"name":"Delete Customer Address","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/addresses/{address}","host":["{{baseUrl}}"],"path":["api","customers",":customer","addresses",":address"],"variable":[{"key":"customer","value":"1","description":"Customer ID"},{"key":"address","value":"10","description":"Address ID to remove from the customer"}]},"description":"Remove an address from a customer's address book. If the removed address was the customer's default shipping or billing address, the most recently added remaining address is automatically promoted as the new default (or the default is cleared when no other address exists). The underlying address record is permanently deleted only when no other records (other customers, sales orders, etc.) still reference it; otherwise only the customer's link to it is removed.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the customer's refreshed address book.\n\nAuthentication: Requires Bearer token with the customers read/write scope."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/customers/{customer}/addresses/{address}","host":["{{baseUrl}}"],"path":["api","customers",":customer","addresses",":address"],"variable":[{"key":"customer","value":"1","description":"Customer ID"},{"key":"address","value":"10","description":"Address ID to remove from the customer"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"John Doe\",\n    \"email\": \"john.doe@acmecorp.com\",\n    \"zip\": \"91201\",\n    \"address1\": \"500 Industrial Way\",\n    \"addresses\": [\n      {\n        \"id\": 12,\n        \"label\": \"Head Office\",\n        \"name\": \"John Doe\",\n        \"company\": \"Acme Corp\",\n        \"email\": \"john.doe@acmecorp.com\",\n        \"phone\": \"+1-555-0102\",\n        \"address1\": \"500 Industrial Way\",\n        \"address2\": null,\n        \"city\": \"Glendale\",\n        \"province\": \"California\",\n        \"province_code\": \"CA\",\n        \"zip\": \"91201\",\n        \"country\": \"United States\",\n        \"country_code\": \"US\",\n        \"is_default_shipping\": true,\n        \"is_default_billing\": true\n      }\n    ]\n  },\n  \"message\": \"The address Warehouse Address deleted successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Archive Customers","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers/archive","host":["{{baseUrl}}"],"path":["api","customers","archive"]},"description":"Bulk archive customers by IDs or by applying to all filtered results.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    4,\n    5,\n    6\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/customers/archive","host":["{{baseUrl}}"],"path":["api","customers","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 customers archived successfully.\"\n}"}]},{"name":"Bulk Unarchive Customers","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers/unarchive","host":["{{baseUrl}}"],"path":["api","customers","unarchive"]},"description":"Bulk unarchive customers by IDs or by applying to all filtered results.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    4,\n    5,\n    6\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/customers/unarchive","host":["{{baseUrl}}"],"path":["api","customers","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 customers unarchived successfully.\"\n}"}]},{"name":"Check Deletable Customers","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers/deletable","host":["{{baseUrl}}"],"path":["api","customers","deletable"]},"description":"Check which customers can be deleted. Returns deletability status for each customer.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ],\n  \"scope\": \"ids\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/customers/deletable","host":["{{baseUrl}}"],"path":["api","customers","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"John Doe\",\n      \"deletable\": false,\n      \"reason\": \"Linked to sales orders\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Jane Smith\",\n      \"deletable\": true,\n      \"reason\": null\n    }\n  ]\n}"}]},{"name":"Archive Customer","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/archive","host":["{{baseUrl}}"],"path":["api","customers",":customer","archive"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]},"description":"Archive a single customer.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/customers/{customer}/archive","host":["{{baseUrl}}"],"path":["api","customers",":customer","archive"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"customer John Doe archived successfully\"\n}"}]},{"name":"Unarchive Customer","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/unarchived","host":["{{baseUrl}}"],"path":["api","customers",":customer","unarchived"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]},"description":"Unarchive a previously archived customer.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/customers/{customer}/unarchived","host":["{{baseUrl}}"],"path":["api","customers",":customer","unarchived"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"customer John Doe unarchived successfully\"\n}"}]},{"name":"Preview Customers Import","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/customers/import/preview","host":["{{baseUrl}}"],"path":["api","customers","import","preview"]},"description":"Preview a customer import from a CSV file before committing.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/customers/import/preview","host":["{{baseUrl}}"],"path":["api","customers","import","preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total\": 50,\n    \"valid\": 48,\n    \"errors\": 2\n  }\n}"}]},{"name":"Import Customers","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/customers/import","host":["{{baseUrl}}"],"path":["api","customers","import"]},"description":"Import customers from a CSV file.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/customers/import","host":["{{baseUrl}}"],"path":["api","customers","import"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"imported\": 48,\n    \"skipped\": 2,\n    \"errors\": []\n  },\n  \"message\": \"Import completed successfully\"\n}"}]},{"name":"Find Customer Match","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/find-match?name=John Doe&zip=90210&address1=123 Main St","host":["{{baseUrl}}"],"path":["api","customers","find-match"],"query":[{"key":"name","value":"John Doe","description":"Customer name (required)","disabled":true},{"key":"zip","value":"90210","description":"ZIP code (required)","disabled":true},{"key":"address1","value":"123 Main St","description":"Address line 1 (required) — uses 90% fuzzy match","disabled":true}]},"description":"Find customer matches based on name, ZIP code, and address. Uses approximate matching (90% match on address1).\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/find-match?name=John Doe&zip=90210&address1=123 Main St","host":["{{baseUrl}}"],"path":["api","customers","find-match"],"query":[{"key":"name","value":"John Doe","description":"Customer name (required)","disabled":true},{"key":"zip","value":"90210","description":"ZIP code (required)","disabled":true},{"key":"address1","value":"123 Main St","description":"Address line 1 (required) — uses 90% fuzzy match","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"John Doe\"\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Get Open Sales Credits","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/open-sales-credits","host":["{{baseUrl}}"],"path":["api","customers",":customer","open-sales-credits"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]},"description":"Get all open (un-fully-allocated) sales credits for a customer.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/{customer}/open-sales-credits","host":["{{baseUrl}}"],"path":["api","customers",":customer","open-sales-credits"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5,\n      \"sales_credit_number\": \"SC-0005\",\n      \"total_credit\": 250,\n      \"status\": \"open\"\n    }\n  ]\n}"}]},{"name":"Get Unpaid Orders","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/unpaid-orders","host":["{{baseUrl}}"],"path":["api","customers",":customer","unpaid-orders"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]},"description":"Get all unpaid sales orders for a customer.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/{customer}/unpaid-orders","host":["{{baseUrl}}"],"path":["api","customers",":customer","unpaid-orders"],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 10,\n      \"sales_order_number\": \"SO-0010\",\n      \"total\": 500,\n      \"total_paid\": 0\n    }\n  ]\n}"}]},{"name":"Allocate Sales Credit to Orders","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/sales-credits/{salesCredit}/allocate","host":["{{baseUrl}}"],"path":["api","customers",":customer","sales-credits",":salesCredit","allocate"],"variable":[{"key":"customer","value":"1","description":"Customer ID"},{"key":"salesCredit","value":"5","description":"Sales Credit ID"}]},"description":"Allocate an open sales credit to one or more sales orders.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"sales_orders\": [\n    {\n      \"id\": 10,\n      \"amount\": 250\n    },\n    {\n      \"id\": 11,\n      \"amount\": 100\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/customers/{customer}/sales-credits/{salesCredit}/allocate","host":["{{baseUrl}}"],"path":["api","customers",":customer","sales-credits",":salesCredit","allocate"],"variable":[{"key":"customer","value":"1","description":"Customer ID"},{"key":"salesCredit","value":"5","description":"Sales Credit ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Sales credit allocated successfully.\"\n}"}]},{"name":"Unallocate Sales Credit from Order","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/sales-credits/{salesCredit}/allocations/{allocation}","host":["{{baseUrl}}"],"path":["api","customers",":customer","sales-credits",":salesCredit","allocations",":allocation"],"variable":[{"key":"customer","value":"1","description":"Customer ID"},{"key":"salesCredit","value":"5","description":"Sales Credit ID"},{"key":"allocation","value":"12","description":"Sales Credit Allocation ID"}]},"description":"Reverse a single sales credit allocation. Deletes the allocation row, reverses the corresponding Credit-type Payment recorded against the sales order, and recalculates the credit's payment_status (paid → partially_paid or open).\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nIf the allocation was previously synced to QuickBooks Online, a background job is dispatched after the local DB transaction commits to delete the paired QBO Payment.\n\nScoping behavior:\n- Returns 404 if the credit does not belong to the customer in the URL\n- Returns 404 if the allocation does not belong to the credit in the URL\n\nValidation:\n- Returns 422 if the sales credit is archived (unarchive before reversing allocations)\n- Returns 422 if the allocation has already been synced to Xero (Xero CreditNote Allocations are functionally immutable once accepted — unallocate in Xero first, then refresh)\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/customers/{customer}/sales-credits/{salesCredit}/allocations/{allocation}","host":["{{baseUrl}}"],"path":["api","customers",":customer","sales-credits",":salesCredit","allocations",":allocation"],"variable":[{"key":"customer","value":"1","description":"Customer ID"},{"key":"salesCredit","value":"5","description":"Sales Credit ID"},{"key":"allocation","value":"12","description":"Sales Credit Allocation ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"sales credit allocation 12 was deleted.\"\n}"}]},{"name":"Submit Orders to Starshipit","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/starshipit/submit-orders","host":["{{baseUrl}}"],"path":["api","customers","starshipit","submit-orders"]},"description":"**This GET dispatches a background job** (queues a bulk submit of every open Starshipit fulfillment).\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nSubmit pending orders to Starshipit shipping integration.\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/starshipit/submit-orders","host":["{{baseUrl}}"],"path":["api","customers","starshipit","submit-orders"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"submitted\": 5,\n    \"failed\": 0\n  },\n  \"message\": \"Orders submitted to Starshipit.\"\n}"}]},{"name":"Update Starshipit Order from Fulfillment","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/customers/starshipit/update-order/{sales_order_fulfillment}","host":["{{baseUrl}}"],"path":["api","customers","starshipit","update-order",":sales_order_fulfillment"],"variable":[{"key":"sales_order_fulfillment","value":"1","description":"Sales Order Fulfillment ID"}]},"description":"Update an existing order in Starshipit from a sales order fulfillment.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/customers/starshipit/update-order/{sales_order_fulfillment}","host":["{{baseUrl}}"],"path":["api","customers","starshipit","update-order",":sales_order_fulfillment"],"variable":[{"key":"sales_order_fulfillment","value":"1","description":"Sales Order Fulfillment ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Starshipit order updated.\"\n}"}]},{"name":"View Starshipit Order","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/starshipit/view-order/{sales_order_fulfillment}","host":["{{baseUrl}}"],"path":["api","customers","starshipit","view-order",":sales_order_fulfillment"],"variable":[{"key":"sales_order_fulfillment","value":"1","description":"Sales Order Fulfillment ID"}]},"description":"View the Starshipit order for a sales order fulfillment.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/starshipit/view-order/{sales_order_fulfillment}","host":["{{baseUrl}}"],"path":["api","customers","starshipit","view-order",":sales_order_fulfillment"],"variable":[{"key":"sales_order_fulfillment","value":"1","description":"Sales Order Fulfillment ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"order_number\": \"SF-1001\",\n    \"status\": \"pending\",\n    \"carrier\": \"Australia Post\"\n  }\n}"}]},{"name":"List SKU Mappings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer_id}/sku-mappings","host":["{{baseUrl}}"],"path":["api","customers",":customer_id","sku-mappings"],"variable":[{"key":"customer_id","value":"","description":"The customer ID."}]},"description":"Return a paginated list of SKU mappings for the specified customer. Each mapping links a customer-facing SKU code to an internal product.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."}},{"name":"Create / Upsert SKU Mapping","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers/{customer_id}/sku-mappings","host":["{{baseUrl}}"],"path":["api","customers",":customer_id","sku-mappings"],"variable":[{"key":"customer_id","value":"","description":"The customer ID."}]},"description":"Create a new SKU mapping or update an existing one (upsert) for the given customer. If a mapping for customer_sku already exists, it is updated with the new product_id.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"customer_sku\": \"CUST-SKU-001\",\n  \"product_id\": 1\n}","options":{"raw":{"language":"json"}}}}},{"name":"Delete SKU Mapping","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer_id}/sku-mappings/{mapping_id}","host":["{{baseUrl}}"],"path":["api","customers",":customer_id","sku-mappings",":mapping_id"],"variable":[{"key":"customer_id","value":"","description":"The customer ID."},{"key":"mapping_id","value":"","description":"The mapping ID."}]},"description":"Delete a specific SKU mapping by ID for the given customer.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."}},{"name":"Import SKU Mappings from CSV","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer_id}/sku-mappings/import","host":["{{baseUrl}}"],"path":["api","customers",":customer_id","sku-mappings","import"],"variable":[{"key":"customer_id","value":"","description":"The customer ID."}]},"description":"Bulk-import SKU mappings for a customer from a CSV file. The CSV must have a header row with columns customer_sku and product_id. Existing mappings for the same customer_sku are updated (upsert behaviour).\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}}},{"name":"Export SKU Mappings as CSV","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer_id}/sku-mappings/export","host":["{{baseUrl}}"],"path":["api","customers",":customer_id","sku-mappings","export"],"variable":[{"key":"customer_id","value":"","description":"The customer ID."}]},"description":"Export all SKU mappings for a customer as a CSV file download. The response is a CSV with columns customer_sku and product_id. Use this as a template for the import endpoint.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."}},{"name":"List Customer Contacts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/customers/{customer}/contacts","host":["{{baseUrl}}"],"path":["api","v2","customers",":customer","contacts"],"variable":[{"key":"customer","value":"12","description":"Customer ID"}]},"description":"List all contacts attached to a customer, paginated. Supports search (title/notes), role/primary/has-email filters, advanced per-column filters, and sorting.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n`portal_user` reports the customer-portal login granted to this contact, or null when they have none — portal access is a property of the contact, so there is one list of people per customer. The field is absent for supplier and warehouse contacts, which have no portal."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/customers/{customer}/contacts","host":["{{baseUrl}}"],"path":["api","v2","customers",":customer","contacts"],"variable":[{"key":"customer","value":"12","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 42,\n      \"name\": \"Jane Buyer\",\n      \"email\": \"jane.buyer@example.com\",\n      \"phone\": \"+1 415-555-0100\",\n      \"role\": \"accounts_payable\",\n      \"role_label\": \"Accounts Payable\",\n      \"title\": \"AP Specialist\",\n      \"is_primary\": true,\n      \"is_messaging\": false,\n      \"notes\": \"Prefers email over phone.\",\n      \"portal_user\": {\n        \"id\": 88,\n        \"role\": \"approver\",\n        \"status\": \"active\",\n        \"last_login_at\": \"2026-09-01T09:12:00+00:00\"\n      },\n      \"link_type\": \"customer\",\n      \"link_id\": 12,\n      \"created_at\": \"2026-07-13T14:22:05+00:00\",\n      \"updated_at\": \"2026-07-13T14:22:05+00:00\"\n    },\n    {\n      \"id\": 43,\n      \"name\": \"Sam Sales\",\n      \"email\": \"sam.sales@example.com\",\n      \"phone\": \"+1 415-555-0100\",\n      \"role\": \"sales_rep\",\n      \"role_label\": \"Sales Rep\",\n      \"title\": \"Account Executive\",\n      \"is_primary\": false,\n      \"is_messaging\": false,\n      \"notes\": \"Prefers email over phone.\",\n      \"link_type\": \"customer\",\n      \"link_id\": 12,\n      \"created_at\": \"2026-07-13T14:22:05+00:00\",\n      \"updated_at\": \"2026-07-13T14:22:05+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 2,\n  \"from\": 1,\n  \"to\": 2\n}"}]},{"name":"Create Customer Contact","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/customers/{customer}/contacts","host":["{{baseUrl}}"],"path":["api","v2","customers",":customer","contacts"],"variable":[{"key":"customer","value":"12","description":"Customer ID"}]},"description":"Add a new contact to a customer. Fields: name (required), email, phone, role (default general), title, is_primary, notes (max 500). The first contact added to a customer automatically becomes primary; setting is_primary demotes the previous primary.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRole options: general, sales_rep, account_manager, customer_service, accounts_payable, accounts_receivable, purchasing, shipping_receiving, warehouse_manager, operations, billing, owner, other.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"name\": \"Jane Buyer\",\n  \"email\": \"jane.buyer@example.com\",\n  \"phone\": \"+1 415-555-0100\",\n  \"role\": \"accounts_payable\",\n  \"title\": \"AP Specialist\",\n  \"is_primary\": true,\n  \"is_messaging\": false,\n  \"notes\": \"Prefers email over phone.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/customers/{customer}/contacts","host":["{{baseUrl}}"],"path":["api","v2","customers",":customer","contacts"],"variable":[{"key":"customer","value":"12","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"name\": \"Jane Buyer\",\n    \"email\": \"jane.buyer@example.com\",\n    \"phone\": \"+1 415-555-0100\",\n    \"role\": \"accounts_payable\",\n    \"role_label\": \"Accounts Payable\",\n    \"title\": \"AP Specialist\",\n    \"is_primary\": true,\n    \"is_messaging\": false,\n    \"notes\": \"Prefers email over phone.\",\n    \"portal_user\": {\n      \"id\": 88,\n      \"role\": \"approver\",\n      \"status\": \"active\",\n      \"last_login_at\": \"2026-09-01T09:12:00+00:00\"\n    },\n    \"link_type\": \"customer\",\n    \"link_id\": 12,\n    \"created_at\": \"2026-07-13T14:22:05+00:00\",\n    \"updated_at\": \"2026-07-13T14:22:05+00:00\"\n  }\n}"}]},{"name":"Export Contacts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/customers/{customer}/contacts/export?format=csv","host":["{{baseUrl}}"],"path":["api","v2","customers",":customer","contacts","export"],"query":[{"key":"format","value":"csv","description":"Export format: csv (default) or xlsx","disabled":true}],"variable":[{"key":"customer","value":"1","description":"Customer ID"}]},"description":"Download all contacts belonging to the customer as a CSV or XLSX file. Columns: Contact ID, Name, Email, Phone, Role, Title, Primary, Notes, Created At, Updated At. The file mirrors the contacts bulk-import template, so an exported file can be edited and re-imported (rows with a Contact ID or a matching email update the existing contact; others are created).\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nOptional comma-separated column keys (`name,role,title,email,phone,notes,created_at`) to narrow the exported fields; `Contact ID` is always included so the file can round-trip as an update. Omit for the full field set.\n\nOptional. `filtered` applies the request's filter/sort parameters (same syntax as the list endpoint) instead of exporting every contact."}},{"name":"Get Customer Contact","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/customers/{customer}/contacts/{contact}","host":["{{baseUrl}}"],"path":["api","v2","customers",":customer","contacts",":contact"],"variable":[{"key":"customer","value":"12","description":"Customer ID"},{"key":"contact","value":"42","description":"Contact ID"}]},"description":"Get a single contact belonging to the customer. A contact id that belongs to a different customer returns 404.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n`portal_user` reports the customer-portal login granted to this contact, or null when they have none — portal access is a property of the contact, so there is one list of people per customer. The field is absent for supplier and warehouse contacts, which have no portal."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/customers/{customer}/contacts/{contact}","host":["{{baseUrl}}"],"path":["api","v2","customers",":customer","contacts",":contact"],"variable":[{"key":"customer","value":"12","description":"Customer ID"},{"key":"contact","value":"42","description":"Contact ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"name\": \"Jane Buyer\",\n    \"email\": \"jane.buyer@example.com\",\n    \"phone\": \"+1 415-555-0100\",\n    \"role\": \"accounts_payable\",\n    \"role_label\": \"Accounts Payable\",\n    \"title\": \"AP Specialist\",\n    \"is_primary\": true,\n    \"is_messaging\": false,\n    \"notes\": \"Prefers email over phone.\",\n    \"portal_user\": {\n      \"id\": 88,\n      \"role\": \"approver\",\n      \"status\": \"active\",\n      \"last_login_at\": \"2026-09-01T09:12:00+00:00\"\n    },\n    \"link_type\": \"customer\",\n    \"link_id\": 12,\n    \"created_at\": \"2026-07-13T14:22:05+00:00\",\n    \"updated_at\": \"2026-07-13T14:22:05+00:00\"\n  }\n}"}]},{"name":"Update Customer Contact","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/customers/{customer}/contacts/{contact}","host":["{{baseUrl}}"],"path":["api","v2","customers",":customer","contacts",":contact"],"variable":[{"key":"customer","value":"12","description":"Customer ID"},{"key":"contact","value":"42","description":"Contact ID"}]},"description":"Update a contact belonging to the customer. Full update — name is required. Setting is_primary promotes this contact and demotes the previous primary.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRole options: general, sales_rep, account_manager, customer_service, accounts_payable, accounts_receivable, purchasing, shipping_receiving, warehouse_manager, operations, billing, owner, other.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"name\": \"Jane Buyer\",\n  \"email\": \"jane.buyer@example.com\",\n  \"phone\": \"+1 415-555-0100\",\n  \"role\": \"accounts_payable\",\n  \"title\": \"AP Specialist\",\n  \"is_primary\": true,\n  \"is_messaging\": false,\n  \"notes\": \"Prefers email over phone.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/customers/{customer}/contacts/{contact}","host":["{{baseUrl}}"],"path":["api","v2","customers",":customer","contacts",":contact"],"variable":[{"key":"customer","value":"12","description":"Customer ID"},{"key":"contact","value":"42","description":"Contact ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"name\": \"Jane Buyer\",\n    \"email\": \"jane.buyer@example.com\",\n    \"phone\": \"+1 415-555-0100\",\n    \"role\": \"accounts_payable\",\n    \"role_label\": \"Accounts Payable\",\n    \"title\": \"AP Specialist\",\n    \"is_primary\": true,\n    \"is_messaging\": false,\n    \"notes\": \"Prefers email over phone.\",\n    \"portal_user\": {\n      \"id\": 88,\n      \"role\": \"approver\",\n      \"status\": \"active\",\n      \"last_login_at\": \"2026-09-01T09:12:00+00:00\"\n    },\n    \"link_type\": \"customer\",\n    \"link_id\": 12,\n    \"created_at\": \"2026-07-13T14:22:05+00:00\",\n    \"updated_at\": \"2026-07-13T14:22:05+00:00\"\n  }\n}"}]},{"name":"Delete Customer Contact","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/customers/{customer}/contacts/{contact}","host":["{{baseUrl}}"],"path":["api","v2","customers",":customer","contacts",":contact"],"variable":[{"key":"customer","value":"12","description":"Customer ID"},{"key":"contact","value":"42","description":"Contact ID"}]},"description":"Delete a contact from the customer. If the deleted contact was primary and other contacts remain, the oldest remaining contact is promoted to primary.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/customers/{customer}/contacts/{contact}","host":["{{baseUrl}}"],"path":["api","v2","customers",":customer","contacts",":contact"],"variable":[{"key":"customer","value":"12","description":"Customer ID"},{"key":"contact","value":"42","description":"Contact ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"message\": \"Contact deleted.\"\n}"}]},{"name":"Set Customer Contact as Primary","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/customers/{customer}/contacts/{contact}/make-primary","host":["{{baseUrl}}"],"path":["api","v2","customers",":customer","contacts",":contact","make-primary"],"variable":[{"key":"customer","value":"12","description":"Customer ID"},{"key":"contact","value":"42","description":"Contact ID"}]},"description":"Promote a contact to be the customer's primary contact, demoting the previous primary.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/customers/{customer}/contacts/{contact}/make-primary","host":["{{baseUrl}}"],"path":["api","v2","customers",":customer","contacts",":contact","make-primary"],"variable":[{"key":"customer","value":"12","description":"Customer ID"},{"key":"contact","value":"42","description":"Contact ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"name\": \"Jane Buyer\",\n    \"email\": \"jane.buyer@example.com\",\n    \"phone\": \"+1 415-555-0100\",\n    \"role\": \"accounts_payable\",\n    \"role_label\": \"Accounts Payable\",\n    \"title\": \"AP Specialist\",\n    \"is_primary\": true,\n    \"is_messaging\": false,\n    \"notes\": \"Prefers email over phone.\",\n    \"portal_user\": {\n      \"id\": 88,\n      \"role\": \"approver\",\n      \"status\": \"active\",\n      \"last_login_at\": \"2026-09-01T09:12:00+00:00\"\n    },\n    \"link_type\": \"customer\",\n    \"link_id\": 12,\n    \"created_at\": \"2026-07-13T14:22:05+00:00\",\n    \"updated_at\": \"2026-07-13T14:22:05+00:00\"\n  }\n}"}]},{"name":"Get Store Credit Balance","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/store-credit","host":["{{baseUrl}}"],"path":["api","customers",":customer","store-credit"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]},"description":"Returns the customer's current store-credit wallet balance — the running sum of every ledger entry.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (PAT)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/{customer}/store-credit","host":["{{baseUrl}}"],"path":["api","customers",":customer","store-credit"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"customer_id\": 501,\n    \"balance\": 42.5\n  }\n}"}]},{"name":"Issue Store Credit","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/store-credit","host":["{{baseUrl}}"],"path":["api","customers",":customer","store-credit"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]},"description":"Manually issue (or adjust) store credit for a customer.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest body:\n• `amount` (required, numeric, non-zero) — magnitude for signed reasons (goodwill/top_up add, expiry removes); for an `adjustment` the sign is honored, so a negative value removes credit.\n• `reason` (required) — one of the human-selectable reasons: `goodwill`, `top_up`, `adjustment`, `expiry`. System reasons (`refund`, `overpayment`, `redemption`, `reversal`) are rejected here.\n• `notes` (optional, string) — free-text note.\n• `reference` (optional, string) — external reference; defaults to an auto-generated SC-###### code.\n\nAn adjustment/expiry that would drive the wallet below zero returns 422.\n\nAuthentication: Requires Bearer token (PAT).\nRequires permission: customers.update.","body":{"mode":"raw","raw":"{\n  \"amount\": 25,\n  \"reason\": \"goodwill\",\n  \"notes\": \"Apology for the mixed-up order\",\n  \"reference\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/customers/{customer}/store-credit","host":["{{baseUrl}}"],"path":["api","customers",":customer","store-credit"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 123,\n    \"customer_id\": 501,\n    \"reference\": \"SC-000123\",\n    \"amount\": 25,\n    \"balance_after\": 25,\n    \"reason\": \"goodwill\",\n    \"reason_label\": \"Goodwill\",\n    \"is_credit\": true,\n    \"currency_id\": 1,\n    \"notes\": \"Apology for the mixed-up order\",\n    \"source_type\": null,\n    \"source_id\": null,\n    \"created_by_user_id\": 1,\n    \"created_by_name\": \"Jane Cashier\",\n    \"created_at\": \"2026-07-06T14:15:43.000000Z\"\n  },\n  \"balance\": 42.5,\n  \"message\": \"Store credit issued.\"\n}"}]},{"name":"Get Store Credit History","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/store-credit/history","host":["{{baseUrl}}"],"path":["api","customers",":customer","store-credit","history"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]},"description":"Paginated ledger history for the customer's store-credit wallet, newest first. Each entry records a signed movement (`amount`), the running `balance_after`, the reason, and — for system-driven entries — the polymorphic source (e.g. a POS transaction for a refund-to-credit).\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (PAT)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/{customer}/store-credit/history","host":["{{baseUrl}}"],"path":["api","customers",":customer","store-credit","history"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 123,\n      \"customer_id\": 501,\n      \"reference\": \"SC-000123\",\n      \"amount\": 25,\n      \"balance_after\": 25,\n      \"reason\": \"goodwill\",\n      \"reason_label\": \"Goodwill\",\n      \"is_credit\": true,\n      \"currency_id\": 1,\n      \"notes\": \"Apology for the mixed-up order\",\n      \"source_type\": null,\n      \"source_id\": null,\n      \"created_by_user_id\": 1,\n      \"created_by_name\": \"Jane Cashier\",\n      \"created_at\": \"2026-07-06T14:15:43.000000Z\"\n    },\n    {\n      \"id\": 120,\n      \"customer_id\": 501,\n      \"reference\": \"SC-000120\",\n      \"amount\": -17.5,\n      \"balance_after\": 17.5,\n      \"reason\": \"redemption\",\n      \"reason_label\": \"Redemption\",\n      \"is_credit\": false,\n      \"currency_id\": 1,\n      \"notes\": null,\n      \"source_type\": \"App\\\\Models\\\\SalesOrder\",\n      \"source_id\": 5012,\n      \"created_by_user_id\": 1,\n      \"created_by_name\": \"Jane Cashier\",\n      \"created_at\": \"2026-07-05T10:00:00.000000Z\"\n    }\n  ],\n  \"links\": {\n    \"first\": \"...\",\n    \"last\": \"...\",\n    \"prev\": null,\n    \"next\": null\n  },\n  \"meta\": {\n    \"current_page\": 1,\n    \"from\": 1,\n    \"last_page\": 1,\n    \"per_page\": 10,\n    \"to\": 2,\n    \"total\": 2\n  }\n}"}]},{"name":"Get Customer AR Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/ar-summary","host":["{{baseUrl}}"],"path":["api","customers",":customer","ar-summary"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]},"description":"Returns the customer's accounts-receivable summary in one call: outstanding and overdue totals, aging buckets, open credits, net receivable, credit headroom, informational wallet balances, the most recent payment, and the head of the overdue worklist.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAll amounts are in the account's base currency (`currency`).\n\nResponse fields:\n- `outstanding_total` — sum of balance due across the customer's open sales orders.\n- `overdue` — `amount`, `count`, and `oldest_due_date` of orders past their due date.\n- `aging` — outstanding amount bucketed by days past due: `current` (not yet due), `d1_30`, `d31_60`, `d61_90`, `d90_plus`.\n- `open_credits` — `amount` and `count` of open sales credits with an unallocated balance.\n- `net_receivable` — `outstanding_total` minus `open_credits.amount`. Negative means you owe the customer.\n- `credit_limit` / `available_credit` — `available_credit` is `credit_limit` minus `exposure`; both null when no credit limit is set.\n- `house_account_balance` / `store_credit_balance` — informational wallet balances. Store credit is a prepaid liability and is never netted into `net_receivable`.\n- `last_payment` — `date`, `amount`, and `id` of the most recent payment against the customer's orders (a convenience hint, not an accounting figure); null when the customer has never paid.\n- `last_statement_sent_at` — when a statement email was last sent to this customer (ISO 8601, or null).\n- `overdue_items` — up to 5 overdue receivable rows, oldest due date first (same row shape as the receivables list).\n- `open_count` — number of open receivable rows.\n- `payment_terms_days` — payment terms used to derive due dates (customer override, else the account default).\n- `exposure` — `outstanding_total` minus `open_credits.amount`: the amount measured against the credit limit.\n- `credit_hold` — true while the customer is under a manual credit hold; every new unpaid order is held in Reserved until released.\n- `payment_term` — `id` and `name` of the customer's default payment term for sales orders, or null when the customer has none.\n- `credit_enforcement_mode` — how credit limits are enforced for new orders: `off`, `warn`, or `hold`.\n\nDue dates are derived as order date + payment terms.\n\nAuthentication: Requires Bearer token (PAT). Requires permissions `customers.show` and `sales_orders.index`, and a token with the orders scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/{customer}/ar-summary","host":["{{baseUrl}}"],"path":["api","customers",":customer","ar-summary"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"currency\": \"USD\",\n    \"outstanding_total\": 764.38,\n    \"overdue\": {\n      \"amount\": 219.88,\n      \"count\": 1,\n      \"oldest_due_date\": \"2026-06-01\"\n    },\n    \"aging\": {\n      \"current\": 544.5,\n      \"d1_30\": 0,\n      \"d31_60\": 219.88,\n      \"d61_90\": 0,\n      \"d90_plus\": 0\n    },\n    \"open_credits\": {\n      \"amount\": 30,\n      \"count\": 1\n    },\n    \"net_receivable\": 734.38,\n    \"credit_limit\": 5000,\n    \"available_credit\": 4265.62,\n    \"house_account_balance\": 120,\n    \"store_credit_balance\": 42.5,\n    \"last_payment\": {\n      \"date\": \"2026-07-02\",\n      \"amount\": 95.5,\n      \"id\": 9911\n    },\n    \"last_statement_sent_at\": \"2026-07-01T15:04:05+00:00\",\n    \"overdue_items\": [\n      {\n        \"id\": 8123,\n        \"number\": \"SO-01021\",\n        \"date\": \"2026-05-02\",\n        \"due_date\": \"2026-06-01\",\n        \"days_until_due\": -46,\n        \"payment_due_status\": \"overdue\",\n        \"order_status\": \"open\",\n        \"payment_status\": \"unpaid\",\n        \"total\": 219.88,\n        \"total_paid\": 0,\n        \"balance_due\": 219.88,\n        \"balance_due_in_tenant_currency\": 219.88,\n        \"currency\": \"USD\"\n      }\n    ],\n    \"open_count\": 2,\n    \"payment_terms_days\": 30,\n    \"exposure\": 734.38,\n    \"credit_hold\": false,\n    \"payment_term\": {\n      \"id\": 4,\n      \"name\": \"Net 30\"\n    },\n    \"credit_enforcement_mode\": \"warn\"\n  }\n}"}]},{"name":"List Customer Receivables","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/receivables","host":["{{baseUrl}}"],"path":["api","customers",":customer","receivables"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]},"description":"Paginated receivables worklist for one customer — each row is a sales order reduced to its receivable shape.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRow fields: `number`, `date`, `due_date` (order date + payment terms), `days_until_due` (negative when past due), `payment_due_status` (`overdue`, `due_soon` — due within 7 days, `current`, or `no_due_date`), `order_status`, `payment_status`, `total`, `total_paid`, and `balance_due` in the order's own currency, `balance_due_in_tenant_currency` converted to the account's base currency at the order's exchange rate, and `currency`.\n\nFiltering:\n- `filter[status]` — `open` (default: any order with a balance due), `overdue` (balance due and past the due date), `due_7d` (balance due within the next 7 days), `paid` (settled orders), `all`.\n- `filter[search]` — matches the order number.\n\nSorting: `sort` accepts `due_date` (default), `-due_date`, `date`, `-date`.\n\nPagination: `page` (default 1) and `per_page` (default 10, max 100).\n\nAuthentication: Requires Bearer token (PAT). Requires permissions `customers.show` and `sales_orders.index`, and a token with the orders scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/{customer}/receivables","host":["{{baseUrl}}"],"path":["api","customers",":customer","receivables"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 8123,\n      \"number\": \"SO-01021\",\n      \"date\": \"2026-05-02\",\n      \"due_date\": \"2026-06-01\",\n      \"days_until_due\": -46,\n      \"payment_due_status\": \"overdue\",\n      \"order_status\": \"open\",\n      \"payment_status\": \"unpaid\",\n      \"total\": 219.88,\n      \"total_paid\": 0,\n      \"balance_due\": 219.88,\n      \"balance_due_in_tenant_currency\": 219.88,\n      \"currency\": \"USD\"\n    },\n    {\n      \"id\": 8156,\n      \"number\": \"SO-01044\",\n      \"date\": \"2026-07-06\",\n      \"due_date\": \"2026-07-20\",\n      \"days_until_due\": 3,\n      \"payment_due_status\": \"due_soon\",\n      \"order_status\": \"open\",\n      \"payment_status\": \"partially_paid\",\n      \"total\": 640,\n      \"total_paid\": 95.5,\n      \"balance_due\": 544.5,\n      \"balance_due_in_tenant_currency\": 544.5,\n      \"currency\": \"USD\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 2,\n  \"from\": 1,\n  \"to\": 2\n}"}]},{"name":"Get Customer Statement","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/statement","host":["{{baseUrl}}"],"path":["api","customers",":customer","statement"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]},"description":"Returns the customer's statement of account as JSON, in one of two views selected by `type`:\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n- `open_item` (default) — every open sales order plus open sales credits with an unallocated balance, and a `totals` block (`open`, `credits`, `net_receivable`). `as_at` optionally limits the statement to documents dated on or before that date (balances remain current values).\n- `activity` — a chronological ledger for the `from`/`to` window (both required for this view): an opening balance carried from before the period, then every receivable-affecting event with a running balance — orders (debits), payments (credits), refunds (debits), and issued sales credits (credits) — satisfying opening + debits - credits = closing. Line `type` is one of `order`, `payment`, `refund`, `credit_issued`; each line links its source document via `link.entity` (`sales_order` or `sales_credit`) and `link.id`.\n\nAll amounts are in the account's base currency; foreign-currency documents are converted at each order's snapshot exchange rate. Both views also include the customer header, the aging buckets, and the account's configured `payment_instructions`.\n\nUse the export endpoint for a PDF or CSV download of the same statement.\n\nAuthentication: Requires Bearer token (PAT). Requires permissions `customers.show` and `sales_orders.index`, and a token with the orders scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/{customer}/statement","host":["{{baseUrl}}"],"path":["api","customers",":customer","statement"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"customer\": {\n      \"id\": 501,\n      \"name\": \"Jane Smith\",\n      \"company\": \"Acme Retail LLC\",\n      \"email\": \"ap@acmeretail.com\"\n    },\n    \"as_of\": \"2026-07-17\",\n    \"currency\": \"USD\",\n    \"open_items\": [\n      {\n        \"id\": 8123,\n        \"number\": \"SO-01021\",\n        \"date\": \"2026-05-02\",\n        \"due_date\": \"2026-06-01\",\n        \"days_until_due\": -46,\n        \"payment_due_status\": \"overdue\",\n        \"order_status\": \"open\",\n        \"payment_status\": \"unpaid\",\n        \"total\": 219.88,\n        \"total_paid\": 0,\n        \"balance_due\": 219.88,\n        \"balance_due_in_tenant_currency\": 219.88,\n        \"currency\": \"USD\"\n      },\n      {\n        \"id\": 8156,\n        \"number\": \"SO-01044\",\n        \"date\": \"2026-07-06\",\n        \"due_date\": \"2026-07-20\",\n        \"days_until_due\": 3,\n        \"payment_due_status\": \"due_soon\",\n        \"order_status\": \"open\",\n        \"payment_status\": \"partially_paid\",\n        \"total\": 640,\n        \"total_paid\": 95.5,\n        \"balance_due\": 544.5,\n        \"balance_due_in_tenant_currency\": 544.5,\n        \"currency\": \"USD\"\n      }\n    ],\n    \"credits\": [\n      {\n        \"id\": 55,\n        \"number\": \"SC-00055\",\n        \"date\": \"2026-06-18\",\n        \"available\": 30\n      }\n    ],\n    \"totals\": {\n      \"open\": 764.38,\n      \"credits\": 30,\n      \"net_receivable\": 734.38\n    },\n    \"aging\": {\n      \"current\": 544.5,\n      \"d1_30\": 0,\n      \"d31_60\": 219.88,\n      \"d61_90\": 0,\n      \"d90_plus\": 0\n    },\n    \"payment_instructions\": \"Pay by ACH to routing 021000021, account 1234567. Please reference your order number.\"\n  }\n}"}]},{"name":"Export Customer Statement","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/statement/export","host":["{{baseUrl}}"],"path":["api","customers",":customer","statement","export"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]},"description":"Downloads the customer's statement of account as a file. `format=pdf` (default) returns a branded PDF document (`application/pdf`); `format=csv` returns comma-separated rows (`text/csv`).\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAccepts the same statement parameters as the JSON statement endpoint: `type` (`open_item` default, or `activity`), `from`/`to` (required when `type=activity`), and `as_at` (`open_item` only).\n\nThe response is a file download named `customer-statement-{customer-name}-{date}.pdf` (or `.csv`) — not JSON.\n\nCSV columns:\n- `activity`: Date, Type, Reference, Description, Debit, Credit, Running Balance — with opening and closing balance rows.\n- `open_item`: Type, Document, Date, Due Date, Status, Document Currency, Total (Document Currency), Outstanding (base currency) — one row per open order, one negative row per open credit, and a final net_receivable row.\n\nAuthentication: Requires Bearer token (PAT). Requires permissions `customers.show` and `sales_orders.index`, and a token with the orders scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/{customer}/statement/export","host":["{{baseUrl}}"],"path":["api","customers",":customer","statement","export"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"Type,Document,Date,\"Due Date\",Status,\"Document Currency\",\"Total (Document Currency)\",\"Outstanding (USD)\"\nsales_order,SO-01021,2026-05-02,2026-06-01,unpaid,USD,219.88,219.88\nsales_order,SO-01044,2026-07-06,2026-07-20,partially_paid,USD,640.00,544.50\nsales_credit,SC-00055,2026-06-18,,open,USD,,-30\nnet_receivable,,,,,USD,,734.38\n"}]},{"name":"Preview Customer Statement Email","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/statement/preview","host":["{{baseUrl}}"],"path":["api","customers",":customer","statement","preview"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]},"description":"Preview the statement email that a send would produce — the exact From and Reply-To addresses, resolved recipient, subject and body — WITHOUT sending it or logging anything. Use it to show the sender and content before committing to send.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody is identical to Send Customer Statement: `type` (required — `open_item` or `activity`); for `activity`, `from` and `to` (Y-m-d) are required; `as_at` (Y-m-d) optionally dates an open-item statement; `recipient` overrides the customer's billing email; `subject` and `body` override the defaults and support the placeholders {customer_name}, {total_due}, {payment_instructions}.\n\nAuthentication: Requires Bearer token (PAT).","body":{"mode":"raw","raw":"{\n  \"type\": \"open_item\",\n  \"as_at\": \"2026-06-30\",\n  \"recipient\": \"ap@acmeretail.com\",\n  \"subject\": \"Statement of Account — {customer_name}\",\n  \"body\": \"Dear {customer_name},\\n\\nPlease find your statement of account attached. Your current balance is {total_due}.\\n\\n{payment_instructions}\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/customers/{customer}/statement/preview","host":["{{baseUrl}}"],"path":["api","customers",":customer","statement","preview"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"from\": {\n      \"address\": \"billing@acme-wholesale.example\",\n      \"name\": \"Acme Wholesale\"\n    },\n    \"reply_to\": {\n      \"address\": \"ar@acme-wholesale.example\",\n      \"name\": \"Acme Wholesale AR\"\n    },\n    \"recipient\": \"ap@acmeretail.com\",\n    \"subject\": \"Statement of Account — Acme Retail\",\n    \"body\": \"Dear Acme Retail,\\n\\nPlease find your statement of account attached. Your current balance is $764.38.\\n\\nTo pay, reply to this email or contact our accounts team.\",\n    \"attachment_filename\": \"customer-statement-acme-retail-2026-07-18.pdf\"\n  }\n}"}]},{"name":"Send Customer Statement","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/send-statement","host":["{{baseUrl}}"],"path":["api","customers",":customer","send-statement"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]},"description":"Emails the customer a statement of account with the PDF attached, and records the send in the communications log.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe statement is generated with the same parameters as the statement endpoints: `type` (required — `open_item` or `activity`), `from`/`to` (required when `type=activity`), and `as_at` (`open_item` only).\n\nThe recipient defaults to the customer's billing email, falling back to their primary email; pass `recipient` to override. `subject` and `body` are optional templates; these placeholders are replaced before sending: `{customer_name}`, `{total_due}` (the statement's closing balance or net receivable, formatted with currency), `{days_overdue}`, and `{payment_instructions}` (the account's configured payment instructions). When omitted, a default subject and body are used.\n\nReturns the logged communication entry.\n\nBody parameters:\n- type (string, required): `open_item` or `activity`.\n- from (date): Period start. Required when type is `activity`.\n- to (date): Period end, on or after `from`. Required when type is `activity`.\n- as_at (date, optional): `open_item` only — include only documents dated on or before this date.\n- recipient (string, email, optional): Override the destination email address.\n- subject (string, max 255, optional): Email subject template.\n- body (string, max 10000, optional): Email body template.\n\nErrors:\n- 422 when `type` is missing, `from`/`to` are missing for the activity view, or no recipient email can be resolved (the customer has no billing or primary email and no `recipient` was supplied).\n\nAuthentication: Requires Bearer token (PAT). Requires permission `customers.send_communications` and a token with the orders scope. Rate limited to 60 requests per minute.\n\nThe activity statement date range cannot exceed 366 days.","body":{"mode":"raw","raw":"{\n  \"type\": \"open_item\",\n  \"as_at\": \"2026-06-30\",\n  \"recipient\": \"ap@acmeretail.com\",\n  \"subject\": \"Statement of Account — {customer_name}\",\n  \"body\": \"Dear {customer_name},\\n\\nPlease find your statement of account attached. Your current balance is {total_due}.\\n\\n{payment_instructions}\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/customers/{customer}/send-statement","host":["{{baseUrl}}"],"path":["api","customers",":customer","send-statement"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Statement sent to ap@acmeretail.com.\",\n  \"data\": {\n    \"id\": 87,\n    \"customer_id\": 501,\n    \"sales_order_id\": null,\n    \"sales_order_number\": null,\n    \"type\": \"statement\",\n    \"scope\": \"account\",\n    \"statement_type\": \"open_item\",\n    \"params\": {\n      \"as_at\": \"2026-06-30\",\n      \"total_due\": 734.38\n    },\n    \"recipient\": \"ap@acmeretail.com\",\n    \"subject\": \"Statement of Account — Acme Retail LLC\",\n    \"sent_by\": 3,\n    \"sender_name\": \"Alex Rivera\",\n    \"created_at\": \"2026-07-01T15:04:05+00:00\"\n  }\n}"}]},{"name":"Request Customer Payment","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/request-payment","host":["{{baseUrl}}"],"path":["api","customers",":customer","request-payment"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]},"description":"Emails the customer an account-level payment request for their outstanding balance, and records the send in the communications log. To request payment for a single order instead, use the request-payment action on that sales order (Orders collection).\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe recipient defaults to the customer's billing email, falling back to their primary email; pass `recipient` to override. `subject` and `body` are optional templates; these placeholders are replaced before sending: `{customer_name}`, `{total_due}` (the customer's outstanding balance, formatted with currency), `{days_overdue}` (days since the oldest overdue due date), and `{payment_instructions}` (the account's configured payment instructions). When omitted, a default subject and body are used.\n\nSet `attach_statement` to true to attach the customer's current open-item statement as a PDF.\n\nReturns the logged communication entry.\n\nBody parameters (all optional):\n- recipient (string, email): Override the destination email address.\n- subject (string, max 255): Email subject template.\n- body (string, max 10000): Email body template.\n- attach_statement (boolean): Attach the open-item statement PDF. Default false.\n\nErrors:\n- 422 when no recipient email can be resolved (the customer has no billing or primary email and no `recipient` was supplied).\n\nAuthentication: Requires Bearer token (PAT). Requires permission `customers.send_communications` and a token with the orders scope. Rate limited to 60 requests per minute.","body":{"mode":"raw","raw":"{\n  \"recipient\": \"ap@acmeretail.com\",\n  \"subject\": \"Payment Request — {customer_name}\",\n  \"body\": \"Dear {customer_name},\\n\\nOur records show an outstanding balance of {total_due} on your account. Please arrange payment at your earliest convenience.\\n\\n{payment_instructions}\",\n  \"attach_statement\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/customers/{customer}/request-payment","host":["{{baseUrl}}"],"path":["api","customers",":customer","request-payment"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Payment request sent to ap@acmeretail.com.\",\n  \"data\": {\n    \"id\": 89,\n    \"customer_id\": 501,\n    \"sales_order_id\": null,\n    \"sales_order_number\": null,\n    \"type\": \"payment_request\",\n    \"scope\": \"account\",\n    \"statement_type\": null,\n    \"params\": {\n      \"total_due\": 764.38,\n      \"attach_statement\": true\n    },\n    \"recipient\": \"ap@acmeretail.com\",\n    \"subject\": \"Payment Request — Acme Retail LLC\",\n    \"sent_by\": 3,\n    \"sender_name\": \"Alex Rivera\",\n    \"created_at\": \"2026-07-17T09:12:03+00:00\"\n  }\n}"}]},{"name":"List Customer AR Communications","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/ar-communications","host":["{{baseUrl}}"],"path":["api","customers",":customer","ar-communications"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]},"description":"Paginated log of outbound receivables emails sent to this customer, newest first. Entries are created by the send-statement and request-payment actions (both the account-level actions here and the per-order request-payment action in the Orders collection).\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEntry fields: `type` (`statement` or `payment_request`), `scope` (`account`, or `order` with `sales_order_id` / `sales_order_number` set), `statement_type` (`open_item` or `activity` for statement sends, null otherwise), `params` (send parameters and amounts captured at send time, e.g. `total_due`, `amount_due`, `days_overdue`, `as_at`, `attach_statement`, `attach_invoice`), `recipient`, `subject`, `sent_by` / `sender_name` (the user who sent it), and `created_at`.\n\nPagination: `page` (default 1) and `per_page` (default 10).\n\nAuthentication: Requires Bearer token (PAT). Requires permissions `customers.show` and `sales_orders.index`, and a token with the orders scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/{customer}/ar-communications","host":["{{baseUrl}}"],"path":["api","customers",":customer","ar-communications"],"variable":[{"key":"customer","value":"501","description":"Customer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 88,\n      \"customer_id\": 501,\n      \"sales_order_id\": 8123,\n      \"sales_order_number\": \"SO-01021\",\n      \"type\": \"payment_request\",\n      \"scope\": \"order\",\n      \"statement_type\": null,\n      \"params\": {\n        \"amount_due\": 219.88,\n        \"days_overdue\": 46,\n        \"attach_invoice\": true\n      },\n      \"recipient\": \"ap@acmeretail.com\",\n      \"subject\": \"Payment Request — Order SO-01021\",\n      \"sent_by\": 3,\n      \"sender_name\": \"Alex Rivera\",\n      \"created_at\": \"2026-07-16T18:22:41+00:00\"\n    },\n    {\n      \"id\": 87,\n      \"customer_id\": 501,\n      \"sales_order_id\": null,\n      \"sales_order_number\": null,\n      \"type\": \"statement\",\n      \"scope\": \"account\",\n      \"statement_type\": \"open_item\",\n      \"params\": {\n        \"as_at\": \"2026-06-30\",\n        \"total_due\": 734.38\n      },\n      \"recipient\": \"ap@acmeretail.com\",\n      \"subject\": \"Statement of Account — Acme Retail LLC\",\n      \"sent_by\": 3,\n      \"sender_name\": \"Alex Rivera\",\n      \"created_at\": \"2026-07-01T15:04:05+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 2,\n  \"from\": 1,\n  \"to\": 2\n}"}]},{"name":"List Customers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/customers","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","customers"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"}]},"description":"Paginated list of Magento 2 customers scoped to one integration instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Path params:**\n- `magento2IntegrationInstance` (required, integer) — Magento 2 IntegrationInstance ID.\n\n**Query params** — all optional. Sorts: `id`, `magento_entity_id`, `email`, `firstname`, `lastname`, `group_id`, `website_id`, `magento_created_at`, `magento_updated_at`, `created_at`, `updated_at`. Advanced filters (Spatie AdvancedFilterBuilder) are also available for `email`, `firstname`, `lastname`, `telephone`, and the datetime columns.\n\n**Notes:**\n- `filter[integration_instance_ids]` is accepted as a no-op so the channel layout's URL param doesn't 400.\n- Eager-loads the integration instance + addresses + a withCount('orders') so the resource can emit `orders_count` and a derived primary-address city / region / country without N+1.\n- Fields the schema doesn't yet carry (`group_name`, `website_name`, `total_spent`, etc.) are returned as `null`; Phase 2D's sync work will populate them."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/customers","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","customers"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 17,\n      \"integration_instance_id\": 42,\n      \"magento_id\": 1234,\n      \"magento_entity_id\": 1234,\n      \"email\": \"buyer@example.com\",\n      \"firstname\": \"Alex\",\n      \"lastname\": \"Buyer\",\n      \"first_name\": \"Alex\",\n      \"last_name\": \"Buyer\",\n      \"full_name\": \"Alex Buyer\",\n      \"telephone\": \"+1 415 555 0190\",\n      \"phone\": \"+1 415 555 0190\",\n      \"website_id\": 1,\n      \"group_id\": 1,\n      \"group_name\": null,\n      \"website_name\": null,\n      \"store_id\": null,\n      \"store_view_code\": null,\n      \"sku_customer_name\": null,\n      \"currency\": null,\n      \"city\": \"San Francisco\",\n      \"region\": \"CA\",\n      \"country_id\": \"US\",\n      \"orders_count\": 4,\n      \"total_spent\": null,\n      \"is_active\": true,\n      \"magento_created_at\": \"2026-02-12T18:01:33+00:00\",\n      \"magento_updated_at\": \"2026-06-01T11:22:18+00:00\",\n      \"created_at\": \"2026-02-12T18:30:00+00:00\",\n      \"updated_at\": \"2026-06-15T09:11:08+00:00\",\n      \"sku_customer_id\": null,\n      \"magento_admin_url\": \"https://shop.example.com/admin/customer/index/edit/id/1234\",\n      \"raw_payload\": null\n    }\n  ],\n  \"first_page_url\": \"https://sku-io-test/api/magento2/integration-instances/42/customers?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://sku-io-test/api/magento2/integration-instances/42/customers?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://sku-io-test/api/magento2/integration-instances/42/customers\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"Get Customer","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/customers/{customer}","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","customers",":customer"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"},{"key":"customer","value":"17","description":"Internal magento2_customers.id"}]},"description":"Single customer detail. Eager-loads the integration instance, addresses, and orders_count.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Path params:**\n- `magento2IntegrationInstance` (required, integer) — Magento 2 IntegrationInstance ID.\n- `customer` (required, integer) — Internal `magento2_customers.id`.\n\n**Error responses:**\n- `404` — customer does not exist OR belongs to a different instance.\n- `401` — caller is not authenticated."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/customers/{customer}","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","customers",":customer"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"},{"key":"customer","value":"17","description":"Internal magento2_customers.id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 17,\n    \"integration_instance_id\": 42,\n    \"magento_id\": 1234,\n    \"magento_entity_id\": 1234,\n    \"email\": \"buyer@example.com\",\n    \"firstname\": \"Alex\",\n    \"lastname\": \"Buyer\",\n    \"first_name\": \"Alex\",\n    \"last_name\": \"Buyer\",\n    \"full_name\": \"Alex Buyer\",\n    \"telephone\": \"+1 415 555 0190\",\n    \"phone\": \"+1 415 555 0190\",\n    \"website_id\": 1,\n    \"group_id\": 1,\n    \"group_name\": null,\n    \"website_name\": null,\n    \"store_id\": null,\n    \"store_view_code\": null,\n    \"sku_customer_name\": null,\n    \"currency\": null,\n    \"city\": \"San Francisco\",\n    \"region\": \"CA\",\n    \"country_id\": \"US\",\n    \"orders_count\": 4,\n    \"total_spent\": null,\n    \"is_active\": true,\n    \"magento_created_at\": \"2026-02-12T18:01:33+00:00\",\n    \"magento_updated_at\": \"2026-06-01T11:22:18+00:00\",\n    \"created_at\": \"2026-02-12T18:30:00+00:00\",\n    \"updated_at\": \"2026-06-15T09:11:08+00:00\",\n    \"sku_customer_id\": null,\n    \"magento_admin_url\": \"https://shop.example.com/admin/customer/index/edit/id/1234\",\n    \"raw_payload\": null\n  }\n}"}]},{"name":"Get Customer Filter Options","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/customers/filter-options","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","customers","filter-options"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"}]},"description":"Aggregated dropdown options for the customers filter panel — countries (via the address rows), customer groups, and websites — each as `{value, label, count}` so the picker can render counts inline. Pulled live from the customers persisted for this instance; empty / never-synced instances surface empty lists.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/customers/filter-options","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","customers","filter-options"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"countries\": [\n      {\n        \"value\": \"US\",\n        \"label\": \"US\",\n        \"count\": 12\n      },\n      {\n        \"value\": \"GB\",\n        \"label\": \"GB\",\n        \"count\": 3\n      }\n    ],\n    \"groups\": [\n      {\n        \"value\": 1,\n        \"label\": \"1\",\n        \"count\": 14\n      },\n      {\n        \"value\": 2,\n        \"label\": \"2\",\n        \"count\": 1\n      }\n    ],\n    \"websites\": [\n      {\n        \"value\": 1,\n        \"label\": \"1\",\n        \"count\": 15\n      }\n    ]\n  }\n}"}]},{"name":"Refresh Customers","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/customers/refresh","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","customers","refresh"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"}]},"description":"Dispatch the tracked job that refreshes Magento 2 customers. Returns the `tracked_job_log_id` so the frontend can pivot the global JobProgressTray to it. Job name prefix is `Sync Magento 2 Customers: ` — the page's `lastCompletedJob` watcher matches on this prefix.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPhase 2C scope: the dispatch + tracked-job harness only. The actual `/V1/customers/search` walking + upsert lands in Phase 2D — until then the job acknowledges the dispatch and completes immediately with zero-counter results."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/customers/refresh","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","customers","refresh"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 91812\n  },\n  \"message\": \"Customer sync queued.\"\n}"}]},{"name":"Refresh Single Customer","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/customers/{customer}/refresh","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","customers",":customer","refresh"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"},{"key":"customer","value":"17","description":"Internal magento2_customers.id"}]},"description":"Dispatch the tracked job to re-fetch a single customer. Shares the `Sync Magento 2 Customers: ` job-name prefix with the bulk refresh so the page's `lastCompletedJob` watcher reacts to either run.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Error responses:**\n- `404` — customer does not exist OR belongs to a different instance.\n- `401` — caller is not authenticated."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/customers/{customer}/refresh","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","customers",":customer","refresh"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"},{"key":"customer","value":"17","description":"Internal magento2_customers.id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 91823\n  },\n  \"message\": \"Customer refresh queued.\"\n}"}]},{"name":"Search Customers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/customers/search","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","customers","search"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"}]},"description":"Search-as-you-type endpoint. Delegates to the same paginated query as `List Customers` (Spatie QueryBuilder under the hood) so every filter / sort / pagination param the index supports is also accepted here. Provided as a separate route so the frontend can wire a dedicated debounce path.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/customers/search","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","customers","search"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 17,\n      \"integration_instance_id\": 42,\n      \"magento_id\": 1234,\n      \"magento_entity_id\": 1234,\n      \"email\": \"buyer@example.com\",\n      \"firstname\": \"Alex\",\n      \"lastname\": \"Buyer\",\n      \"first_name\": \"Alex\",\n      \"last_name\": \"Buyer\",\n      \"full_name\": \"Alex Buyer\",\n      \"telephone\": \"+1 415 555 0190\",\n      \"phone\": \"+1 415 555 0190\",\n      \"website_id\": 1,\n      \"group_id\": 1,\n      \"group_name\": null,\n      \"website_name\": null,\n      \"store_id\": null,\n      \"store_view_code\": null,\n      \"sku_customer_name\": null,\n      \"currency\": null,\n      \"city\": \"San Francisco\",\n      \"region\": \"CA\",\n      \"country_id\": \"US\",\n      \"orders_count\": 4,\n      \"total_spent\": null,\n      \"is_active\": true,\n      \"magento_created_at\": \"2026-02-12T18:01:33+00:00\",\n      \"magento_updated_at\": \"2026-06-01T11:22:18+00:00\",\n      \"created_at\": \"2026-02-12T18:30:00+00:00\",\n      \"updated_at\": \"2026-06-15T09:11:08+00:00\",\n      \"sku_customer_id\": null,\n      \"magento_admin_url\": \"https://shop.example.com/admin/customer/index/edit/id/1234\",\n      \"raw_payload\": null\n    }\n  ],\n  \"first_page_url\": \"https://sku-io-test/api/magento2/integration-instances/42/customers?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://sku-io-test/api/magento2/integration-instances/42/customers?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://sku-io-test/api/magento2/integration-instances/42/customers\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"List Square Customers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/square/customers","host":["{{baseUrl}}"],"path":["api","square","customers"]},"description":"Paginated list of Square customers across integration instances.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.customers.index"}},{"name":"Sync Square Customers","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/square/instances/{squareIntegrationInstance}/customers/sync","host":["{{baseUrl}}"],"path":["api","square","instances",":squareIntegrationInstance","customers","sync"],"variable":[{"key":"squareIntegrationInstance","value":"1","description":"Square integration instance ID"}]},"description":"Fetch and upsert Square customers for a specific instance. Queued.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.customers.sync"}}]},{"name":"Suppliers","item":[{"name":"List Suppliers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/suppliers","host":["{{baseUrl}}"],"path":["api","v2","suppliers"]},"description":"List suppliers with pagination, search, sorting, sparse fieldsets and the full advanced-filter grammar. Every supplier field that can be exported or imported is also readable and filterable here.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: requires a Bearer token with the `suppliers:read` scope.\n\n**Pagination**\n- `page` — 1-based page number (default `1`).\n- `per_page` — rows per page (default `10`). Pass `per_page=-1` to return every matching supplier on a single page, which is how pickers and lookups load the full list.\n\n**Sparse fieldsets**\n- `included[]` — return ONLY these fields. Repeat the parameter once per field: `included[]=name&included[]=city&included[]=default_payment_term`.\n- `excluded[]` — return everything except these fields.\n\nRelation values are exposed twice: nested (`address`, `default_store`, `default_incoterm`, `default_pricing_tier`, `default_tax_rate`, `default_payment_term`, `default_shipping_method`) and flat (`city`, `province_code`, `country_code`, `zip`, `label`, `fax`, `address1`–`address3`, `address_name`, `address_company`, `address_phone`, `default_warehouse_name`, `default_store_name`, `default_incoterm_code`, `contractor_default_warehouse_name`, `wire_instructions_verified_by_name`). Use the flat keys with `included[]` — a dotted key such as `address.city` is not a valid `included[]` entry.\n\n**Sorting** — `sort=<field>`, prefix with `-` for descending. Default `-created_at`. Allowed fields:\nid, name, type, company_name, email, primary_contact_name, leadtime, minimum_order_quantity, contacts_count, products_count, created_at, updated_at, purchase_order_email, messaging_phone, website, timezone, archived_at, target_stock_days, minimum_purchase_order, minimum_purchase_order_quantity, default_incoterm_place, purchase_order_format, po_processing_method, auto_submit_dropship_po, auto_fulfill_dropship, auto_generate_backorder_po, auto_submit_backorder_po, auto_split_backorder_po_by_brand, auto_receive_backorder_po, default_stock_level, stock_mode, in_stock_quantity, stock_buffer_percentage, default_deposit_percentage, auto_apply_deposits, proportional_apply_deposits, wire_instructions_verified_at, invoice_reminders_mode, invoice_reminder_interval_days, invoice_reminder_max_count, invoice_reminder_start_after_days, is_manufacturing_contractor, default_outsourced_cost_basis, default_pricing_tier_id, default_warehouse_id, default_store_id, default_payment_term_id, default_tax_rate_id, default_incoterm_id, default_shipping_method_id, contractor_default_warehouse_id.\n\nFields backed by a related record (address parts, `default_payment_term`, `default_shipping_method`, `default_warehouse_name`, …) are filterable but not sortable.\n\n**Simple filters**\n- `filter[search]` — matches `name`, `company_name`, `email`, `primary_contact_name`.\n- `filter[archived]` — `only`, `archived` or `true` returns archived suppliers only; `all` returns both; any other value, or omitting the parameter, returns active suppliers only.\n- `filter[type]` — `goods` or `service`.\n\n**Advanced filters** — `filter[<field>.<operator>]=<value>`. The bare form `filter[<field>]=<value>` behaves as an implicit `is`. List operators (`is_one_of`, `is_not_one_of`) and `between` take comma-separated values.\n\nText operators: contains, does_not_contain, is, is_not, is_one_of, is_not_one_of, starts_with, does_not_start_with, ends_with, does_not_end_with, is_empty, is_not_empty.\nText fields: name, company_name, email, primary_contact_name, type, purchase_order_email, messaging_phone, website, timezone, address_name, address_company, address_phone, fax, address1, address2, address3, city, province, province_code, zip, country, country_code, label, default_warehouse_name, default_store_name, default_payment_term, default_tax_rate, default_pricing_tier, default_incoterm_code, default_shipping_method, contractor_default_warehouse_name, default_incoterm_place, purchase_order_format, po_processing_method, default_stock_level, stock_mode, wire_instructions, wire_instructions_verified_by_name, invoice_reminders_mode, default_outsourced_cost_basis.\n\nNumeric operators: is, is_not, is_one_of, is_not_one_of, greater_than, less_than, greater_than_or_equal, less_than_or_equal, between, is_empty, is_not_empty.\nNumeric fields: id, leadtime, minimum_order_quantity, target_stock_days, minimum_purchase_order, minimum_purchase_order_quantity, in_stock_quantity, stock_buffer_percentage, default_deposit_percentage, invoice_reminder_interval_days, invoice_reminder_max_count, invoice_reminder_start_after_days, contacts_count, products_count, default_warehouse_id, default_store_id, default_payment_term_id, default_tax_rate_id, default_pricing_tier_id, default_incoterm_id, default_shipping_method_id, contractor_default_warehouse_id.\n\nDate/time operators: is, is_not, before, after, on_or_before, on_or_after, between, is_empty, is_not_empty, today, yesterday, tomorrow, past_week, past_month, past_year, next_week, next_month, next_year, days_ago, days_from_now, past_days, next_days. Relative operators (`today`, `past_week`, …) ignore the value; `days_ago`, `days_from_now`, `past_days` and `next_days` take an integer number of days.\nDate/time fields: created_at, updated_at, archived_at, wire_instructions_verified_at.\n\nBoolean flags — use `filter[<field>.is]=true|false`: auto_submit_dropship_po, auto_fulfill_dropship, auto_generate_backorder_po, auto_submit_backorder_po, auto_split_backorder_po_by_brand, auto_receive_backorder_po, auto_apply_deposits, proportional_apply_deposits, is_manufacturing_contractor. `proportional_apply_deposits` is three-valued — `null` means the supplier inherits the payment-term / account default — so `filter[proportional_apply_deposits.is]=true|false` matches only suppliers with an explicit setting.\n\nCustom fields — each active supplier custom field adds `filter[cf_<slug>.<operator>]`, using the operator family that matches the field's type (text/long text/dropdown → text, number/decimal → numeric, date → date, datetime → date/time, checkbox → `cf_<slug>.is`). The stored values are returned in `custom_field_values[]`.\n\n**Grouped filters** — for AND/OR trees, send `filter_groups` as base64-encoded JSON. Decoded shape:\n\n{\"conjunction\":\"and\",\"children\":[{\"type\":\"condition\",\"condition\":{\"column\":\"leadtime\",\"operator\":\"greater_than\",\"value\":14}},{\"type\":\"group\",\"group\":{\"conjunction\":\"or\",\"children\":[{\"type\":\"condition\",\"condition\":{\"column\":\"country_code\",\"operator\":\"is\",\"value\":\"US\"}},{\"type\":\"condition\",\"condition\":{\"column\":\"country_code\",\"operator\":\"is\",\"value\":\"CA\"}}]}}]}\n\nColumns and operators are the same set listed above. Groups may nest, and each level carries its own `conjunction` (`and` / `or`).\n\nInclude `compliance_status` in a `with` parameter to add each supplier's compliance posture to the response: `required`, `missing` and `expired` (arrays of document type values) plus `is_complete`. Resolved in a single batched pass for the whole page, so it does not cost a query per row. The key is omitted when the include is not requested — absent means \"not evaluated\", which is not the same as compliant. A supplier that requires no certificates reports `is_complete: true` with an empty `required` array. Compliance is advisory throughout and never blocks a purchase order."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/suppliers","host":["{{baseUrl}}"],"path":["api","v2","suppliers"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"name\": \"Acme Corp\",\n      \"id\": 41,\n      \"contacts_count\": 3,\n      \"primary_contact\": {\n        \"id\": 812,\n        \"name\": \"John Doe\",\n        \"email\": \"john@acme.com\",\n        \"phone\": \"+15551234567\",\n        \"role\": \"purchasing\",\n        \"role_label\": \"Purchasing\",\n        \"title\": \"Purchasing Manager\",\n        \"is_primary\": true\n      },\n      \"type\": \"goods\",\n      \"type_label\": \"Product Supplier\",\n      \"company_name\": \"Acme Corporation\",\n      \"email\": \"orders@acme.com\",\n      \"primary_contact_name\": \"John Doe\",\n      \"purchase_order_email\": \"po@acme.com\",\n      \"messaging_phone\": \"+15551234567\",\n      \"invoice_reminders_mode\": \"auto\",\n      \"invoice_reminder_interval_days\": 7,\n      \"invoice_reminder_max_count\": 3,\n      \"invoice_reminder_start_after_days\": 14,\n      \"website\": \"https://acme.com\",\n      \"leadtime\": 14,\n      \"minimum_order_quantity\": 50,\n      \"target_stock_days\": 60,\n      \"minimum_purchase_order\": 2500,\n      \"minimum_purchase_order_quantity\": 100,\n      \"purchase_order_format\": \"csv\",\n      \"purchase_order_csv_columns\": [\n        \"sku\",\n        \"quantity\",\n        \"unit_cost\"\n      ],\n      \"created_at\": \"2026-01-14T09:22:31.000000Z\",\n      \"updated_at\": \"2026-07-02T16:04:11.000000Z\",\n      \"archived_at\": null,\n      \"address\": {\n        \"id\": 9114,\n        \"label\": \"Head Office\",\n        \"name\": \"John Doe\",\n        \"phone\": \"+15551234567\",\n        \"company\": \"Acme Corporation\",\n        \"address1\": \"120 Industrial Way\",\n        \"address2\": \"Suite 400\",\n        \"address3\": null,\n        \"city\": \"Portland\",\n        \"province\": \"Oregon\",\n        \"province_code\": \"OR\",\n        \"zip\": \"97201\",\n        \"country\": \"United States\",\n        \"country_code\": \"US\",\n        \"fax\": \"+15559876543\",\n        \"redacted\": false\n      },\n      \"address_name\": \"John Doe\",\n      \"address_company\": \"Acme Corporation\",\n      \"address_phone\": \"+15551234567\",\n      \"fax\": \"+15559876543\",\n      \"address1\": \"120 Industrial Way\",\n      \"address2\": \"Suite 400\",\n      \"address3\": null,\n      \"city\": \"Portland\",\n      \"province\": \"Oregon\",\n      \"province_code\": \"OR\",\n      \"zip\": \"97201\",\n      \"country\": \"United States\",\n      \"country_code\": \"US\",\n      \"label\": \"Head Office\",\n      \"pricing\": [\n        {\n          \"id\": 7,\n          \"name\": \"Wholesale USD\",\n          \"currency_code\": \"USD\",\n          \"is_default\": true\n        }\n      ],\n      \"warehouses\": [\n        {\n          \"id\": 302,\n          \"name\": \"Acme West DC\",\n          \"display_name\": \"Acme Corp - Acme West DC\",\n          \"dropship_enabled\": true,\n          \"address\": {\n            \"id\": 9115,\n            \"address1\": \"88 Dock Road\",\n            \"city\": \"Reno\",\n            \"province_code\": \"NV\",\n            \"zip\": \"89501\",\n            \"country_code\": \"US\"\n          }\n        }\n      ],\n      \"default_pricing_tier\": {\n        \"id\": 7,\n        \"name\": \"Wholesale USD\",\n        \"currency_code\": \"USD\"\n      },\n      \"default_pricing_tier_id\": 7,\n      \"default_warehouse_id\": 12,\n      \"default_warehouse_name\": \"Main Warehouse\",\n      \"default_store_id\": 4,\n      \"default_store_name\": \"Acme US Store\",\n      \"default_store\": {\n        \"id\": 4,\n        \"name\": \"Acme US Store\"\n      },\n      \"po_processing_method\": \"batch\",\n      \"po_batch_schedule\": [\n        {\n          \"time\": \"09:00\",\n          \"days\": [\n            \"monday\",\n            \"thursday\"\n          ]\n        }\n      ],\n      \"default_shipping_method_id\": 23,\n      \"default_shipping_method\": {\n        \"id\": 23,\n        \"name\": \"Ground\",\n        \"carrier_name\": \"UPS\",\n        \"method_name\": \"Ground\",\n        \"full_name\": \"UPS Ground\"\n      },\n      \"schedule_last_run\": {\n        \"po_batch_schedule\": \"2026-07-02 09:00:00\"\n      },\n      \"auto_fulfill_dropship\": true,\n      \"auto_submit_dropship_po\": true,\n      \"auto_generate_backorder_po\": false,\n      \"auto_submit_backorder_po\": false,\n      \"auto_split_backorder_po_by_brand\": false,\n      \"auto_receive_backorder_po\": false,\n      \"backorder_po_schedule\": null,\n      \"default_stock_level\": \"in_stock\",\n      \"stock_mode\": \"buffered\",\n      \"in_stock_quantity\": 999,\n      \"stock_buffer_percentage\": 10,\n      \"default_tax_rate_id\": 3,\n      \"default_tax_rate\": {\n        \"id\": 3,\n        \"name\": \"No Tax\",\n        \"rate\": 0\n      },\n      \"default_payment_term_id\": 5,\n      \"default_incoterm_id\": 2,\n      \"default_incoterm_place\": \"Port of Long Beach\",\n      \"default_deposit_percentage\": 30,\n      \"auto_apply_deposits\": true,\n      \"proportional_apply_deposits\": null,\n      \"wire_instructions\": \"Bank of Acme\\nAccount 000123456\\nSWIFT ACMEUS33\",\n      \"wire_instructions_verified_at\": \"2026-06-18T11:05:00+00:00\",\n      \"wire_instructions_verified_by\": 18,\n      \"wire_instructions_verified_by_name\": \"Dana Fields\",\n      \"default_payment_term\": {\n        \"id\": 5,\n        \"name\": \"Net 30\",\n        \"generated_description\": \"Net 30 days from invoice date\"\n      },\n      \"default_incoterm\": {\n        \"id\": 2,\n        \"code\": \"FOB\",\n        \"name\": \"Free On Board\",\n        \"description\": \"Seller delivers goods on board the vessel\"\n      },\n      \"default_incoterm_code\": \"FOB\",\n      \"timezone\": \"America/Los_Angeles\",\n      \"is_manufacturing_contractor\": false,\n      \"contractor_default_warehouse_id\": null,\n      \"contractor_default_warehouse_name\": null,\n      \"default_outsourced_cost_basis\": \"standard_cost\",\n      \"products_count\": 187,\n      \"custom_field_values\": [\n        {\n          \"id\": 5521,\n          \"custom_field_id\": 14,\n          \"value\": \"Tier A\",\n          \"name\": \"Vendor Tier\",\n          \"slug\": \"vendor_tier\",\n          \"field_type\": \"dropdown\",\n          \"options\": [\n            \"Tier A\",\n            \"Tier B\",\n            \"Tier C\"\n          ],\n          \"description\": \"Internal supplier grading\",\n          \"is_visible_in_details\": true\n        }\n      ]\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 9,\n  \"per_page\": 10,\n  \"total\": 84,\n  \"from\": 1,\n  \"to\": 10,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/v2/suppliers?page=1\",\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/v2/suppliers?page=9\",\n  \"next_page_url\": \"{{protocol}}{{domain}}/api/v2/suppliers?page=2\",\n  \"prev_page_url\": null,\n  \"path\": \"{{protocol}}{{domain}}/api/v2/suppliers\"\n}"}]},{"name":"Show Supplier","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/suppliers/{supplier}","host":["{{baseUrl}}"],"path":["api","v2","suppliers",":supplier"],"variable":[{"key":"supplier","value":"3","description":"Supplier ID"}]},"description":"Returns a single supplier. The response includes the supplier's address, default payment term, default shipping method, default incoterm, and warehouses (each with its address) by default.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nUse the `with` query parameter (comma-separated) to include additional relations: `pricingTiers` (also aliased as `pricing`), `defaultStore`, `defaultIncoterm`, `wireInstructionsVerifiedBy`, `customFieldValues`. Passing `customFieldValues` adds `custom_field_values` to the response — each stored custom-field value with its field metadata (`custom_field_id`, `name`, `slug`, `field_type`, `options`, `description`, `is_visible_in_details`). Pass `bill_stats` to include bill activity statistics for the supplier.\n\nInclude `last_po_destination` in the `with` parameter to receive `last_po_destination_warehouse_id` — the destination warehouse of the supplier's most recent purchase order (null when the supplier has none). Useful for defaulting the destination of a new PO.\n\nInclude `compliance_status` in the `with` parameter to receive the supplier's compliance posture against the certificate types it is required to keep current: `required`, `missing` and `expired` (arrays of document type values) plus `is_complete`. A supplier that is missing a required certificate, or holding one whose expiry date has passed, reports `is_complete: false`. The key is OMITTED entirely when the include is not requested — absent means \"not evaluated\", which is not the same as compliant. Compliance is advisory: it never blocks a purchase order."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/suppliers/{supplier}","host":["{{baseUrl}}"],"path":["api","v2","suppliers",":supplier"],"variable":[{"key":"supplier","value":"3","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Global Supplies Inc\",\n    \"email\": \"orders@globalsupplies.com\",\n    \"phone\": \"+1-555-0200\",\n    \"created_at\": \"2023-01-10T08:00:00.000000Z\",\n    \"updated_at\": \"2024-02-01T10:00:00.000000Z\",\n    \"last_po_destination_warehouse_id\": 3,\n    \"compliance_status\": {\n      \"required\": [\n        \"nsf_cert\",\n        \"gmp_cert\",\n        \"insurance\"\n      ],\n      \"missing\": [],\n      \"expired\": [\n        \"insurance\"\n      ],\n      \"is_complete\": false\n    }\n  }\n}"}]},{"name":"List Suppliers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers?page=1&limit=10&search=&sort=&archived=0&included[]=&excluded[]=","host":["{{baseUrl}}"],"path":["api","suppliers"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"10","description":"Items per page (default: 10)","disabled":true},{"key":"search","value":"","description":"Search by supplier name, email, company name","disabled":true},{"key":"sort","value":"","description":"Sort field. Prefix with - for descending (e.g. -name, created_at, leadtime)","disabled":true},{"key":"archived","value":"0","description":"0 = active only, 1 = archived only","disabled":true},{"key":"included[]","value":"","description":"Columns to include in response","disabled":true},{"key":"excluded[]","value":"","description":"Columns to exclude from response","disabled":true}]},"description":"List all suppliers with list view support. Supports filtering, sorting, and pagination.\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.\n\nSort fields: name, email, company_name, leadtime, created_at, updated_at, archived_at."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/suppliers?page=1&limit=10&search=&sort=&archived=0&included[]=&excluded[]=","host":["{{baseUrl}}"],"path":["api","suppliers"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"10","description":"Items per page (default: 10)","disabled":true},{"key":"search","value":"","description":"Search by supplier name, email, company name","disabled":true},{"key":"sort","value":"","description":"Sort field. Prefix with - for descending (e.g. -name, created_at, leadtime)","disabled":true},{"key":"archived","value":"0","description":"0 = active only, 1 = archived only","disabled":true},{"key":"included[]","value":"","description":"Columns to include in response","disabled":true},{"key":"excluded[]","value":"","description":"Columns to exclude from response","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Acme Supplier\",\n      \"company_name\": \"Acme Wholesale Co\",\n      \"email\": \"orders@acmewholesale.com\",\n      \"primary_contact_name\": \"Bob Johnson\",\n      \"purchase_order_email\": \"po@acmewholesale.com\",\n      \"messaging_phone\": \"+15551234567\",\n      \"website\": \"https://acmewholesale.com\",\n      \"leadtime\": 7,\n      \"minimum_order_quantity\": 10,\n      \"target_stock_days\": 30,\n      \"minimum_purchase_order\": 500,\n      \"purchase_order_format\": null,\n      \"po_processing_method\": \"email\",\n      \"auto_fulfill_dropship\": false,\n      \"auto_submit_dropship_po\": false,\n      \"auto_generate_backorder_po\": false,\n      \"auto_submit_backorder_po\": false,\n      \"auto_split_backorder_po_by_brand\": false,\n      \"auto_receive_backorder_po\": false,\n      \"default_warehouse_id\": 2,\n      \"default_store_id\": null,\n      \"default_tax_rate_id\": null,\n      \"default_payment_term_id\": null,\n      \"timezone\": \"Australia/Sydney\",\n      \"products_count\": 45,\n      \"address\": {\n        \"id\": 3,\n        \"label\": \"Default\",\n        \"address1\": \"100 Supplier Lane\",\n        \"city\": \"Sydney\",\n        \"zip\": \"2000\",\n        \"country\": \"Australia\",\n        \"country_code\": \"AU\"\n      },\n      \"created_at\": \"2024-01-01T00:00:00.000000Z\",\n      \"updated_at\": \"2024-01-15T09:00:00.000000Z\",\n      \"archived_at\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 10,\n  \"total\": 28\n}"}]},{"name":"Create Supplier","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/suppliers","host":["{{baseUrl}}"],"path":["api","suppliers"]},"description":"Create a new supplier. Automatically creates a default warehouse for the supplier and attaches default pricing tiers.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`proportional_apply_deposits` (boolean or null) is the supplier's default for proportional deposit application — each automatic application of a deposit capped at the bill's share of its purchase order. `true` / `false` apply to every deposit for this supplier that has no explicit `proportional_apply_enabled` of its own; `null` defers to the purchase order's payment term (on when the term has a payment schedule) and then the account setting `vendor_deposits_proportional_apply_enabled`. Optional on create.\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.\n\nNotes:\n- email and purchase_order_email accept comma-separated multiple email addresses\n- messaging_phone: the supplier's WhatsApp/SMS number in international format (E.164, e.g. +15551234567), used to message them from a purchase order\n- name must be unique\n- po_processing_method: email, ftp, direct\n- po_batch_schedule.days: monday, tuesday, wednesday, thursday, friday, saturday, sunday\n- default_stock_level: normal, low, critical\n\n**Requires permission:** `suppliers.create`\n\nClearing `default_incoterm_id` (sending it as null/empty) also clears `default_incoterm_place` — a default named place is never kept without a default term.\n\n**Custom fields:** `custom_field_values` (optional array) sets values for supplier custom fields. Each entry takes `custom_field_id` (required — must reference a custom field defined for suppliers) and `value` (nullable); `id` is prohibited on create (value ids only exist once the supplier does). The response echoes each value with its field metadata: `name`, `slug`, `field_type` (`text` | `long_text` | `number` | `decimal` | `date` | `datetime` | `checkbox` | `dropdown`), `options`, `description`, and `is_visible_in_details`.\n\nDropship vendor labels (all optional):\n- dropship_label_mode (string): `vendor_label` (default — labels off: the vendor ships on their own account), `auto` (the store buys the label when the PO is sent) or `on_request` (the vendor requests it from the supplier portal). On update, `auto` / `on_request` require a complete ship-from address on every dropship warehouse of the supplier (422 otherwise).\n- dropship_label_integration_instance_id (integer|null): a connected label-capable shipping integration (see List Dropship Label Providers); null = the account default\n- dropship_label_allowed_services (array of strings|null): service tokens the label may use; empty/null = any\n- dropship_label_handling_days (integer, 0–30)\n- dropship_label_max_relabels (integer, 0–10): how many replacement labels the vendor may request per order\n- dropship_label_default_package (object|null): weight, weight_unit (lb, oz, kg, g), length, width, height, dimension_unit (in, cm) — all required when the object is sent; numbers > 0","body":{"mode":"raw","raw":"{\n  \"name\": \"New Supplier\",\n  \"company_name\": \"New Supplier Co Pty Ltd\",\n  \"primary_contact_name\": \"Alice Brown\",\n  \"email\": \"alice@newsupplier.com,billing@newsupplier.com\",\n  \"purchase_order_email\": \"po@newsupplier.com\",\n  \"messaging_phone\": \"+15551234567\",\n  \"website\": \"https://newsupplier.com\",\n  \"leadtime\": 14,\n  \"minimum_order_quantity\": 5,\n  \"target_stock_days\": 45,\n  \"minimum_purchase_order\": 200,\n  \"po_processing_method\": \"email\",\n  \"po_batch_schedule\": [\n    {\n      \"time\": \"09:00\",\n      \"days\": [\n        \"monday\",\n        \"wednesday\",\n        \"friday\"\n      ]\n    }\n  ],\n  \"auto_fulfill_dropship\": false,\n  \"dropship_label_mode\": \"on_request\",\n  \"dropship_label_integration_instance_id\": 14,\n  \"dropship_label_allowed_services\": [\n    \"usps_ground_advantage\",\n    \"ups_ground\"\n  ],\n  \"dropship_label_handling_days\": 1,\n  \"dropship_label_max_relabels\": 2,\n  \"dropship_label_default_package\": {\n    \"weight\": 4.5,\n    \"weight_unit\": \"lb\",\n    \"length\": 14,\n    \"width\": 10,\n    \"height\": 8,\n    \"dimension_unit\": \"in\"\n  },\n  \"auto_submit_dropship_po\": false,\n  \"auto_generate_backorder_po\": true,\n  \"auto_submit_backorder_po\": false,\n  \"auto_split_backorder_po_by_brand\": false,\n  \"auto_receive_backorder_po\": false,\n  \"backorder_po_schedule\": [],\n  \"default_stock_level\": \"normal\",\n  \"default_tax_rate_id\": null,\n  \"default_payment_term_id\": 1,\n  \"timezone\": \"Australia/Sydney\",\n  \"address\": {\n    \"name\": \"Alice Brown\",\n    \"company\": \"New Supplier Co\",\n    \"email\": \"info@newsupplier.com\",\n    \"phone\": \"+61-2-9000-0000\",\n    \"address1\": \"200 Supplier Road\",\n    \"city\": \"Melbourne\",\n    \"province\": \"Victoria\",\n    \"province_code\": \"VIC\",\n    \"zip\": \"3000\",\n    \"country\": \"Australia\",\n    \"country_code\": \"AU\",\n    \"label\": \"Default\"\n  },\n  \"pricing\": [\n    {\n      \"id\": 1\n    }\n  ],\n  \"default_incoterm_id\": 12,\n  \"default_incoterm_place\": \"Shanghai Port, CN\",\n  \"custom_field_values\": [\n    {\n      \"custom_field_id\": 3,\n      \"value\": \"Gold\"\n    },\n    {\n      \"custom_field_id\": 5,\n      \"value\": \"2026-01-15\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/suppliers","host":["{{baseUrl}}"],"path":["api","suppliers"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 10,\n    \"name\": \"New Supplier\",\n    \"company_name\": \"New Supplier Co Pty Ltd\",\n    \"email\": \"alice@newsupplier.com,billing@newsupplier.com\",\n    \"leadtime\": 14,\n    \"minimum_order_quantity\": 5,\n    \"address\": {\n      \"id\": 8,\n      \"label\": \"Default\",\n      \"address1\": \"200 Supplier Road\",\n      \"city\": \"Melbourne\",\n      \"zip\": \"3000\",\n      \"country\": \"Australia\",\n      \"country_code\": \"AU\"\n    },\n    \"pricing\": [\n      {\n        \"id\": 1,\n        \"name\": \"Standard\",\n        \"currency_code\": \"AUD\"\n      }\n    ],\n    \"custom_field_values\": [\n      {\n        \"id\": 21,\n        \"custom_field_id\": 3,\n        \"value\": \"Gold\",\n        \"name\": \"Preferred Tier\",\n        \"slug\": \"preferred_tier\",\n        \"field_type\": \"dropdown\",\n        \"options\": [\n          \"Gold\",\n          \"Silver\",\n          \"Bronze\"\n        ],\n        \"description\": \"Negotiated service tier\",\n        \"is_visible_in_details\": true\n      },\n      {\n        \"id\": 22,\n        \"custom_field_id\": 5,\n        \"value\": \"2026-01-15\",\n        \"name\": \"Contract Renewal Date\",\n        \"slug\": \"contract_renewal_date\",\n        \"field_type\": \"date\",\n        \"options\": null,\n        \"description\": null,\n        \"is_visible_in_details\": true\n      }\n    ],\n    \"created_at\": \"2024-01-20T10:00:00.000000Z\",\n    \"updated_at\": \"2024-01-20T10:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"supplier created successfully\"\n}"}]},{"name":"Bulk Delete Suppliers","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers","host":["{{baseUrl}}"],"path":["api","suppliers"]},"description":"Bulk delete suppliers by IDs or by applying to all filtered results.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.\n\n**Requires permission:** `suppliers.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/suppliers","host":["{{baseUrl}}"],"path":["api","suppliers"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 suppliers deleted successfully.\"\n}"}]},{"name":"Get Supplier (with Warehouses)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]},"description":"Get a single supplier with warehouses loaded.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.\n\nThe response includes the supplier's `default_incoterm` (the full incoterm object, or null when no default is set).\n\nThe response also includes `custom_field_values` — each entry pairs the stored value with its field metadata (`custom_field_id`, `name`, `slug`, `field_type`, `options`, `description`, `is_visible_in_details`).\n\n`proportional_apply_deposits` (boolean or null) is the supplier's default for proportional deposit application — each automatic application of a deposit capped at the bill's share of its purchase order. `true` / `false` apply to every deposit for this supplier that has no explicit `proportional_apply_enabled` of its own; `null` defers to the purchase order's payment term (on when the term has a payment schedule) and then the account setting `vendor_deposits_proportional_apply_enabled`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Acme Supplier\",\n    \"company_name\": \"Acme Wholesale Co\",\n    \"email\": \"orders@acmewholesale.com\",\n    \"primary_contact_name\": \"Bob Johnson\",\n    \"purchase_order_email\": \"po@acmewholesale.com\",\n    \"messaging_phone\": \"+15551234567\",\n    \"website\": \"https://acmewholesale.com\",\n    \"leadtime\": 7,\n    \"minimum_order_quantity\": 10,\n    \"target_stock_days\": 30,\n    \"minimum_purchase_order\": 500,\n    \"po_processing_method\": \"email\",\n    \"auto_fulfill_dropship\": false,\n    \"dropship_label_mode\": \"on_request\",\n    \"dropship_label_integration_instance_id\": 14,\n    \"dropship_label_allowed_services\": [\n      \"usps_ground_advantage\",\n      \"ups_ground\"\n    ],\n    \"dropship_label_handling_days\": 1,\n    \"dropship_label_max_relabels\": 2,\n    \"dropship_label_default_package\": {\n      \"weight\": 4.5,\n      \"weight_unit\": \"lb\",\n      \"length\": 14,\n      \"width\": 10,\n      \"height\": 8,\n      \"dimension_unit\": \"in\"\n    },\n    \"auto_generate_backorder_po\": false,\n    \"auto_submit_backorder_po\": false,\n    \"auto_split_backorder_po_by_brand\": false,\n    \"auto_receive_backorder_po\": false,\n    \"default_warehouse_id\": 2,\n    \"default_store_id\": null,\n    \"default_payment_term\": {\n      \"id\": 1,\n      \"name\": \"Net 30\",\n      \"generated_description\": \"Payment due within 30 days\"\n    },\n    \"default_deposit_percentage\": 30,\n    \"auto_apply_deposits\": true,\n    \"proportional_apply_deposits\": null,\n    \"timezone\": \"Australia/Sydney\",\n    \"address\": {\n      \"id\": 3,\n      \"label\": \"Default\",\n      \"address1\": \"100 Supplier Lane\",\n      \"city\": \"Sydney\",\n      \"zip\": \"2000\",\n      \"country\": \"Australia\",\n      \"country_code\": \"AU\"\n    },\n    \"warehouses\": [\n      {\n        \"id\": 2,\n        \"name\": \"Acme Warehouse\",\n        \"type\": \"supplier\"\n      }\n    ],\n    \"warehouse_lead_times\": [\n      {\n        \"warehouse_id\": 2,\n        \"warehouse_name\": \"Main DC\",\n        \"leadtime\": 20\n      }\n    ],\n    \"custom_field_values\": [\n      {\n        \"id\": 21,\n        \"custom_field_id\": 3,\n        \"value\": \"Gold\",\n        \"name\": \"Preferred Tier\",\n        \"slug\": \"preferred_tier\",\n        \"field_type\": \"dropdown\",\n        \"options\": [\n          \"Gold\",\n          \"Silver\",\n          \"Bronze\"\n        ],\n        \"description\": \"Negotiated service tier\",\n        \"is_visible_in_details\": true\n      },\n      {\n        \"id\": 22,\n        \"custom_field_id\": 5,\n        \"value\": \"2026-01-15\",\n        \"name\": \"Contract Renewal Date\",\n        \"slug\": \"contract_renewal_date\",\n        \"field_type\": \"date\",\n        \"options\": null,\n        \"description\": null,\n        \"is_visible_in_details\": true\n      }\n    ],\n    \"created_at\": \"2024-01-01T00:00:00.000000Z\",\n    \"updated_at\": \"2024-01-15T09:00:00.000000Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Update Supplier","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]},"description":"Update an existing supplier. All fields are optional.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n- `messaging_phone` — the supplier's WhatsApp/SMS number in international format (E.164, e.g. +15551234567), used to message them from a purchase order.\n\n- `purchase_order_format` — only allowed on PUT.\n- `purchase_order_csv_columns` — optional array of column keys controlling which columns appear in this supplier's outgoing PO email CSV attachment. Each entry must be one of the keys returned by `GET /api/purchase-orders/csv-columns`. Set to `null` (or omit) to fall back to the tenant default. Set to an empty array to fall back to the tenant default. Order of the array determines column order in the CSV. Use the `POST /api/suppliers/:supplier/csv-sample` endpoint to preview the layout.\n- `proportional_apply_deposits` — boolean or null. Supplier-level default for proportional deposit application (each automatic deposit application capped at the bill's share of its purchase order). `true` / `false` override the payment-term and account defaults for this supplier's deposits; `null` (the default) inherits — on when the purchase order's payment term has a payment schedule, otherwise the account setting `vendor_deposits_proportional_apply_enabled`. A deposit's own `proportional_apply_enabled` still wins over all of these.\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.\n\n**Requires permission:** `suppliers.update`\n\nClearing `default_incoterm_id` (sending it as null/empty) also clears `default_incoterm_place` — a default named place is never kept without a default term.\n\n**Custom fields:** `custom_field_values` (optional array) fully replaces the supplier's stored custom field values when present — entries with an `id` update that existing value (the id must reference a custom-field value belonging to THIS supplier; a value id from another supplier is rejected with a 422), entries without one create a new value (`custom_field_id` required, must reference a custom field defined for suppliers), and any stored value row omitted from the array is deleted. Omit the key entirely to leave stored values untouched.\n\n**Advanced lead times:** `warehouse_lead_times` (optional array of `{ warehouse_id, leadtime }`) sets supplier-level per-warehouse lead-time overrides. Rows with a `warehouse_id` and a numeric `leadtime` are upserted; a warehouse omitted from the array has its override removed. Omit the key to leave overrides untouched.\n\nDropship vendor labels (all optional):\n- dropship_label_mode (string): `vendor_label` (default — labels off: the vendor ships on their own account), `auto` (the store buys the label when the PO is sent) or `on_request` (the vendor requests it from the supplier portal). On update, `auto` / `on_request` require a complete ship-from address on every dropship warehouse of the supplier (422 otherwise).\n- dropship_label_integration_instance_id (integer|null): a connected label-capable shipping integration (see List Dropship Label Providers); null = the account default\n- dropship_label_allowed_services (array of strings|null): service tokens the label may use; empty/null = any\n- dropship_label_handling_days (integer, 0–30)\n- dropship_label_max_relabels (integer, 0–10): how many replacement labels the vendor may request per order\n- dropship_label_default_package (object|null): weight, weight_unit (lb, oz, kg, g), length, width, height, dimension_unit (in, cm) — all required when the object is sent; numbers > 0","body":{"mode":"raw","raw":"{\n  \"name\": \"Acme Supplier Updated\",\n  \"company_name\": \"Acme Wholesale International\",\n  \"primary_contact_name\": \"Bob Johnson\",\n  \"email\": \"orders@acmewholesale.com\",\n  \"purchase_order_email\": \"po@acmewholesale.com\",\n  \"messaging_phone\": \"+15551234567\",\n  \"leadtime\": 10,\n  \"warehouse_lead_times\": [\n    {\n      \"warehouse_id\": 2,\n      \"leadtime\": 20\n    },\n    {\n      \"warehouse_id\": 5,\n      \"leadtime\": 45\n    }\n  ],\n  \"minimum_order_quantity\": 15,\n  \"target_stock_days\": 45,\n  \"po_processing_method\": \"email\",\n  \"auto_fulfill_dropship\": true,\n  \"dropship_label_mode\": \"on_request\",\n  \"dropship_label_integration_instance_id\": 14,\n  \"dropship_label_allowed_services\": [\n    \"usps_ground_advantage\",\n    \"ups_ground\"\n  ],\n  \"dropship_label_handling_days\": 1,\n  \"dropship_label_max_relabels\": 2,\n  \"dropship_label_default_package\": {\n    \"weight\": 4.5,\n    \"weight_unit\": \"lb\",\n    \"length\": 14,\n    \"width\": 10,\n    \"height\": 8,\n    \"dimension_unit\": \"in\"\n  },\n  \"auto_generate_backorder_po\": true,\n  \"proportional_apply_deposits\": true,\n  \"purchase_order_format\": null,\n  \"purchase_order_csv_columns\": [\n    \"sku\",\n    \"barcode\",\n    \"name\",\n    \"qty_ordered\",\n    \"unit_cost\",\n    \"extended_cost\"\n  ],\n  \"address\": {\n    \"address1\": \"100 Supplier Lane Updated\",\n    \"city\": \"Sydney\",\n    \"zip\": \"2000\",\n    \"country\": \"Australia\",\n    \"country_code\": \"AU\",\n    \"label\": \"Default\"\n  },\n  \"pricing\": [\n    {\n      \"id\": 1\n    },\n    {\n      \"id\": 2\n    }\n  ],\n  \"default_incoterm_id\": 12,\n  \"default_incoterm_place\": \"Shanghai Port, CN\",\n  \"custom_field_values\": [\n    {\n      \"id\": 21,\n      \"custom_field_id\": 3,\n      \"value\": \"Silver\"\n    },\n    {\n      \"custom_field_id\": 5,\n      \"value\": \"2027-01-15\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Acme Supplier Updated\",\n    \"leadtime\": 10,\n    \"proportional_apply_deposits\": true,\n    \"warehouse_lead_times\": [\n      {\n        \"warehouse_id\": 2,\n        \"warehouse_name\": \"Main DC\",\n        \"leadtime\": 20\n      },\n      {\n        \"warehouse_id\": 5,\n        \"warehouse_name\": \"West Coast Store\",\n        \"leadtime\": 45\n      }\n    ],\n    \"custom_field_values\": [\n      {\n        \"id\": 21,\n        \"custom_field_id\": 3,\n        \"value\": \"Silver\",\n        \"name\": \"Preferred Tier\",\n        \"slug\": \"preferred_tier\",\n        \"field_type\": \"dropdown\",\n        \"options\": [\n          \"Gold\",\n          \"Silver\",\n          \"Bronze\"\n        ],\n        \"description\": \"Negotiated service tier\",\n        \"is_visible_in_details\": true\n      },\n      {\n        \"id\": 23,\n        \"custom_field_id\": 5,\n        \"value\": \"2027-01-15\",\n        \"name\": \"Contract Renewal Date\",\n        \"slug\": \"contract_renewal_date\",\n        \"field_type\": \"date\",\n        \"options\": null,\n        \"description\": null,\n        \"is_visible_in_details\": true\n      }\n    ],\n    \"updated_at\": \"2024-01-20T11:00:00.000000Z\"\n  },\n  \"message\": \"supplier Acme Supplier Updated updated successfully\"\n}"}]},{"name":"Delete Supplier","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]},"description":"Delete a supplier. Returns 400 if the supplier is linked to purchase orders or other records. Deleting a supplier also removes its owned records, including its custom field values.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.\n\n**Requires permission:** `suppliers.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"supplier deleted successfully\"\n}"}]},{"name":"Set Supplier WhatsApp Contact","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/suppliers/{supplier}/messaging-contact","host":["{{baseUrl}}"],"path":["api","v2","suppliers",":supplier","messaging-contact"],"variable":[{"key":"supplier","value":"41","description":"The supplier ID."}]},"description":"Designate which of the supplier's contacts is its WhatsApp/SMS contact. The supplier's messaging number is synced to that contact's phone. Send `contact_id: null` to clear the designation (the stored number is kept). Returns the updated supplier.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequires the `suppliers.update` permission. Returns 404 when the contact does not belong to the supplier.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"contact_id\": 5012\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/suppliers/{supplier}/messaging-contact","host":["{{baseUrl}}"],"path":["api","v2","suppliers",":supplier","messaging-contact"],"variable":[{"key":"supplier","value":"41","description":"The supplier ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 41,\n    \"name\": \"Acme Corp\",\n    \"messaging_phone\": \"+15551234567\"\n  }\n}"}]},{"name":"Merge Suppliers","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/merge","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","merge"],"variable":[{"key":"supplier","value":"58","description":"Source supplier ID — this supplier is merged away and permanently deleted"}]},"description":"Merge one supplier into another. The supplier in the URL is the **source**: all of its records are re-pointed to the target, and the source is then **permanently deleted**. **This operation is irreversible — there is no undo.**\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody:\n- `target_supplier_id` (integer, required) — the surviving supplier that absorbs everything.\n\nWhat gets transferred to the target: purchase orders, purchase invoices, bills, vendor credits, vendor deposits, inbound shipments, supplier warehouses, inventory allocations, manufacturing/contractor references, cost entries, and forecast configurations. Supplier-product links, pricing-tier attachments, supplier inventory, and metric snapshots move too — where the target already has an equivalent record (e.g. the same product), the source's duplicate is dropped in favor of the target's. If the source was a product's default supplier and the target also supplies that product, the target becomes the default. Custom field values move to the target as well — where the target already has a value for the same custom field, the target's value is kept and the source's is dropped.\n\nErrors (422):\n- merging a supplier into itself\n- the target supplier does not exist\n- source and target are different types (a product supplier cannot merge with a service provider)\n- the target supplier is archived (unarchive it first)\n\nSynchronous — completes in one transaction. Requires the `suppliers:write` token scope and permission to delete suppliers.","body":{"mode":"raw","raw":"{\n  \"target_supplier_id\": 41\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/merge","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","merge"],"variable":[{"key":"supplier","value":"58","description":"Source supplier ID — this supplier is merged away and permanently deleted"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"The supplier merged into 'Acme Corp' successfully\",\n  \"data\": {\n    \"name\": \"Acme Corp\",\n    \"id\": 41,\n    \"type\": \"goods\",\n    \"type_label\": \"Product Supplier\",\n    \"company_name\": \"Acme Corporation\",\n    \"email\": \"orders@acme.com\",\n    \"primary_contact_name\": \"John Doe\",\n    \"purchase_order_email\": \"po@acme.com\",\n    \"messaging_phone\": \"+15551234567\",\n    \"website\": \"https://acme.com\",\n    \"leadtime\": 14,\n    \"minimum_order_quantity\": 50,\n    \"target_stock_days\": 60,\n    \"minimum_purchase_order\": \"500.00\",\n    \"minimum_purchase_order_quantity\": 100,\n    \"purchase_order_format\": \"pdf\",\n    \"purchase_order_csv_columns\": null,\n    \"created_at\": \"2025-11-02T09:15:22.000000Z\",\n    \"updated_at\": \"2026-07-06T18:40:00.000000Z\",\n    \"archived_at\": null,\n    \"address\": {\n      \"id\": 913,\n      \"label\": null,\n      \"name\": \"Acme Corp\",\n      \"phone\": \"+1 555 0100\",\n      \"company\": \"Acme Corporation\",\n      \"address1\": \"100 Main St\",\n      \"address2\": null,\n      \"address3\": null,\n      \"city\": \"Austin\",\n      \"province\": \"Texas\",\n      \"province_code\": \"TX\",\n      \"zip\": \"78701\",\n      \"country\": \"United States\",\n      \"country_code\": \"US\",\n      \"fax\": null,\n      \"redacted\": false\n    },\n    \"warehouses\": [\n      {\n        \"id\": 12,\n        \"name\": \"Acme Dropship WH\",\n        \"dropship_enabled\": true,\n        \"direct_returns\": false,\n        \"customer_returns\": false\n      }\n    ],\n    \"default_pricing_tier\": {\n      \"id\": 3,\n      \"name\": \"Wholesale\",\n      \"currency_code\": \"USD\"\n    },\n    \"default_warehouse_id\": 12,\n    \"default_store_id\": null,\n    \"po_processing_method\": null,\n    \"po_batch_schedule\": null,\n    \"default_shipping_method\": null,\n    \"auto_fulfill_dropship\": false,\n    \"auto_submit_dropship_po\": false,\n    \"dropship_label_mode\": \"on_request\",\n    \"dropship_label_integration_instance_id\": 14,\n    \"dropship_label_allowed_services\": [\n      \"usps_ground_advantage\",\n      \"ups_ground\"\n    ],\n    \"dropship_label_handling_days\": 1,\n    \"dropship_label_max_relabels\": 2,\n    \"dropship_label_default_package\": {\n      \"weight\": 4.5,\n      \"weight_unit\": \"lb\",\n      \"length\": 14,\n      \"width\": 10,\n      \"height\": 8,\n      \"dimension_unit\": \"in\"\n    },\n    \"auto_generate_backorder_po\": false,\n    \"auto_submit_backorder_po\": false,\n    \"auto_split_backorder_po_by_brand\": false,\n    \"auto_receive_backorder_po\": false,\n    \"backorder_po_schedule\": null,\n    \"default_stock_level\": null,\n    \"default_tax_rate_id\": null,\n    \"default_payment_term_id\": 2,\n    \"default_deposit_percentage\": null,\n    \"auto_apply_deposits\": false,\n    \"wire_instructions\": null,\n    \"default_payment_term\": {\n      \"id\": 2,\n      \"name\": \"Net 30\",\n      \"generated_description\": \"Net 30\"\n    },\n    \"timezone\": null,\n    \"products_count\": null\n  }\n}"}]},{"name":"Get Supplier Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/activity-log","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","activity-log"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]},"description":"Rolled-up activity timeline for a single supplier. Returns the supplier's own field changes plus related activity across its purchase orders (and their lines and invoices), vendor credits (and their lines and shipments), vendor deposits, bills, and supplier products (and pricing). Results are paginated and ordered newest-first.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token and the suppliers.show permission.\n\nFilters: filter[event] (created, updated, deleted, bulk_upsert), filter[source] (the origin of the change), filter[batch_uuid] (a single bulk batch), and filter[search] (matches actor name, event, changed properties, and related record type). Pagination: per_page (default 15)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/activity-log","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","activity-log"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 48213,\n      \"description\": \"updated\",\n      \"event\": \"updated\",\n      \"subject_type\": \"Supplier\",\n      \"subject_id\": 35,\n      \"properties\": {\n        \"source\": \"manual\",\n        \"attributes\": {\n          \"leadtime\": 10\n        },\n        \"old\": {\n          \"leadtime\": 7\n        },\n        \"metadata\": {\n          \"name\": \"HOGTUNES\",\n          \"type\": \"service\",\n          \"company_name\": \"Hogtunes Inc\"\n        }\n      },\n      \"causer_name\": \"Kelly McKeown\",\n      \"created_at\": \"2026-07-06T14:22:11.000000Z\"\n    },\n    {\n      \"id\": 48190,\n      \"description\": \"created\",\n      \"event\": \"created\",\n      \"subject_type\": \"PurchaseOrder\",\n      \"subject_id\": 9021,\n      \"properties\": {\n        \"source\": \"manual\",\n        \"attributes\": {\n          \"status\": \"draft\",\n          \"supplier_id\": 35\n        },\n        \"metadata\": {\n          \"purchase_order_number\": \"PO-9021\"\n        }\n      },\n      \"causer_name\": \"Kelly McKeown\",\n      \"created_at\": \"2026-07-05T09:10:03.000000Z\"\n    },\n    {\n      \"id\": 48001,\n      \"description\": \"created\",\n      \"event\": \"created\",\n      \"subject_type\": \"Bill\",\n      \"subject_id\": 412,\n      \"properties\": {\n        \"source\": \"manual\",\n        \"metadata\": {\n          \"invoice_number\": \"INV-412\",\n          \"supplier_id\": 35,\n          \"invoice_date\": \"2026-07-01\"\n        }\n      },\n      \"causer_name\": \"System\",\n      \"created_at\": \"2026-07-01T00:05:44.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 4,\n  \"per_page\": 15,\n  \"total\": 52,\n  \"from\": 1,\n  \"to\": 15,\n  \"path\": \"{{protocol}}{{domain}}/api/suppliers/35/activity-log\",\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/suppliers/35/activity-log?page=1\",\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/suppliers/35/activity-log?page=4\",\n  \"next_page_url\": \"{{protocol}}{{domain}}/api/suppliers/35/activity-log?page=2\",\n  \"prev_page_url\": null\n}"}]},{"name":"Archive Supplier","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/archive","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","archive"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]},"description":"Archive a single supplier.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.\n\n**Requires permission:** `suppliers.update`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/archive","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","archive"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Acme Supplier\"\n  },\n  \"message\": \"supplier Acme Supplier archived successfully\"\n}"}]},{"name":"Unarchive Supplier","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/unarchived","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","unarchived"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]},"description":"Unarchive a previously archived supplier.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.\n\n**Requires permission:** `suppliers.update`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/unarchived","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","unarchived"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Acme Supplier\",\n    \"archived_at\": null\n  },\n  \"message\": \"supplier Acme Supplier unarchived successfully\"\n}"}]},{"name":"Download PO CSV Sample","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/csv-sample","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","csv-sample"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]},"description":"**Legacy proxy.** Generate and download a 3-row sample CSV showing exactly which columns will be included in this supplier's outgoing PO email attachment.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThis endpoint is kept for backward compatibility — new clients should call `POST /api/suppliers/:supplier/po-sample` instead, which supports both CSV and XLSX (`format=xlsx`). Internally this route forwards to `po-sample` with `format=csv` forced.\n\nUsed by the per-supplier CSV column picker so users can preview the layout before saving.\n\nRequest body (all optional):\n- `columns` (array of strings) — Unsaved column selection to preview. If omitted, the saved supplier preference is used; if that is also empty, the tenant default falls through, then the system default (every registered column).\n\nValid column keys (current registry): `purchase_order`, `sku`, `barcode`, `supplier_sku`, `name`, `qty_ordered`, `unit_cost`, `extended_cost`. Unknown keys are silently dropped.\n\nThe response is a CSV file download (`Content-Type: text/csv`), not JSON.\n\nAuthentication: Requires Bearer token. Scope: `suppliers:rw`.","body":{"mode":"raw","raw":"{\n  \"columns\": [\n    \"sku\",\n    \"barcode\",\n    \"name\",\n    \"qty_ordered\",\n    \"unit_cost\",\n    \"extended_cost\"\n  ]\n}","options":{"raw":{"language":"json"}}}}},{"name":"Download PO Sample (CSV/XLSX)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/po-sample","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","po-sample"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]},"description":"Generate and download a 3-row sample of the file the supplier will receive when this PO is submitted. Supports both CSV and XLSX (controlled by `format`). Backs the per-supplier PO file attachment column picker so users can preview the layout before saving.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest body (all optional):\n- `format` (string, `csv` | `xlsx`) — Output format. Defaults to `csv`. Use `xlsx` to preview the spreadsheet with text-typed cells (no scientific-notation corruption of long barcodes/SKUs).\n- `columns` (array of strings) — Unsaved column selection to preview. If omitted, the saved supplier preference is used; if that is also empty, the tenant default falls through, then the system default (every registered column).\n\nValid column keys (current registry): `purchase_order`, `sku`, `barcode`, `supplier_sku`, `name`, `qty_ordered`, `unit_cost`, `extended_cost`. Unknown keys are silently dropped.\n\n**CSV behavior:** Values are written as plain CSV strings — quoted when they contain commas, quotes, or newlines per RFC 4180. No formula-prefix escaping is applied; long numeric-text columns (barcode, SKU) may be coerced to scientific notation by Excel on open. Use XLSX format to avoid that.\n\n**XLSX behavior:** Numeric-text columns are written with `t=\"inlineStr\"` cells (force text), quantities as integers, costs with the `#,##0.00` price format.\n\nThe response is a binary file download — `text/csv` for CSV, `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` for XLSX. Not JSON.\n\nValidation:\n- `format` must be one of `csv`, `xlsx` (any other value returns 422).\n\nAuthentication: Requires Bearer token. Scope: `suppliers:rw`.","body":{"mode":"raw","raw":"{\n  \"format\": \"xlsx\",\n  \"columns\": [\n    \"sku\",\n    \"barcode\",\n    \"name\",\n    \"qty_ordered\",\n    \"unit_cost\",\n    \"extended_cost\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Unauthenticated — the bearer token is missing, revoked, expired, or malformed. Never retry automatically; fix the credential. See the [Errors guide](https://developer.sku.io/docs/guides/errors).","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/po-sample","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","po-sample"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]}},"status":"Unauthenticated — the bearer token is missing, revoked, expired, or malformed. Never retry automatically; fix the credential. See the [Errors guide](https://developer.sku.io/docs/guides/errors).","code":401,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Unauthenticated.\"\n}"}]},{"name":"Bulk Archive Suppliers","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/suppliers/archive","host":["{{baseUrl}}"],"path":["api","suppliers","archive"]},"description":"Bulk archive suppliers by IDs or by applying to all filtered results.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.\n\n**Requires permission:** `suppliers.update`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    2,\n    3\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/suppliers/archive","host":["{{baseUrl}}"],"path":["api","suppliers","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 suppliers archived successfully.\"\n}"}]},{"name":"Bulk Unarchive Suppliers","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/suppliers/unarchive","host":["{{baseUrl}}"],"path":["api","suppliers","unarchive"]},"description":"Bulk unarchive suppliers by IDs or by applying to all filtered results.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.\n\n**Requires permission:** `suppliers.update`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    2,\n    3\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/suppliers/unarchive","host":["{{baseUrl}}"],"path":["api","suppliers","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 suppliers unarchived successfully.\"\n}"}]},{"name":"List Supplier Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/products?query=&limit=15&page=1","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","products"],"query":[{"key":"query","value":"","description":"Search by supplier SKU, product SKU, or product name","disabled":true},{"key":"limit","value":"15","description":"Items per page. Pass negative value to return all.","disabled":true},{"key":"page","value":"1","description":"Page number","disabled":true}],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]},"description":"Retrieve all products linked to a supplier. Supports search by supplier SKU, product SKU, or name (using Typesense with SQL fallback).\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.\n\nPass a negative limit to return all products without pagination."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/products?query=&limit=15&page=1","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","products"],"query":[{"key":"query","value":"","description":"Search by supplier SKU, product SKU, or product name","disabled":true},{"key":"limit","value":"15","description":"Items per page. Pass negative value to return all.","disabled":true},{"key":"page","value":"1","description":"Page number","disabled":true}],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5,\n      \"supplier_sku\": \"ACM-001\",\n      \"product_id\": 12,\n      \"product\": {\n        \"id\": 12,\n        \"sku\": \"PROD-001\",\n        \"name\": \"Widget A\"\n      },\n      \"supplier_id\": 1,\n      \"cost\": 9.99,\n      \"minimum_order_quantity\": 10\n    }\n  ],\n  \"status\": \"success\",\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"total\": 25\n}"}]},{"name":"Get Supplier Products Stock","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/products-stocks","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","products-stocks"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]},"description":"Get stock levels for specific products from a supplier's perspective.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    12,\n    15,\n    20\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/products-stocks","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","products-stocks"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 12,\n      \"sku\": \"PROD-001\",\n      \"supplier_stock\": 500,\n      \"available_stock\": 450\n    }\n  ]\n}"}]},{"name":"Get Supplier Pricing Tiers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/pricing-tiers?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","pricing-tiers"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default 10). -1 returns all rows on one page (the legacy `limit` param is accepted as an alias)","disabled":true}],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]},"description":"List pricing tiers attached to a supplier using Spatie QueryBuilder (filter[key]=value bracket format).\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe legacy table_specifications flag is now ignored. Response is a standard Laravel paginator (top-level data, current_page, last_page, per_page, total, links — not resource meta).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.\n\nQuery params: page, per_page (default 10; -1 = all rows), filter[search], filter[name], filter[currency_code], filter[pricing_type], filter[is_default], filter[archived], filter_groups (base64-encoded advanced filter tree), sort (default -is_default,-created_at)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/pricing-tiers?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","pricing-tiers"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default 10). -1 returns all rows on one page (the legacy `limit` param is accepted as an alias)","disabled":true}],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Standard\",\n      \"currency_code\": \"AUD\",\n      \"pricing_type\": \"fixed_price\",\n      \"is_default\": true,\n      \"base_pricing_tier_id\": null,\n      \"archived_at\": null,\n      \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2024-06-01T08:00:00.000000Z\"\n    }\n  ],\n  \"first_page_url\": \"https://example.sku.io/api/suppliers/1/pricing-tiers?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://example.sku.io/api/suppliers/1/pricing-tiers?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://example.sku.io/api/suppliers/1/pricing-tiers\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"Attach Pricing Tier to Supplier","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/pricing-tiers/{supplierPricingTier}/attach","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","pricing-tiers",":supplierPricingTier","attach"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"},{"key":"supplierPricingTier","value":"2","description":"Supplier Pricing Tier ID to attach"}]},"description":"Attach an existing pricing tier to a supplier.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/pricing-tiers/{supplierPricingTier}/attach","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","pricing-tiers",":supplierPricingTier","attach"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"},{"key":"supplierPricingTier","value":"2","description":"Supplier Pricing Tier ID to attach"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {},\n  \"message\": \"Pricing tier attached successfully.\"\n}"}]},{"name":"Detach Pricing Tier from Supplier","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/pricing-tiers/{tierId}","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","pricing-tiers",":tierId"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"},{"key":"tierId","value":"2","description":"ID of the supplier pricing tier to detach (numeric)"}]},"description":"Detach a pricing tier from a supplier. Removes the tier from the supplier's assigned pricing tiers and deletes this supplier's product prices on that tier. The pricing tier itself is not deleted and remains available to other suppliers (to delete a tier entirely, use Delete Supplier Pricing Tier).\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns 404 if the tier is not currently assigned to the supplier (archived tiers that are assigned can still be detached).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `suppliers.manage_pricing_tiers`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/pricing-tiers/{tierId}","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","pricing-tiers",":tierId"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"},{"key":"tierId","value":"2","description":"ID of the supplier pricing tier to detach (numeric)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Pricing tier 'Wholesale' detached from supplier.\"\n}"}]},{"name":"Get Supplier Warehouses","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/warehouses?page=1&included[]=&excluded[]=","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","warehouses"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"included[]","value":"","description":"Columns to include","disabled":true},{"key":"excluded[]","value":"","description":"Columns to exclude","disabled":true}],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]},"description":"Get all warehouses associated with a supplier, with list view support.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/warehouses?page=1&included[]=&excluded[]=","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","warehouses"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"included[]","value":"","description":"Columns to include","disabled":true},{"key":"excluded[]","value":"","description":"Columns to exclude","disabled":true}],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 2,\n      \"name\": \"Acme Warehouse\",\n      \"type\": \"supplier\",\n      \"supplier_id\": 1,\n      \"address\": {\n        \"address1\": \"100 Supplier Lane\",\n        \"city\": \"Sydney\",\n        \"country_code\": \"AU\"\n      }\n    }\n  ]\n}"}]},{"name":"Create Supplier Warehouse","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/warehouses","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","warehouses"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]},"description":"Create a new warehouse for a supplier. Automatically initializes supplier inventory for the new warehouse. If this is the supplier's first warehouse (or is_default is true), it becomes the default warehouse.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.","body":{"mode":"raw","raw":"{\n  \"name\": \"Sydney Warehouse\",\n  \"is_default\": false,\n  \"address\": {\n    \"label\": \"Main\",\n    \"address1\": \"500 Warehouse Rd\",\n    \"city\": \"Sydney\",\n    \"province\": \"New South Wales\",\n    \"province_code\": \"NSW\",\n    \"zip\": \"2000\",\n    \"country\": \"Australia\",\n    \"country_code\": \"AU\"\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/warehouses","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","warehouses"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Sydney Warehouse\",\n    \"type\": \"supplier\",\n    \"supplier_id\": 1,\n    \"address\": {\n      \"address1\": \"500 Warehouse Rd\",\n      \"city\": \"Sydney\",\n      \"country_code\": \"AU\"\n    }\n  },\n  \"message\": \"warehouse created successfully\"\n}"}]},{"name":"Get Warehouse Linked Orders","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/warehouses/{warehouse}/linked-orders","host":["{{baseUrl}}"],"path":["api","suppliers","warehouses",":warehouse","linked-orders"],"variable":[{"key":"warehouse","value":"2","description":"Warehouse ID"}]},"description":"Get the count of open purchase orders linked to a warehouse. Used before deleting a warehouse to check for dependencies.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/suppliers/warehouses/{warehouse}/linked-orders","host":["{{baseUrl}}"],"path":["api","suppliers","warehouses",":warehouse","linked-orders"],"variable":[{"key":"warehouse","value":"2","description":"Warehouse ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"linked_orders\": 3\n  }\n}"}]},{"name":"Preview Supplier Import","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/import/preview","host":["{{baseUrl}}"],"path":["api","suppliers","import","preview"]},"description":"Preview a supplier import from a CSV file.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.\n\n**Requires permission:** `suppliers.import`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/suppliers/import/preview","host":["{{baseUrl}}"],"path":["api","suppliers","import","preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total\": 20,\n    \"valid\": 18,\n    \"errors\": 2\n  }\n}"}]},{"name":"Import Suppliers","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/import","host":["{{baseUrl}}"],"path":["api","suppliers","import"]},"description":"Import suppliers from a CSV file.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.\n\n**Requires permission:** `suppliers.import`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/suppliers/import","host":["{{baseUrl}}"],"path":["api","suppliers","import"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"imported\": 18,\n    \"skipped\": 2\n  },\n  \"message\": \"Import completed\"\n}"}]},{"name":"Preview Supplier Inventory Import","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/import/preview","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","import","preview"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]},"description":"Preview supplier inventory import from a CSV file.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/import/preview","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","import","preview"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total\": 100,\n    \"valid\": 98,\n    \"errors\": 2\n  }\n}"}]},{"name":"Import Supplier Inventory","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/import","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","import"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]},"description":"Import inventory levels for a specific supplier from a CSV file.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/import","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","import"],"variable":[{"key":"supplier","value":"1","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"imported\": 98,\n    \"skipped\": 2\n  },\n  \"message\": \"Inventory imported successfully\"\n}"}]},{"name":"Export Suppliers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/suppliers/export","host":["{{baseUrl}}"],"path":["api","v2","suppliers","export"]},"description":"Export suppliers to a spreadsheet. The file contains one column per supplier field — the same field set the supplier spreadsheet import accepts, with matching headings, so an exported file can be edited and re-imported without remapping columns.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: requires a Bearer token with the `suppliers:read` scope.\n\n**Columns** (in file order, 78 plus one per custom field):\nID, Name, Company Name, Email, Contact Name, Lead Time, Min Order Qty, Created At, Updated At, PO Email, Website, Minimum PO $, Minimum PO Qty, Fax, Address Line 1, Address Line 2, Address Line 3, City, State/Province, State/Province Code, Zip/Postal Code, Country, Country Code, Address Label, Default Pricing Tier ID, Default Tax Rate, Default Pricing Tier, Default Warehouse ID, Default Store ID, Type, Messaging Phone, Timezone, Archived At, Address Contact Name, Address Company, Address Phone, Target Stock Days, Default Warehouse, Default Store, Default Payment Term, Default Payment Term ID, Default Tax Rate ID, Default Incoterm, Default Incoterm ID, Default Incoterm Place, Default Shipping Method, Default Shipping Method ID, PO Submission Format, PO CSV Columns (JSON), PO Processing Method, PO Batch Schedule (JSON), Backorder PO Schedule (JSON), Auto Submit Dropship PO, Auto Fulfill Dropship, Auto Generate Backorder PO, Auto Submit Backorder PO, Auto Split Backorder PO By Brand, Auto Receive Backorder PO, Schedule Last Run (JSON), Default Stock Level, Stock Mode, In Stock Quantity, Stock Buffer %, Default Deposit %, Auto Apply Deposits, Payment Instructions, Payment Instructions Verified At, Payment Instructions Verified By, Invoice Reminders Mode, Invoice Reminder Interval (days), Invoice Reminder Max Count, Invoice Reminder Start After (days), Manufacturing Contractor, Contractor Default Warehouse, Contractor Default Warehouse ID, Outsourced Cost Basis, Contacts, Products.\n\nThe four timestamp columns above (`Created At`, `Updated At`, `Archived At`, `Payment Instructions Verified At`) carry the account timezone in the heading — `Created At (America/New_York)`, or `Created At (UTC)` for an account on UTC. The suffix is the IANA identifier, not an abbreviation, because an export spanning a DST boundary holds rows on both sides of the change. All four are export-only, so import column auto-mapping is unaffected.\n\nEach active supplier custom field appends a `Custom: <field name>` column.\n\n**Export-only columns** — present in the file but ignored on import, because they are derived values, audit stamps, or the raw foreign key whose by-name twin is importable: Created At, Updated At, Archived At, Default Pricing Tier ID, Default Warehouse ID, Default Store ID, Default Payment Term ID, Default Tax Rate ID, Default Incoterm ID, Default Shipping Method ID, Contractor Default Warehouse ID, Schedule Last Run (JSON), Payment Instructions Verified At, Payment Instructions Verified By, Contacts, Products. Every other column round-trips.\n\n**Cell formats**\n- Yes/No columns (`Auto …`, `Manufacturing Contractor`, `Auto Apply Deposits`) export as `Yes` / `No`.\n- Enum columns (`Type`, `Stock Mode`, `Default Stock Level`, `PO Submission Format`, `PO Processing Method`, `Invoice Reminders Mode`, `Outsourced Cost Basis`) export their stored value, e.g. `goods`, `buffered`.\n- `(JSON)` columns export compact JSON, e.g. `[{\"time\":\"09:00\",\"days\":[\"monday\"]}]`. An empty array exports as blank.\n- Timestamp columns export as `YYYY-MM-DD HH:MM:SS` already converted from UTC to the account's timezone, so they match what the app displays. The cell carries no offset — spreadsheets reject one — so the timezone is named in the heading instead.\n- Lookup columns (`Default Warehouse`, `Default Store`, `Default Payment Term`, `Default Tax Rate`, `Default Pricing Tier`, `Default Incoterm`, `Default Shipping Method`, `Contractor Default Warehouse`) export the referenced record's name/code and resolve by that name/code on import.\n- Redacted address values export as blank rather than leaking the redaction placeholder.\n\n**Which suppliers are exported** — `scope`:\n- `filtered` (default) — every supplier matching the `filter[...]` / `filter_groups` parameters on this request.\n- `all` — every supplier, ignoring all filters on the request.\n- `current_page` — only the current page, honouring `page` / `per_page`.\n- `selected` — only the ids listed in `ids` (comma-separated).\n\n**Contacts** — pass `include_contacts=true` (or `content_mode=contacts`) to append the supplier's contacts. The file then has one row per contact with the supplier columns repeated, followed by: Contact ID, Contact Name, Contact Email, Contact Phone, Contact Role, Contact Title, Contact Primary, Contact Notes. Primary contact first, then oldest first.\n\nFiltering, searching and sorting use exactly the same parameters as the supplier list endpoint — see \"List Suppliers\" for the full field and operator reference.\n\nThe response is a file download named `suppliers-YYYY-MM-DD-HH-MM-SS.<format>`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/suppliers/export","host":["{{baseUrl}}"],"path":["api","v2","suppliers","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"ID,Name,Company Name,Email,Contact Name,Lead Time,Min Order Qty,Created At (UTC),Updated At (UTC),PO Email,Website,Minimum PO $,Minimum PO Qty,Fax,Address Line 1,Address Line 2,Address Line 3,City,State/Province,State/Province Code,Zip/Postal Code,Country,Country Code,Address Label,Default Pricing Tier ID,Default Tax Rate,Default Pricing Tier,Default Warehouse ID,Default Store ID,Type,Messaging Phone,Timezone,Archived At (UTC),Address Contact Name,Address Company,Address Phone,Target Stock Days,Default Warehouse,Default Store,Default Payment Term,Default Payment Term ID,Default Tax Rate ID,Default Incoterm,Default Incoterm ID,Default Incoterm Place,Default Shipping Method,Default Shipping Method ID,PO Submission Format,PO CSV Columns (JSON),PO Processing Method,PO Batch Schedule (JSON),Backorder PO Schedule (JSON),Auto Submit Dropship PO,Auto Fulfill Dropship,Auto Generate Backorder PO,Auto Submit Backorder PO,Auto Split Backorder PO By Brand,Auto Receive Backorder PO,Schedule Last Run (JSON),Default Stock Level,Stock Mode,In Stock Quantity,Stock Buffer %,Default Deposit %,Auto Apply Deposits,Payment Instructions,Payment Instructions Verified At (UTC),Payment Instructions Verified By,Invoice Reminders Mode,Invoice Reminder Interval (days),Invoice Reminder Max Count,Invoice Reminder Start After (days),Manufacturing Contractor,Contractor Default Warehouse,Contractor Default Warehouse ID,Outsourced Cost Basis,Contacts,Products\n41,Acme Corp,Acme Corporation,orders@acme.com,John Doe,14,50,2026-01-14 01:22:31,2026-07-02 09:04:11,po@acme.com,https://acme.com,2500,100,+15559876543,120 Industrial Way,Suite 400,,Portland,Oregon,OR,97201,United States,US,Head Office,7,No Tax,Wholesale USD,12,4,goods,+15551234567,America/Los_Angeles,,John Doe,Acme Corporation,+15551234567,60,Main Warehouse,Acme US Store,Net 30,5,3,FOB,2,Port of Long Beach,UPS Ground,23,csv,\"[\"\"sku\"\",\"\"quantity\"\",\"\"unit_cost\"\"]\",batch,\"[{\"\"time\"\":\"\"09:00\"\",\"\"days\"\":[\"\"monday\"\",\"\"thursday\"\"]}]\",,Yes,Yes,No,No,No,No,\"{\"\"po_batch_schedule\"\":\"\"2026-07-02 09:00:00\"\"}\",in_stock,buffered,999,10,30,Yes,Bank of Acme Account 000123456,2026-06-18 04:05:00,Dana Fields,auto,7,3,14,No,,,standard_cost,3,187\n"}]},{"name":"Request Invoices (Consolidated)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/request-invoices","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","request-invoices"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]},"description":"Send a supplier one consolidated email requesting the outstanding invoices for several purchase orders at once. Each selected PO is re-validated server-side: it must belong to this supplier AND still be awaiting its invoice — ineligible ids are skipped and reported back (not emailed). Every PO that IS sent is stamped (invoice_requested_at, invoice_request_count++, invoice_last_requested_by).\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nIf none of the selected POs are eligible, returns 422.\n\nAuthentication: Requires Bearer token.\n**Requires permission:** `purchase_orders.update`. Throttled: 60 requests/min.\n\n**Body fields:**\n- to (string, required) — recipient email\n- subject (string, required, max 255)\n- purchase_order_ids (array of ints, required, min 1) — POs to include\n- note (string, optional, max 2000)\n- cc (array of emails, optional, max 10)\n\nRequires both the suppliers and purchase-orders token scopes (it mutates purchase orders by stamping invoice-request tracking), plus the purchase_orders.update permission.\n\n**Delivery outcome:** the request email is sent from a background job, so a successful response means the request was queued and stamped, not that the supplier has received it. If the email later fails for good (rejected recipient, mail relay refusal, missing attachment), each purchase order in the email reports it: `invoice_request_failed_at` (ISO 8601) and `invoice_request_error` (the transport's reason, up to 500 characters). Both are null while nothing is known to have failed, and both are cleared the moment a new request is queued — they always describe the latest request. `invoice_requested_at` and `invoice_request_count` are not rolled back by a failure.","body":{"mode":"raw","raw":"{\n  \"to\": \"ap@acme.example\",\n  \"subject\": \"Outstanding invoices — 3 purchase orders\",\n  \"purchase_order_ids\": [\n    4521,\n    4530,\n    4544\n  ],\n  \"note\": \"Please send the invoices for the orders listed below; several are already prepaid.\",\n  \"cc\": [\n    \"buyer@ourcompany.example\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/request-invoices","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","request-invoices"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"sent_count\": 2,\n  \"skipped\": [\n    {\n      \"purchase_order_id\": 4544,\n      \"reason\": \"Not awaiting an invoice for this supplier.\"\n    }\n  ]\n}"}]},{"name":"Manage Invoice Reminders","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/purchase-orders/{purchase_order}/invoice-reminders","host":["{{baseUrl}}"],"path":["api","purchase-orders",":purchase_order","invoice-reminders"],"variable":[{"key":"purchase_order","value":"4521","description":"Purchase Order ID"}]},"description":"Set the automated invoice-reminder mode for a purchase order: inherit (use the supplier/tenant setting), on, or off. Setting on arms the recurring chase when the PO is still awaiting its invoice; off stops it.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nRequires permission: purchase_orders.update. Throttled: 60 requests/min.\n\nBody fields:\n- mode (string, required) — one of: inherit, on, off","body":{"mode":"raw","raw":"{\n  \"mode\": \"on\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/purchase-orders/{purchase_order}/invoice-reminders","host":["{{baseUrl}}"],"path":["api","purchase-orders",":purchase_order","invoice-reminders"],"variable":[{"key":"purchase_order","value":"4521","description":"Purchase Order ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 4521,\n  \"purchase_order_number\": \"PO-004521\",\n  \"invoice_status\": \"uninvoiced\",\n  \"invoice_requested_at\": \"2026-07-09T09:30:00+00:00\",\n  \"invoice_request_count\": 1,\n  \"invoice_request_failed_at\": null,\n  \"invoice_request_error\": null,\n  \"invoice_reminder_state\": \"active\",\n  \"invoice_reminder_next_at\": \"2026-07-16T09:30:00+00:00\",\n  \"invoice_reminder_paused_reason\": null,\n  \"invoice_reply_received_at\": null,\n  \"invoice_reminders_enabled\": true,\n  \"invoice_reminders_mode\": \"on\"\n}"}]},{"name":"Update Supplier Invoice Reminders","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/invoice-reminders","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","invoice-reminders"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]},"description":"Update a supplier's automated invoice-reminder settings — the mode plus optional cadence overrides. These resolve above the tenant default and below any per-PO override.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nRequires permission: purchase_orders.update.\n\nBody fields:\n- invoice_reminders_mode (string, required) — inherit | on | off\n- invoice_reminder_interval_days (int, nullable, 1-90) — days between reminders (blank = tenant default)\n- invoice_reminder_max_count (int, nullable, 1-12) — total requests (blank = tenant default)\n- invoice_reminder_start_after_days (int, nullable, 0-90) — delay before the first reminder (blank = tenant default)","body":{"mode":"raw","raw":"{\n  \"invoice_reminders_mode\": \"on\",\n  \"invoice_reminder_interval_days\": 10,\n  \"invoice_reminder_max_count\": 4,\n  \"invoice_reminder_start_after_days\": 3\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/invoice-reminders","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","invoice-reminders"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 12,\n  \"name\": \"Acme Co\",\n  \"invoice_reminders_mode\": \"on\",\n  \"invoice_reminder_interval_days\": 10,\n  \"invoice_reminder_max_count\": 4,\n  \"invoice_reminder_start_after_days\": 3\n}"}]},{"name":"Verify Supplier Bank Details","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/verify-bank-details","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","verify-bank-details"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]},"description":"Mark this supplier's wire instructions as verified out-of-band (for example, confirmed by phone with a known contact). Records who verified them and when, and clears the non-blocking bank-details warning shown when authorizing payment on the supplier's bills.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEditing a supplier's wire instructions afterwards automatically clears this verification, so changed payee details must be re-verified.\n\nAuthentication: Requires Bearer token.\nRequires permission: suppliers.update.\n\nReturns 422 if the supplier has no wire instructions to verify."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/verify-bank-details","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","verify-bank-details"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"name\": \"Acme Manufacturing Co.\",\n    \"type\": \"goods\",\n    \"wire_instructions\": \"Beneficiary: Acme Manufacturing Co.\\nBank: HSBC Hong Kong\\nSWIFT: HSBCHKHH\\nIBAN: HK29 2000 0000 0000 4821\",\n    \"wire_instructions_verified_at\": \"2026-07-11T14:32:00+00:00\",\n    \"wire_instructions_verified_by\": 7,\n    \"wire_instructions_verified_by_name\": \"Jane Cooper\"\n  },\n  \"message\": \"supplier Acme Manufacturing Co. updated successfully\"\n}"}]},{"name":"Unverify Supplier Bank Details","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/unverify-bank-details","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","unverify-bank-details"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]},"description":"Clear a previous out-of-band verification of this supplier's wire instructions. The bank-details warning reappears on the supplier's bills until the details are verified again.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nRequires permission: suppliers.update."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/unverify-bank-details","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","unverify-bank-details"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"name\": \"Acme Manufacturing Co.\",\n    \"type\": \"goods\",\n    \"wire_instructions\": \"Beneficiary: Acme Manufacturing Co.\\nBank: HSBC Hong Kong\\nSWIFT: HSBCHKHH\\nIBAN: HK29 2000 0000 0000 4821\",\n    \"wire_instructions_verified_at\": null,\n    \"wire_instructions_verified_by\": null,\n    \"wire_instructions_verified_by_name\": null\n  },\n  \"message\": \"supplier Acme Manufacturing Co. updated successfully\"\n}"}]},{"name":"Check Suppliers Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/suppliers/deletable","host":["{{baseUrl}}"],"path":["api","suppliers","deletable"]},"description":"Check which suppliers can be deleted before performing bulk delete.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Requires IsPowerUserAccess middleware.\n\n**Requires permission:** `suppliers.delete`\n\nEach result carries `deletable`, plus — when it is blocked — `reason` (a message per blocking relation), `reason_counts` (how many records each relation holds) and `reason_details` (a preview of the linked records, available for some relations only). Use `reason_counts` for the number of blocking records; `reason_details` does not cover every relation.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2\n  ],\n  \"scope\": \"ids\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/suppliers/deletable","host":["{{baseUrl}}"],"path":["api","suppliers","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Acme Supplier\",\n      \"deletable\": false,\n      \"reason\": {\n        \"purchaseOrders\": \"There are 4 purchase orders associated with this supplier(Acme Supplier).\",\n        \"warehouse_salesOrderLines\": \"There are 2 sales order lines associated with this warehouse(Acme Main).\"\n      },\n      \"reason_counts\": {\n        \"purchaseOrders\": 4,\n        \"warehouse_salesOrderLines\": 2\n      },\n      \"reason_details\": {\n        \"purchaseOrders\": [\n          {\n            \"id\": 88,\n            \"label\": \"PO-1042\"\n          },\n          {\n            \"id\": 91,\n            \"label\": \"PO-1051\"\n          }\n        ],\n        \"warehouse_salesOrderLines\": [\n          {\n            \"id\": 5501,\n            \"label\": \"Sales Order #5501\"\n          }\n        ]\n      }\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Beta Supplier\",\n      \"deletable\": true,\n      \"reason\": null,\n      \"reason_counts\": null,\n      \"reason_details\": null\n    }\n  ]\n}"}]},{"name":"Create Supplier Product","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-products","host":["{{baseUrl}}"],"path":["api","supplier-products"]},"description":"Creates a new supplier product relationship (a product sold by a specific supplier).\nAuthentication: Requires Bearer token.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields:\n- supplier_id (required): Supplier ID (must exist in suppliers table)\n- product_id (required): Product ID (must be unique per supplier; must exist in products table)\n- supplier_sku (nullable, max:255): Supplier's own SKU. The same supplier SKU may be used on several products of this supplier\n- leadtime (nullable, integer): Lead time in days\n- minimum_order_quantity (nullable, numeric): Minimum order quantity (max 999,999)\n- target_stock_days (nullable, integer): Target stock days (1-365)\n- is_default (boolean): Whether this is the default supplier for this product\n- default_tax_rate_id (nullable): Tax rate ID for this supplier product","body":{"mode":"raw","raw":"{\n  \"supplier_id\": 3,\n  \"product_id\": 42,\n  \"supplier_sku\": \"ACM-WIDGET-001\",\n  \"leadtime\": 7,\n  \"minimum_order_quantity\": 50,\n  \"target_stock_days\": 30,\n  \"is_default\": true,\n  \"default_tax_rate_id\": 1\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/supplier-products","host":["{{baseUrl}}"],"path":["api","supplier-products"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 15,\n    \"product_id\": 42,\n    \"supplier_id\": 3,\n    \"supplier_sku\": \"ACM-WIDGET-001\",\n    \"leadtime\": 7,\n    \"minimum_order_quantity\": 50,\n    \"target_stock_days\": 30,\n    \"is_default\": true,\n    \"default_tax_rate_id\": 1,\n    \"product_leadtime\": 7,\n    \"product_minimum_order_quantity\": 50,\n    \"product_target_stock_days\": 30,\n    \"created_at\": \"2024-01-15T10:00:00Z\",\n    \"updated_at\": \"2024-01-15T10:00:00Z\"\n  },\n  \"message\": \"supplier product created successfully.\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Supplier Product","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-products/{supplier_product}","host":["{{baseUrl}}"],"path":["api","supplier-products",":supplier_product"],"variable":[{"key":"supplier_product","value":"15","description":"Supplier product ID"}]},"description":"Returns full details for a supplier product including product, supplier, pricing tiers, and inventory levels per warehouse.\nAuthentication: Requires Bearer token.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/supplier-products/{supplier_product}","host":["{{baseUrl}}"],"path":["api","supplier-products",":supplier_product"],"variable":[{"key":"supplier_product","value":"15","description":"Supplier product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 15,\n    \"product_id\": 42,\n    \"supplier_id\": 3,\n    \"supplier_sku\": \"ACM-WIDGET-001\",\n    \"leadtime\": 7,\n    \"minimum_order_quantity\": 50,\n    \"target_stock_days\": 30,\n    \"is_default\": true,\n    \"default_tax_rate_id\": 1,\n    \"supplier\": {\n      \"id\": 3,\n      \"name\": \"Acme Supplies Co.\",\n      \"default_tax_rate_id\": 1\n    },\n    \"supplier_default_tax_rate_id\": 1,\n    \"product\": {\n      \"id\": 42,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Blue Widget\",\n      \"average_cost\": 12.5,\n      \"leadtime\": 7\n    },\n    \"supplier_stock\": {\n      \"id\": 8,\n      \"quantity\": 500,\n      \"warehouse_id\": 1\n    },\n    \"pricing\": [\n      {\n        \"id\": 20,\n        \"supplier_pricing_tier_id\": 1,\n        \"price\": 10,\n        \"min_quantity\": 1\n      }\n    ],\n    \"inventories\": [\n      {\n        \"id\": 8,\n        \"warehouse_id\": 1,\n        \"warehouse_name\": \"Main Warehouse\",\n        \"quantity\": 500,\n        \"in_stock\": true,\n        \"discontinued\": false,\n        \"eta\": null,\n        \"source\": \"manual\",\n        \"updated_at\": \"2024-01-10T08:00:00Z\"\n      }\n    ],\n    \"product_leadtime\": 7,\n    \"product_minimum_order_quantity\": 50,\n    \"product_target_stock_days\": 30,\n    \"created_at\": \"2024-01-15T10:00:00Z\",\n    \"updated_at\": \"2024-01-15T10:00:00Z\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Update Supplier Product","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-products/{supplier_product}","host":["{{baseUrl}}"],"path":["api","supplier-products",":supplier_product"],"variable":[{"key":"supplier_product","value":"15","description":"Supplier product ID"}]},"description":"Updates an existing supplier product.\nAuthentication: Requires Bearer token.\nNote: If supplier_id or product_id changes, supplier inventories are cleared and re-initialized. A warning is included in the response.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"supplier_sku\": \"ACM-WIDGET-001-V2\",\n  \"leadtime\": 10,\n  \"minimum_order_quantity\": 100,\n  \"target_stock_days\": 45,\n  \"is_default\": true,\n  \"default_tax_rate_id\": 2\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/supplier-products/{supplier_product}","host":["{{baseUrl}}"],"path":["api","supplier-products",":supplier_product"],"variable":[{"key":"supplier_product","value":"15","description":"Supplier product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 15,\n    \"supplier_sku\": \"ACM-WIDGET-001-V2\",\n    \"leadtime\": 10,\n    \"minimum_order_quantity\": 100,\n    \"target_stock_days\": 45,\n    \"updated_at\": \"2024-01-16T09:00:00Z\"\n  },\n  \"message\": \"supplier product ACM-WIDGET-001-V2 updated successfully.\",\n  \"status\": \"success\"\n}"}]},{"name":"Delete Supplier Product","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-products/{supplier_product}","host":["{{baseUrl}}"],"path":["api","supplier-products",":supplier_product"],"variable":[{"key":"supplier_product","value":"15","description":"Supplier product ID"}]},"description":"Deletes a supplier product and its associated inventories.\nAuthentication: Requires Bearer token.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/supplier-products/{supplier_product}","host":["{{baseUrl}}"],"path":["api","supplier-products",":supplier_product"],"variable":[{"key":"supplier_product","value":"15","description":"Supplier product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"supplier product deleted.\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Supplier Product Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-products/{supplier_product}/activity-log?per_page=15&filter[search]=","host":["{{baseUrl}}"],"path":["api","supplier-products",":supplier_product","activity-log"],"query":[{"key":"per_page","value":"15","description":"Items per page (default: 15)","disabled":true},{"key":"filter[search]","value":"","description":"Search across causer name, properties, event","disabled":true}],"variable":[{"key":"supplier_product","value":"15","description":"Supplier product ID"}]},"description":"Returns paginated activity log for a supplier product.\nAuthentication: Requires Bearer token.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/supplier-products/{supplier_product}/activity-log?per_page=15&filter[search]=","host":["{{baseUrl}}"],"path":["api","supplier-products",":supplier_product","activity-log"],"query":[{"key":"per_page","value":"15","description":"Items per page (default: 15)","disabled":true},{"key":"filter[search]","value":"","description":"Search across causer name, properties, event","disabled":true}],"variable":[{"key":"supplier_product","value":"15","description":"Supplier product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 300,\n      \"description\": \"updated\",\n      \"event\": \"updated\",\n      \"subject_type\": \"SupplierProduct\",\n      \"subject_id\": 15,\n      \"properties\": {\n        \"old\": {\n          \"leadtime\": 7\n        },\n        \"attributes\": {\n          \"leadtime\": 10\n        }\n      },\n      \"causer_name\": \"Jane Doe\",\n      \"created_at\": \"2024-01-16T09:00:00Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1\n}"}]},{"name":"Get Supplier Product Notes","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-products/{supplier_product}/notes","host":["{{baseUrl}}"],"path":["api","supplier-products",":supplier_product","notes"],"variable":[{"key":"supplier_product","value":"15","description":"Supplier product ID"}]},"description":"Returns paginated notes for a supplier product, most recent first.\nAuthentication: Requires Bearer token.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/supplier-products/{supplier_product}/notes","host":["{{baseUrl}}"],"path":["api","supplier-products",":supplier_product","notes"],"variable":[{"key":"supplier_product","value":"15","description":"Supplier product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"data\": [\n      {\n        \"id\": 25,\n        \"note\": \"Lead time confirmed by supplier.\",\n        \"is_pinned\": true,\n        \"user\": {\n          \"id\": 5,\n          \"name\": \"John Smith\"\n        },\n        \"created_at\": \"2024-01-15T10:00:00Z\"\n      }\n    ],\n    \"current_page\": 1,\n    \"total\": 1\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Add Supplier Product Note","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-products/{supplier_product}/notes","host":["{{baseUrl}}"],"path":["api","supplier-products",":supplier_product","notes"],"variable":[{"key":"supplier_product","value":"15","description":"Supplier product ID"}]},"description":"Adds a note to a supplier product.\nAuthentication: Requires Bearer token.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields:\n- note (required): Note text content\n- is_pinned (optional, boolean): Whether to pin the note","body":{"mode":"raw","raw":"{\n  \"note\": \"Lead time confirmed by supplier via email on 2024-01-15.\",\n  \"is_pinned\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/supplier-products/{supplier_product}/notes","host":["{{baseUrl}}"],"path":["api","supplier-products",":supplier_product","notes"],"variable":[{"key":"supplier_product","value":"15","description":"Supplier product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 26,\n    \"note\": \"Lead time confirmed by supplier via email on 2024-01-15.\",\n    \"is_pinned\": false,\n    \"user\": {\n      \"id\": 5,\n      \"name\": \"John Smith\"\n    },\n    \"created_at\": \"2024-01-15T11:00:00Z\"\n  },\n  \"message\": \"note created successfully.\",\n  \"status\": \"success\"\n}"}]},{"name":"Delete Supplier Product Note","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-products/{supplier_product}/notes/{note}","host":["{{baseUrl}}"],"path":["api","supplier-products",":supplier_product","notes",":note"],"variable":[{"key":"supplier_product","value":"15","description":"Supplier product ID"},{"key":"note","value":"26","description":"Note ID to delete"}]},"description":"Deletes a note from a supplier product.\nAuthentication: Requires Bearer token.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/supplier-products/{supplier_product}/notes/{note}","host":["{{baseUrl}}"],"path":["api","supplier-products",":supplier_product","notes",":note"],"variable":[{"key":"supplier_product","value":"15","description":"Supplier product ID"},{"key":"note","value":"26","description":"Note ID to delete"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"note 26 deleted successfully.\",\n  \"status\": \"success\"\n}"}]},{"name":"Update Pricing Tiers","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-products/{supplier_product}/pricing","host":["{{baseUrl}}"],"path":["api","supplier-products",":supplier_product","pricing"],"variable":[{"key":"supplier_product","value":"15","description":"Supplier product ID"}]},"description":"Updates pricing tiers for a supplier product.\nAuthentication: Requires Bearer token.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields:\n- pricing (required, array): Array of pricing tier entries\n  - pricing.*.supplier_pricing_tier_id (required, integer): Must exist in supplier_pricing_tiers table\n  - pricing.*.price (required, numeric, min:0): Price for this tier","body":{"mode":"raw","raw":"{\n  \"pricing\": [\n    {\n      \"supplier_pricing_tier_id\": 1,\n      \"price\": 10\n    },\n    {\n      \"supplier_pricing_tier_id\": 2,\n      \"price\": 9.5\n    },\n    {\n      \"supplier_pricing_tier_id\": 3,\n      \"price\": 9\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/supplier-products/{supplier_product}/pricing","host":["{{baseUrl}}"],"path":["api","supplier-products",":supplier_product","pricing"],"variable":[{"key":"supplier_product","value":"15","description":"Supplier product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 15,\n    \"pricing\": [\n      {\n        \"id\": 20,\n        \"supplier_pricing_tier_id\": 1,\n        \"price\": 10\n      },\n      {\n        \"id\": 21,\n        \"supplier_pricing_tier_id\": 2,\n        \"price\": 9.5\n      },\n      {\n        \"id\": 22,\n        \"supplier_pricing_tier_id\": 3,\n        \"price\": 9\n      }\n    ]\n  },\n  \"message\": \"pricing 15 updated successfully.\",\n  \"status\": \"success\"\n}"}]},{"name":"List Supplier Pricing Tiers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers?search=retail&sort=name&page=1&limit=25&archived=0","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers"],"query":[{"key":"search","value":"retail","description":"General text search across searchable columns (name, currency_code)","disabled":true},{"key":"sort","value":"name","description":"Sort column. Prefix with - for descending. Sortable: name, currency_code, is_default, created_at, updated_at.","disabled":true},{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"25","description":"Results per page","disabled":true},{"key":"archived","value":"0","description":"0 = active only (default), 1 = archived only","disabled":true}]},"description":"Returns a paginated list of supplier pricing tiers.\n\nAuthentication: Requires Bearer token.\n\nUses the list view trait supporting search, sort, pagination, and archived filtering.\n\nResponse fields: id, name, pricing_type (absolute|relative), base_pricing_tier_id, base_pricing_tier_name, adjustment_percentage, adjustment_fixed_amount, rounding_value, display_name, currency_code, is_default, dependent_tier_count, chain_depth, created_at, updated_at, archived_at."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers?search=retail&sort=name&page=1&limit=25&archived=0","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers"],"query":[{"key":"search","value":"retail","description":"General text search across searchable columns (name, currency_code)","disabled":true},{"key":"sort","value":"name","description":"Sort column. Prefix with - for descending. Sortable: name, currency_code, is_default, created_at, updated_at.","disabled":true},{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"25","description":"Results per page","disabled":true},{"key":"archived","value":"0","description":"0 = active only (default), 1 = archived only","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Retail USD\",\n      \"pricing_type\": \"absolute\",\n      \"base_pricing_tier_id\": null,\n      \"base_pricing_tier_name\": null,\n      \"adjustment_percentage\": null,\n      \"adjustment_fixed_amount\": null,\n      \"rounding_value\": null,\n      \"display_name\": \"Retail USD (USD)\",\n      \"currency_code\": \"USD\",\n      \"is_default\": true,\n      \"dependent_tier_count\": 2,\n      \"chain_depth\": 0,\n      \"created_at\": \"2024-01-10T08:00:00.000000Z\",\n      \"updated_at\": \"2024-03-15T10:00:00.000000Z\",\n      \"archived_at\": null\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Wholesale USD\",\n      \"pricing_type\": \"relative\",\n      \"base_pricing_tier_id\": 1,\n      \"base_pricing_tier_name\": \"Retail USD\",\n      \"adjustment_percentage\": \"-15.0000\",\n      \"adjustment_fixed_amount\": null,\n      \"rounding_value\": \"0.0500\",\n      \"display_name\": \"Wholesale USD (USD)\",\n      \"currency_code\": \"USD\",\n      \"is_default\": false,\n      \"dependent_tier_count\": 0,\n      \"chain_depth\": 1,\n      \"created_at\": \"2024-01-12T09:00:00.000000Z\",\n      \"updated_at\": \"2024-03-20T11:00:00.000000Z\",\n      \"archived_at\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 2,\n  \"per_page\": 25,\n  \"total\": 8\n}"}]},{"name":"Create Supplier Pricing Tier","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers"]},"description":"Creates a new supplier pricing tier.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nValidation:\n- `name` (required, max:255, unique in supplier_pricing_tiers)\n- `currency_code` (required, exists:currencies,code)\n- `pricing_type` (required, in:absolute,relative; defaults to 'absolute' if omitted)\n- `base_pricing_tier_id` (required_if:pricing_type,relative; nullable, integer, exists:supplier_pricing_tiers)\n- `adjustment_percentage` (required_if:pricing_type,relative; nullable, numeric, between:-99.9999,999.9999)\n- `adjustment_fixed_amount` (nullable, numeric, between:-999999.9999,999999.9999)\n- `rounding_value` (nullable, numeric, gt:0, max:999999.9999)\n\nBusiness rules:\n- Relative tiers inherit the currency of their root absolute tier\n- Circular references are rejected\n- A tier cannot reference itself as the base tier\n\n**Requires permission:** `supplier_pricing_tiers.create`","body":{"mode":"raw","raw":"{\n  \"name\": \"Wholesale USD\",\n  \"currency_code\": \"USD\",\n  \"pricing_type\": \"relative\",\n  \"base_pricing_tier_id\": 1,\n  \"adjustment_percentage\": -15,\n  \"adjustment_fixed_amount\": null,\n  \"rounding_value\": 0.05\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Wholesale USD\",\n    \"pricing_type\": \"relative\",\n    \"base_pricing_tier_id\": 1,\n    \"base_pricing_tier_name\": null,\n    \"adjustment_percentage\": \"-15.0000\",\n    \"adjustment_fixed_amount\": null,\n    \"rounding_value\": \"0.0500\",\n    \"display_name\": \"Wholesale USD (USD)\",\n    \"currency_code\": \"USD\",\n    \"is_default\": false,\n    \"dependent_tier_count\": 0,\n    \"chain_depth\": 1,\n    \"created_at\": \"2024-04-01T12:00:00.000000Z\",\n    \"updated_at\": \"2024-04-01T12:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"Supplier pricing tier has been created successfully\"\n}"}]},{"name":"Bulk Delete Pricing Tiers","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers"]},"description":"Bulk deletes multiple supplier pricing tiers by ID.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `supplier_pricing_tiers.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 supplier pricing tiers have been deleted successfully\"\n}"}]},{"name":"Get Supplier Pricing Tier","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/{id}","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns a single supplier pricing tier by ID.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/{id}","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Retail USD\",\n    \"pricing_type\": \"absolute\",\n    \"base_pricing_tier_id\": null,\n    \"base_pricing_tier_name\": null,\n    \"adjustment_percentage\": null,\n    \"adjustment_fixed_amount\": null,\n    \"rounding_value\": null,\n    \"display_name\": \"Retail USD (USD)\",\n    \"currency_code\": \"USD\",\n    \"is_default\": true,\n    \"dependent_tier_count\": 2,\n    \"chain_depth\": 0,\n    \"created_at\": \"2024-01-10T08:00:00.000000Z\",\n    \"updated_at\": \"2024-03-15T10:00:00.000000Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Update Supplier Pricing Tier","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/{id}","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Updates an existing supplier pricing tier.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nValidation (PUT — currency_code cannot be changed; all other fields are sometimes/optional):\n- `name` (required, max:255, unique ignoring current record)\n- `pricing_type` (sometimes, in:absolute,relative)\n- `base_pricing_tier_id` (sometimes, required_if:pricing_type,relative; nullable, integer)\n- `adjustment_percentage` (sometimes, required_if:pricing_type,relative; nullable, numeric, between:-99.9999,999.9999)\n- `adjustment_fixed_amount` (sometimes, nullable, numeric, between:-999999.9999,999999.9999)\n- `rounding_value` (sometimes, nullable, numeric, gt:0)\n\nNote: currency_code is immutable — excluded from PUT validation.","body":{"mode":"raw","raw":"{\n  \"name\": \"Wholesale USD Updated\",\n  \"pricing_type\": \"relative\",\n  \"base_pricing_tier_id\": 1,\n  \"adjustment_percentage\": -20,\n  \"adjustment_fixed_amount\": null,\n  \"rounding_value\": 0.05\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/{id}","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 2,\n    \"name\": \"Wholesale USD Updated\",\n    \"pricing_type\": \"relative\",\n    \"base_pricing_tier_id\": 1,\n    \"base_pricing_tier_name\": null,\n    \"adjustment_percentage\": \"-20.0000\",\n    \"adjustment_fixed_amount\": null,\n    \"rounding_value\": \"0.0500\",\n    \"display_name\": \"Wholesale USD Updated (USD)\",\n    \"currency_code\": \"USD\",\n    \"is_default\": false,\n    \"dependent_tier_count\": 0,\n    \"chain_depth\": 1,\n    \"created_at\": \"2024-01-12T09:00:00.000000Z\",\n    \"updated_at\": \"2024-04-01T13:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"Supplier pricing tier Wholesale USD Updated has been updated successfully\"\n}"}]},{"name":"Delete Supplier Pricing Tier","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/{id}","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Deletes a supplier pricing tier. Will fail if:\n- The tier is the default tier\n- The tier has active supplier product prices\n- The tier is used in integration instance import mappings\n- The tier has active dependent relative tiers\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/{id}","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Supplier pricing tier Wholesale USD has been deleted successfully\"\n}"}]},{"name":"Archive Pricing Tier","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/{id}/archive","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers",":id","archive"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Archives a supplier pricing tier. Cannot archive the default tier or a tier that is the base for active dependent tiers.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/{id}/archive","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers",":id","archive"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Supplier pricing tier Wholesale USD has been archived successfully\"\n}"}]},{"name":"Unarchive Pricing Tier","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/{id}/unarchived","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers",":id","unarchived"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Unarchives a supplier pricing tier.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/{id}/unarchived","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers",":id","unarchived"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 2,\n    \"name\": \"Wholesale USD\",\n    \"pricing_type\": \"relative\",\n    \"base_pricing_tier_id\": 1,\n    \"base_pricing_tier_name\": null,\n    \"adjustment_percentage\": \"-15.0000\",\n    \"adjustment_fixed_amount\": null,\n    \"rounding_value\": \"0.0500\",\n    \"display_name\": \"Wholesale USD (USD)\",\n    \"currency_code\": \"USD\",\n    \"is_default\": false,\n    \"dependent_tier_count\": 0,\n    \"chain_depth\": 1,\n    \"created_at\": \"2024-01-12T09:00:00.000000Z\",\n    \"updated_at\": \"2024-04-01T14:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"Supplier pricing tier Wholesale USD has been unarchived successfully\"\n}"}]},{"name":"Bulk Archive Pricing Tiers","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/archive","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers","archive"]},"description":"Bulk archives multiple supplier pricing tiers. Default tiers and tiers with active dependents are skipped with warnings.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `supplier_pricing_tiers.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    3,\n    4,\n    5\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/archive","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 supplier pricing tiers have been archived successfully\"\n}"}]},{"name":"Bulk Unarchive Pricing Tiers","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/unarchive","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers","unarchive"]},"description":"Bulk unarchives multiple supplier pricing tiers.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `supplier_pricing_tiers.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    3,\n    4\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/unarchive","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 supplier pricing tiers have been unarchived successfully\"\n}"}]},{"name":"Check Supplier Pricing Tiers Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/deletable","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers","deletable"]},"description":"Checks whether each pricing tier can be safely deleted.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nValidation:\n- `ids` (required, array of integers, each must exist in supplier_pricing_tiers)\n- `ids.*` (integer)\n\n**Requires permission:** `supplier_pricing_tiers.delete`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/deletable","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Retail USD\",\n      \"deletable\": false,\n      \"reason\": {\n        \"is_default\": \"Cannot delete default supplier pricing tier (Retail USD)\"\n      }\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Wholesale USD\",\n      \"deletable\": false,\n      \"reason\": {\n        \"supplierProducts\": \"Supplier pricing tier(Wholesale USD) is currently used by 8 supplier product(s)\"\n      }\n    },\n    {\n      \"id\": 3,\n      \"name\": \"Unused Tier\",\n      \"deletable\": true,\n      \"reason\": null\n    }\n  ]\n}"}]},{"name":"Check Supplier Pricing Tiers Archivable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/archivable","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers","archivable"]},"description":"Checks whether each pricing tier can be archived. Default tiers cannot be archived.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nValidation:\n- `ids` (required, array of integers, each must exist in supplier_pricing_tiers)\n- `ids.*` (integer)\n\n**Requires permission:** `supplier_pricing_tiers.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/archivable","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers","archivable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Retail USD\",\n      \"is_default\": true,\n      \"archivable\": false,\n      \"reason\": {\n        \"is_default\": \"Cannot archive the default supplier pricing tier (Retail USD)\"\n      }\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Wholesale USD\",\n      \"is_default\": false,\n      \"archivable\": true,\n      \"reason\": null\n    },\n    {\n      \"id\": 3,\n      \"name\": \"VIP USD\",\n      \"is_default\": false,\n      \"archivable\": true,\n      \"reason\": null\n    }\n  ]\n}"}]},{"name":"Set as Default","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/{id}/default","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers",":id","default"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Sets the specified pricing tier as the system default. Clears the is_default flag from the previously default tier.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBusiness rules:\n- Archived tiers cannot be set as default\n- Relative (non-absolute) tiers cannot be set as default"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/{id}/default","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers",":id","default"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 2,\n    \"name\": \"Wholesale USD\",\n    \"pricing_type\": \"absolute\",\n    \"base_pricing_tier_id\": null,\n    \"base_pricing_tier_name\": null,\n    \"adjustment_percentage\": null,\n    \"adjustment_fixed_amount\": null,\n    \"rounding_value\": null,\n    \"display_name\": \"Wholesale USD (USD)\",\n    \"currency_code\": \"USD\",\n    \"is_default\": true,\n    \"dependent_tier_count\": 0,\n    \"chain_depth\": 0,\n    \"created_at\": \"2024-01-12T09:00:00.000000Z\",\n    \"updated_at\": \"2024-04-01T15:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"Supplier pricing tier Wholesale USD has been set as default successfully\"\n}"}]},{"name":"Preview Pricing Tier Import","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/import/preview","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers","import","preview"]},"description":"Preview the result of importing supplier pricing tiers from a CSV/Excel file without committing.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data\n\n**Requires permission:** `supplier_pricing_tiers.import`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/import/preview","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers","import","preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"headers\": [\n      \"name\",\n      \"currency_code\"\n    ],\n    \"rows\": [\n      {\n        \"name\": \"New Tier\",\n        \"currency_code\": \"AUD\",\n        \"status\": \"create\"\n      },\n      {\n        \"name\": \"Retail USD\",\n        \"currency_code\": \"USD\",\n        \"status\": \"update\"\n      }\n    ],\n    \"total\": 2,\n    \"errors\": []\n  }\n}"}]},{"name":"Import Pricing Tiers","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/import","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers","import"]},"description":"Imports supplier pricing tiers from a CSV or Excel file.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data\n\n**Requires permission:** `supplier_pricing_tiers.import`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/supplier-pricing-tiers/import","host":["{{baseUrl}}"],"path":["api","supplier-pricing-tiers","import"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Import completed: 3 created, 1 updated, 0 errors\"\n}"}]},{"name":"List Supplier Inventories","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-inventories?page=1&sort=-updated_at&filter[supplier_id]=1","host":["{{baseUrl}}"],"path":["api","supplier-inventories"],"query":[{"key":"page","value":"1","description":"Page number (Spatie QueryBuilder)","disabled":true},{"key":"sort","value":"-updated_at","description":"Sort field. Allowed: id, quantity, eta, in_stock, discontinued, created_at, updated_at. Prefix with - for descending (default: -updated_at)","disabled":true},{"key":"filter[supplier_id]","value":"1","description":"Filter by exact supplier ID","disabled":true}]},"description":"Returns a paginated list of supplier inventory records using Spatie QueryBuilder.\n\nAuthentication: Requires Bearer token.\n\nFilters (Spatie QueryBuilder):\n- filter[id]: Exact match on ID\n- filter[supplier_id]: Exact match on supplier ID\n- filter[warehouse_id]: Exact match on warehouse ID\n- filter[product_id]: Exact match on product ID\n- filter[sku]: Partial match on product SKU\n- filter[search]: Advanced search on product SKU\n\nSort fields: id, quantity, eta, in_stock, discontinued, created_at, updated_at (default: -updated_at)\n\nPagination: page + per_page (default 10)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/supplier-inventories?page=1&sort=-updated_at&filter[supplier_id]=1","host":["{{baseUrl}}"],"path":["api","supplier-inventories"],"query":[{"key":"page","value":"1","description":"Page number (Spatie QueryBuilder)","disabled":true},{"key":"sort","value":"-updated_at","description":"Sort field. Allowed: id, quantity, eta, in_stock, discontinued, created_at, updated_at. Prefix with - for descending (default: -updated_at)","disabled":true},{"key":"filter[supplier_id]","value":"1","description":"Filter by exact supplier ID","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"quantity\": 500,\n      \"in_stock\": null,\n      \"discontinued\": false,\n      \"eta\": null,\n      \"source\": \"user\",\n      \"product_id\": 42,\n      \"sku\": \"WIDGET-001\",\n      \"product_name\": \"Blue Widget\",\n      \"warehouse\": {\n        \"id\": 2,\n        \"name\": \"Main Warehouse\"\n      },\n      \"supplier_sku\": \"ACME-W001\",\n      \"supplier_id\": 5,\n      \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2024-06-01T14:30:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 5,\n  \"per_page\": 10,\n  \"total\": 48,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"List Our Inventory for Supplier","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}/our-inventory","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id","our-inventory"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns a paginated list of OUR internal warehouse inventory rows for products supplied by the given supplier.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns one row per (product, our_warehouse). Excludes the per-product totals row (warehouse_id = 0) and supplier-type warehouses.\n\nAuthentication: Requires Bearer token.\n\nFilters (Spatie QueryBuilder):\n- filter[warehouse_id]: Exact match on warehouse ID\n- filter[product_id]: Exact match on product ID\n- filter[sku]: Partial match on product SKU\n- filter[search]: Partial match on product SKU or name\n- filter[stock_status]: Computed status filter (in_stock, low_stock, out_of_stock, incoming, allocated)\n\nSort fields: inventory_total, inventory_available, inventory_allocated, inventory_on_hold, inventory_in_transit, inventory_stock_value, updated_at, sku, product_name, warehouse_name (default: -inventory_total)\n\nPagination: page + per_page (default 10)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}/our-inventory","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id","our-inventory"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 101,\n      \"product_id\": 42,\n      \"sku\": \"WIDGET-001\",\n      \"product_name\": \"Blue Widget\",\n      \"warehouse\": {\n        \"id\": 2,\n        \"name\": \"Main Warehouse\",\n        \"type\": \"internal\"\n      },\n      \"inventory_total\": 500,\n      \"inventory_available\": 420,\n      \"inventory_allocated\": 50,\n      \"inventory_on_hold\": 10,\n      \"inventory_in_transit\": 20,\n      \"inventory_stock_value\": 1250.5,\n      \"updated_at\": \"2024-01-15T10:00:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 5,\n  \"per_page\": 10,\n  \"total\": 47,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"Get Our Inventory Stats for Supplier","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}/our-inventory/stats","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id","our-inventory","stats"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns aggregate stats for OUR internal warehouse inventory of products supplied by the given supplier.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nHonors the same filters as the index endpoint so stats reflect the active filter set.\n\nAuthentication: Requires Bearer token.\n\nResponse fields:\n- total_skus: Distinct product count\n- warehouses_count: Distinct internal warehouse count\n- total_available: Sum of available units across all rows\n- total_allocated: Sum of allocated units\n- total_on_hold: Sum of on-hold units\n- total_in_transit: Sum of in-transit units\n- total_units: Sum of total units (available + allocated + on_hold + in_transit)\n- total_stock_value: Sum of stock value (in account currency)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}/our-inventory/stats","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id","our-inventory","stats"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_skus\": 47,\n    \"warehouses_count\": 3,\n    \"total_available\": 12450,\n    \"total_allocated\": 320,\n    \"total_on_hold\": 85,\n    \"total_in_transit\": 540,\n    \"total_units\": 13395,\n    \"total_stock_value\": 24613.27\n  }\n}"}]},{"name":"List Supplier Inventories (Lookup)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-inventories/list?per_page=20&filter[supplier_id]=5","host":["{{baseUrl}}"],"path":["api","supplier-inventories","list"],"query":[{"key":"per_page","value":"20","description":"Records per page","disabled":true},{"key":"filter[supplier_id]","value":"5","description":"Filter by supplier ID","disabled":true}]},"description":"Returns a paginated list of supplier inventories via Spatie QueryBuilder. Same filter/sort support as the list view index.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/supplier-inventories/list?per_page=20&filter[supplier_id]=5","host":["{{baseUrl}}"],"path":["api","supplier-inventories","list"],"query":[{"key":"per_page","value":"20","description":"Records per page","disabled":true},{"key":"filter[supplier_id]","value":"5","description":"Filter by supplier ID","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"quantity\": 500,\n      \"in_stock\": null,\n      \"discontinued\": false,\n      \"eta\": null,\n      \"source\": \"api\",\n      \"product_id\": 42,\n      \"sku\": \"WIDGET-001\",\n      \"product_name\": \"Blue Widget\",\n      \"supplier_id\": 5,\n      \"updated_at\": \"2024-06-01T14:30:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"per_page\": 20,\n  \"total\": 35\n}"}]},{"name":"Get Supplier Inventory","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns a single supplier inventory record with related product and warehouse data.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": {\n    \"id\": 1,\n    \"quantity\": 500,\n    \"in_stock\": null,\n    \"discontinued\": false,\n    \"eta\": \"2024-09-01T00:00:00.000000Z\",\n    \"source\": \"user\",\n    \"product_id\": 42,\n    \"sku\": \"WIDGET-001\",\n    \"product_name\": \"Blue Widget\",\n    \"warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\"\n    },\n    \"supplier_sku\": \"ACME-W001\",\n    \"supplier_id\": 5,\n    \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2024-06-01T14:30:00.000000Z\"\n  }\n}"}]},{"name":"Update Supplier Inventory","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Updates a supplier inventory record. Sets source to 'user'. Provide either quantity OR in_stock (not both). On update every field is optional, so you can send a partial update — for example only `discontinued` or `eta` — without re-sending quantity/in_stock; the existing stock value is preserved.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- supplier_id (required on create, optional on update): ID of the supplier\n- product_id (required on create, optional on update): ID of the product\n- warehouse_id (required on create, optional on update): ID of the warehouse\n- quantity (required on create if in_stock not provided; optional on update): Stock quantity (must be non-negative number). Sending quantity clears in_stock.\n- in_stock (required on create if quantity not provided; optional on update): Boolean stock flag — true means in stock with unspecified quantity. Sending in_stock clears quantity.\n- discontinued (optional): Boolean, marks item as discontinued\n- eta (optional): Expected availability date (ISO date)\n- source (required on create, optional on update): One of: user, api","body":{"mode":"raw","raw":"{\n  \"supplier_id\": 5,\n  \"product_id\": 42,\n  \"warehouse_id\": 2,\n  \"quantity\": 750,\n  \"discontinued\": false,\n  \"eta\": \"2024-09-15\",\n  \"source\": \"user\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"supplier inventory updated successfully\",\n  \"data\": {\n    \"id\": 1,\n    \"quantity\": 750,\n    \"in_stock\": null,\n    \"discontinued\": false,\n    \"eta\": \"2024-09-15T00:00:00.000000Z\",\n    \"source\": \"user\",\n    \"product_id\": 42,\n    \"sku\": \"WIDGET-001\",\n    \"product_name\": \"Blue Widget\",\n    \"warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\"\n    },\n    \"supplier_id\": 5,\n    \"updated_at\": \"2024-06-15T14:00:00.000000Z\"\n  }\n}"}]},{"name":"Export Supplier Inventories","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}/export","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id","export"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Exports all supplier inventory records for a specific supplier as a CSV or Excel file download. The supplier_id filter is automatically applied.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPath params:\n- supplier: Supplier ID\n\nResponse: File download"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}/export","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id","export"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"\"SKU\",\"Product Name\",\"Warehouse\",\"Quantity\",\"In Stock\",\"Discontinued\",\"ETA\",\"Source\"\n\"WIDGET-001\",\"Blue Widget\",\"Main Warehouse\",\"500\",\"\",\"No\",\"\",\"user\""}]},{"name":"Archive Supplier Inventory","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}/archive","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id","archive"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Archives a single supplier inventory record. Idempotent — returns a warning if already archived.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}/archive","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id","archive"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"supplier inventory 1 archived successfully\",\n  \"data\": {\n    \"id\": 1,\n    \"quantity\": 500\n  }\n}"}]},{"name":"Unarchive Supplier Inventory","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}/unarchived","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id","unarchived"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Unarchives a single supplier inventory record.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}/unarchived","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id","unarchived"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"supplier inventory 1 unarchived successfully\",\n  \"data\": {\n    \"id\": 1\n  }\n}"}]},{"name":"Bulk Archive Supplier Inventories","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-inventories/archive","host":["{{baseUrl}}"],"path":["api","supplier-inventories","archive"]},"description":"Bulk archives multiple supplier inventory records by ID array or current list view filters.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/supplier-inventories/archive","host":["{{baseUrl}}"],"path":["api","supplier-inventories","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Supplier inventories archived successfully\"\n}"}]},{"name":"Bulk Unarchive Supplier Inventories","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-inventories/unarchive","host":["{{baseUrl}}"],"path":["api","supplier-inventories","unarchive"]},"description":"Bulk unarchives multiple supplier inventory records by ID array or current list view filters.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/supplier-inventories/unarchive","host":["{{baseUrl}}"],"path":["api","supplier-inventories","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Supplier inventories unarchived successfully\"\n}"}]},{"name":"Check Supplier Inventories Archivable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-inventories/archivable","host":["{{baseUrl}}"],"path":["api","supplier-inventories","archivable"]},"description":"Checks whether each provided supplier inventory ID can be safely archived.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/supplier-inventories/archivable","host":["{{baseUrl}}"],"path":["api","supplier-inventories","archivable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": [\n    {\n      \"id\": 1,\n      \"archivable\": true,\n      \"reason\": null\n    },\n    {\n      \"id\": 2,\n      \"archivable\": false,\n      \"reason\": \"Already archived\"\n    }\n  ]\n}"}]},{"name":"Check Supplier Inventories Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-inventories/deletable","host":["{{baseUrl}}"],"path":["api","supplier-inventories","deletable"]},"description":"Checks whether each provided supplier inventory ID can be safely deleted.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/supplier-inventories/deletable","host":["{{baseUrl}}"],"path":["api","supplier-inventories","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": [\n    {\n      \"id\": 1,\n      \"deletable\": true,\n      \"reason\": null\n    },\n    {\n      \"id\": 2,\n      \"deletable\": false,\n      \"reason\": \"Linked to active purchase orders\"\n    }\n  ]\n}"}]},{"name":"Download Import Template","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}/import/template","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id","import","template"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Downloads an XLSX import template for a specific supplier, pre-populated with headers, sample data, field guide, and reference data (products, warehouses) for the tenant's account.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPath params:\n- supplier: Supplier ID\n\nResponse: XLSX file download"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}/import/template","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id","import","template"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"[Binary XLSX file content]"}]},{"name":"Validate Import","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}/import/validate","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id","import","validate"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Validates import data rows for a supplier without creating any records. Safe to call repeatedly for UI previewing. Resolves SKU and warehouse names against the tenant's catalogue and returns a per-row preview alongside an aggregate summary.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThere is no hard row cap on this endpoint — SKU and warehouse lookups are chunked internally so large files can be submitted in one request. Per-request execution time and memory limits are raised for the duration of the call.\n\nAuthentication: Requires Bearer token.\n\nPath params:\n- supplier: Supplier ID\n\nBody fields:\n- rows (required): Array of raw import rows (1+).\n  - rows.*.sku (optional): Product SKU. Resolved case-insensitively.\n  - rows.*.quantity (optional): Stock quantity (numeric string)\n  - rows.*.eta (optional): ETA date string\n  - rows.*.in_stock (optional): Stock flag (1/0/true/false/yes/y)\n  - rows.*.discontinued (optional): Discontinued flag (1/0/true/false/yes/y)\n  - rows.*.warehouse (optional): Warehouse name. Falls back to the supplier's default warehouse when omitted.\n\nErrors:\n- 422 when standard validation fails (e.g. empty rows array).\n- 422 when the manager raises an exception during lookup (translated from a 500 so the client gets an actionable message).","body":{"mode":"raw","raw":"{\n  \"rows\": [\n    {\n      \"sku\": \"WIDGET-001\",\n      \"quantity\": \"500\",\n      \"eta\": \"2024-09-01\",\n      \"in_stock\": null,\n      \"discontinued\": \"0\",\n      \"warehouse\": \"Main Warehouse\"\n    },\n    {\n      \"sku\": \"GADGET-XL\",\n      \"quantity\": null,\n      \"eta\": null,\n      \"in_stock\": \"1\",\n      \"discontinued\": \"0\",\n      \"warehouse\": \"East Warehouse\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}/import/validate","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id","import","validate"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"items\": [\n    {\n      \"row_index\": 0,\n      \"sku\": \"WIDGET-001\",\n      \"product_id\": 42,\n      \"product_name\": \"Widget 001\",\n      \"warehouse\": {\n        \"id\": 2,\n        \"name\": \"Main Warehouse\"\n      },\n      \"quantity\": 500,\n      \"eta\": \"2024-09-01\",\n      \"in_stock\": false,\n      \"discontinued\": false,\n      \"errors\": []\n    },\n    {\n      \"row_index\": 1,\n      \"sku\": \"UNKNOWN-SKU\",\n      \"product_id\": null,\n      \"product_name\": null,\n      \"warehouse\": {\n        \"id\": 2,\n        \"name\": \"Main Warehouse\"\n      },\n      \"quantity\": null,\n      \"eta\": null,\n      \"in_stock\": false,\n      \"discontinued\": false,\n      \"errors\": [\n        \"SKU 'UNKNOWN-SKU' not found.\"\n      ]\n    }\n  ],\n  \"summary\": {\n    \"total_items\": 2,\n    \"total_errors\": 1,\n    \"total_valid\": 1\n  }\n}"}]},{"name":"Execute Import","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}/import","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id","import"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Queues a supplier inventory import — creates or updates inventory records for a specific supplier in the background.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThis endpoint is asynchronous. The HTTP response returns immediately (202 Accepted) with a `tracked_job_log_id` that the client polls via the tracked job log endpoints for progress and completion status. There is no hard row cap on the payload — the queued job processes items in chunks of 500 in its own transaction per chunk, so payloads of any size complete cleanly without holding a long-running transaction open.\n\nAuthentication: Requires Bearer token.\n\nPath params:\n- supplier: Supplier ID\n\nBody fields:\n- items (required): Array of inventory items (1+). Only the top-level `items` array is validated at the HTTP layer; each row is validated inside the queued job and any per-row problems are surfaced as failures in the tracked job results rather than as a 422. (Per-row `exists` validation was intentionally removed from the request layer to avoid an N+1 that timed out large payloads.)\n  - items.*.product_id (required): Product ID\n  - items.*.warehouse_id (optional): Warehouse ID\n  - items.*.quantity (optional): Stock quantity, integer >= 0\n  - items.*.eta (optional): Expected availability date\n  - items.*.in_stock (optional): Boolean stock flag\n  - items.*.discontinued (optional): Boolean discontinued flag\n\nErrors:\n- 422 only when the top-level `items` array is missing or empty.\n- 404 when the supplier does not exist.\n- Invalid rows (e.g. unknown product_id) do NOT return 422 — the import is queued and those rows are reported as failures in the tracked job results.\n\nNote: Use the validate endpoint first to preview and catch row-level errors before importing.","body":{"mode":"raw","raw":"{\n  \"items\": [\n    {\n      \"product_id\": 42,\n      \"warehouse_id\": 2,\n      \"quantity\": 500,\n      \"eta\": \"2024-09-01\",\n      \"in_stock\": null,\n      \"discontinued\": false\n    },\n    {\n      \"product_id\": 78,\n      \"warehouse_id\": 3,\n      \"quantity\": null,\n      \"eta\": null,\n      \"in_stock\": true,\n      \"discontinued\": false\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/supplier-inventories/{id}/import","host":["{{baseUrl}}"],"path":["api","supplier-inventories",":id","import"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 12345\n  },\n  \"message\": \"Import queued — track progress in the job tray.\"\n}"}]},{"name":"List Price List Documents","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/price-list/documents","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","price-list","documents"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]},"description":"Lists the supplier's price list source documents, newest first, including who uploaded each one. Only documents uploaded through the price list endpoints are returned — other files attached to the supplier are not included.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires scope:** `suppliers:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/price-list/documents","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","price-list","documents"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 512,\n      \"file_name\": \"PriceList_Aug2026.pdf\",\n      \"mime_type\": \"application/pdf\",\n      \"is_image\": false,\n      \"file_size\": 482113,\n      \"source\": \"price_list\",\n      \"description\": \"August 2026 wholesale list, USD\",\n      \"file_url\": \"{{protocol}}{{domain}}/api/documents/512/file\",\n      \"uploaded_by\": \"Jane Castillo\",\n      \"linked_records\": [\n        {\n          \"type\": \"App\\\\Models\\\\Supplier\",\n          \"id\": 12\n        }\n      ],\n      \"created_at\": \"2026-08-11T16:02:11.000000Z\"\n    }\n  ]\n}"}]},{"name":"Upload Price List Document","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/price-list/documents","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","price-list","documents"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]},"description":"Attaches a price list file to the supplier. Send as `multipart/form-data`. The document is tagged as a price list source document, so it appears in the price list documents listing.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAccepted file types: pdf, jpg, jpeg, png, gif, webp, heic, doc, docx, xls, xlsx, csv, txt, html, htm, eml, msg, zip. Maximum size: 20 MB.\n\nThe file type is decided by the **filename extension**, not by sniffing the file's contents. Files whose contents are an executable or script are rejected whatever they are named.\n\nAuthentication: Requires Bearer token.\n\n**Requires scope:** `suppliers:write`\n\n**Requires permission:** `suppliers.update`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]},{"key":"description","type":"text","value":""}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/price-list/documents","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","price-list","documents"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 512,\n    \"file_name\": \"PriceList_Aug2026.pdf\",\n    \"mime_type\": \"application/pdf\",\n    \"is_image\": false,\n    \"file_size\": 482113,\n    \"source\": \"price_list\",\n    \"description\": \"August 2026 wholesale list, USD\",\n    \"file_url\": \"{{protocol}}{{domain}}/api/documents/512/file\",\n    \"uploaded_by\": \"Jane Castillo\",\n    \"linked_records\": [\n      {\n        \"type\": \"App\\\\Models\\\\Supplier\",\n        \"id\": 12\n      }\n    ],\n    \"created_at\": \"2026-08-11T16:02:11.000000Z\"\n  }\n}"}]},{"name":"Update Price List Document Description","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/price-list/documents/{document}","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","price-list","documents",":document"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"},{"key":"document","value":"512","description":"Document ID"}]},"description":"Updates a document's description — the only editable field. The stored file itself is immutable; upload a new document to replace it. Send `description` as null to clear it. Maximum 2000 characters.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns 404 when the document is not a price list document of this supplier.\n\nAuthentication: Requires Bearer token.\n\n**Requires scope:** `suppliers:write`\n\n**Requires permission:** `suppliers.update`","body":{"mode":"raw","raw":"{\n  \"description\": \"September 2026 wholesale list, USD\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/price-list/documents/{document}","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","price-list","documents",":document"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"},{"key":"document","value":"512","description":"Document ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 512,\n    \"file_name\": \"PriceList_Aug2026.pdf\",\n    \"mime_type\": \"application/pdf\",\n    \"is_image\": false,\n    \"file_size\": 482113,\n    \"source\": \"price_list\",\n    \"description\": \"September 2026 wholesale list, USD\",\n    \"file_url\": \"{{protocol}}{{domain}}/api/documents/512/file\",\n    \"uploaded_by\": \"Jane Castillo\",\n    \"linked_records\": [\n      {\n        \"type\": \"App\\\\Models\\\\Supplier\",\n        \"id\": 12\n      }\n    ],\n    \"created_at\": \"2026-08-11T16:02:11.000000Z\"\n  }\n}"}]},{"name":"Delete Price List Document","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/price-list/documents/{document}","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","price-list","documents",":document"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"},{"key":"document","value":"512","description":"Document ID"}]},"description":"Deletes the document, its link to the supplier, and the stored file. This is permanent.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns 404 when the document is not a price list document of this supplier.\n\nAuthentication: Requires Bearer token.\n\n**Requires scope:** `suppliers:write`\n\n**Requires permission:** `suppliers.update`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/price-list/documents/{document}","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","price-list","documents",":document"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"},{"key":"document","value":"512","description":"Document ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Document deleted.\"\n}"}]},{"name":"Stream Price List Document File","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/price-list/documents/{document}/file","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","price-list","documents",":document","file"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"},{"key":"document","value":"512","description":"Document ID"}]},"description":"Streams the stored file inline with its own content type (e.g. `application/pdf`), suitable for in-browser preview or download. The response is the file itself, not JSON.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns 404 when the stored file is missing or the document is not a price list document of this supplier.\n\nAuthentication: Requires Bearer token.\n\n**Requires scope:** `suppliers:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/price-list/documents/{document}/file","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","price-list","documents",":document","file"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"},{"key":"document","value":"512","description":"Document ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary file contents)"}]},{"name":"List Supplier Price List","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/price-list","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","price-list"],"variable":[{"key":"supplier","value":"14","description":"Supplier id"}]},"description":"Paginated price list for one supplier: every product this supplier can supply, joined against the chosen retail pricing tier and the chosen supplier cost tier, with the margin between them pre-computed in five conventions.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: suppliers)\n\nEach row's `margin` object restates the same gap five ways so the caller does not have to:\n- `margin_pct` — (retail - cost) / retail\n- `off_retail_pct` — the same number read as a discount off the retail list price\n- `cost_ratio_pct` — cost / retail\n- `markup_pct` — (retail - cost) / cost\n- `multiplier` — retail / cost\n- `amount` — retail - cost, in the retail tier's currency\n\nEvery margin field is null when either side is missing or the retail price is zero, so unpriced products never produce a misleading 100%.\n\nTiers: when `sell_tier_id` / `cost_tier_id` are omitted the account's default retail and default supplier tiers are used. Call the metadata endpoint for the selectable tiers, the supplier's brands, and the column catalogue.\n\nFiltering\n---------\nFilters are sent as `filter[<name>]` query parameters.\n\nQuick filters:\n- `filter[search]` — partial match across SKU, product name, barcode, MPN and supplier SKU\n- `filter[brand_id]` — one or more brand ids, comma separated. Valid ids come from the metadata endpoint\n- `filter[only_priced]` — `1` keeps only products carrying both a retail price and a supplier cost on the selected tiers\n- `filter[archived]` — `active` (default), `only` for archived products only, `all` for both\n\nColumn filters take the form `filter[<column>.<operator>]`, for example `filter[sku.starts_with]=CB-`, `filter[margin_pct.greater_than]=50`, `filter[cost_price.between]=10,50`.\n\n- Text columns — sku, name, barcode, mpn, hts_code, country_of_origin, brand_name, supplier_sku. Operators: contains, does_not_contain, is, is_not, is_one_of, is_not_one_of, starts_with, does_not_start_with, ends_with, does_not_end_with, is_empty, is_not_empty\n- Numeric columns — sell_price, cost_price, margin_amount, margin_pct, off_retail_pct, markup_pct, multiplier, minimum_order_quantity, leadtime, case_quantity, on_hand_quantity, weight. Operators: is, is_not, is_one_of, is_not_one_of, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between, is_empty, is_not_empty\n- Boolean columns — is_default_supplier, archived. Operator: `is` (and `is_not` for archived)\n\nFor AND/OR logic and nested groups, send `filter_groups` instead: a base64-encoded JSON tree of the shape `{\"conjunction\":\"and\",\"children\":[{\"type\":\"condition\",\"condition\":{\"column\":\"margin_pct\",\"operator\":\"greater_than\",\"value\":50}}]}`. A `children` entry may also be `{\"type\":\"group\",\"group\":{...}}` to nest a sub-group with its own conjunction. Conditions naming a column or operator that is not listed above are ignored.\n\nArchived products are excluded unless the archived scope says otherwise."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/price-list","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","price-list"],"variable":[{"key":"supplier","value":"14","description":"Supplier id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 4821,\n      \"sku\": \"80687599\",\n      \"name\": \"1 Pack - Reusable Cloth Diaper One Size - Cactus Azul\",\n      \"barcode\": \"6903148319130\",\n      \"mpn\": \"CB-OS-CACTUS\",\n      \"brand_name\": \"Charlie Banana\",\n      \"supplier_sku\": \"CB-888-CACTUS\",\n      \"sell_price\": 24.99,\n      \"cost_price\": 9,\n      \"margin\": {\n        \"margin_pct\": 64,\n        \"off_retail_pct\": 64,\n        \"cost_ratio_pct\": 36,\n        \"markup_pct\": 177.7,\n        \"multiplier\": 2.78,\n        \"amount\": 15.99\n      },\n      \"minimum_order_quantity\": 1,\n      \"leadtime\": 30,\n      \"case_quantity\": 12,\n      \"on_hand_quantity\": 24,\n      \"weight\": 0.44,\n      \"weight_unit\": \"lb\",\n      \"dimensions\": \"6.8 x 1.5 x 7.08 in\",\n      \"hts_code\": \"9619.00.7100\",\n      \"country_of_origin\": \"US\",\n      \"is_default_supplier\": true,\n      \"is_archived\": false\n    },\n    {\n      \"product_id\": 4822,\n      \"sku\": \"80687601\",\n      \"name\": \"3 Pack - Reusable Cloth Diaper One Size - Pastel Set\",\n      \"barcode\": \"6903148319147\",\n      \"mpn\": \"CB-OS-PASTEL3\",\n      \"brand_name\": \"Charlie Banana\",\n      \"supplier_sku\": \"CB-888-PASTEL3\",\n      \"sell_price\": 64.99,\n      \"cost_price\": 26.5,\n      \"margin\": {\n        \"margin_pct\": 59.2,\n        \"off_retail_pct\": 59.2,\n        \"cost_ratio_pct\": 40.8,\n        \"markup_pct\": 145.2,\n        \"multiplier\": 2.45,\n        \"amount\": 38.49\n      },\n      \"minimum_order_quantity\": 1,\n      \"leadtime\": 30,\n      \"case_quantity\": 6,\n      \"on_hand_quantity\": 0,\n      \"weight\": 1.32,\n      \"weight_unit\": \"lb\",\n      \"dimensions\": \"9.5 x 3.0 x 7.1 in\",\n      \"hts_code\": \"9619.00.7100\",\n      \"country_of_origin\": \"US\",\n      \"is_default_supplier\": true,\n      \"is_archived\": false\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 7,\n  \"per_page\": 25,\n  \"total\": 171,\n  \"from\": 1,\n  \"to\": 25,\n  \"first_page_url\": \"https://example.sku.io/api/suppliers/14/price-list?page=1\",\n  \"last_page_url\": \"https://example.sku.io/api/suppliers/14/price-list?page=7\",\n  \"next_page_url\": \"https://example.sku.io/api/suppliers/14/price-list?page=2\",\n  \"prev_page_url\": null,\n  \"path\": \"https://example.sku.io/api/suppliers/14/price-list\"\n}"}]},{"name":"Get Supplier Price List Metadata","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/price-list/meta","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","price-list","meta"],"variable":[{"key":"supplier","value":"14","description":"Supplier id"}]},"description":"Everything needed to drive the price list's pickers, fetched once rather than on every page:\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n- `tiers.sell` / `tiers.cost` — selectable retail and supplier pricing tiers with their currency codes\n- `selected_tiers` — the tier ids, names and currencies actually resolved for this request\n- `brands` — the distinct product brands this supplier supplies (id + name), for the brand filter\n- `columns` — the full column catalogue: `key`, human `label`, and `default` (whether the column is shown/exported unless the caller overrides it)\n\nThe `columns[].key` values are the same tokens accepted by the export endpoint's `columns` parameter.\n\nAuthentication: Requires Bearer token (scope: suppliers)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/price-list/meta","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","price-list","meta"],"variable":[{"key":"supplier","value":"14","description":"Supplier id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tiers\": {\n      \"sell\": [\n        {\n          \"id\": 1,\n          \"name\": \"Retail\",\n          \"currency_code\": \"USD\",\n          \"is_default\": true\n        },\n        {\n          \"id\": 4,\n          \"name\": \"Wholesale\",\n          \"currency_code\": \"USD\",\n          \"is_default\": false\n        },\n        {\n          \"id\": 9,\n          \"name\": \"Retail (EUR)\",\n          \"currency_code\": \"EUR\",\n          \"is_default\": false\n        }\n      ],\n      \"cost\": [\n        {\n          \"id\": 1,\n          \"name\": \"Wholesale\",\n          \"currency_code\": \"USD\",\n          \"is_default\": true\n        },\n        {\n          \"id\": 3,\n          \"name\": \"Volume 500+\",\n          \"currency_code\": \"USD\",\n          \"is_default\": false\n        }\n      ]\n    },\n    \"selected_tiers\": {\n      \"sell_tier_id\": 1,\n      \"sell_tier_name\": \"Retail\",\n      \"sell_tier_currency\": \"USD\",\n      \"cost_tier_id\": 1,\n      \"cost_tier_name\": \"Wholesale\",\n      \"cost_tier_currency\": \"USD\"\n    },\n    \"brands\": [\n      {\n        \"id\": 16,\n        \"name\": \"Charlie Banana\"\n      }\n    ],\n    \"columns\": [\n      {\n        \"key\": \"sku\",\n        \"label\": \"SKU\",\n        \"default\": true\n      },\n      {\n        \"key\": \"name\",\n        \"label\": \"Product Name\",\n        \"default\": true\n      },\n      {\n        \"key\": \"barcode\",\n        \"label\": \"Barcode\",\n        \"default\": true\n      },\n      {\n        \"key\": \"mpn\",\n        \"label\": \"MPN\",\n        \"default\": false\n      },\n      {\n        \"key\": \"brand_name\",\n        \"label\": \"Brand\",\n        \"default\": true\n      },\n      {\n        \"key\": \"supplier_sku\",\n        \"label\": \"Supplier SKU\",\n        \"default\": true\n      },\n      {\n        \"key\": \"sell_price\",\n        \"label\": \"Retail Price\",\n        \"default\": true\n      },\n      {\n        \"key\": \"cost_price\",\n        \"label\": \"Supplier Cost\",\n        \"default\": true\n      },\n      {\n        \"key\": \"margin_amount\",\n        \"label\": \"Margin\",\n        \"default\": false\n      },\n      {\n        \"key\": \"margin_pct\",\n        \"label\": \"Margin %\",\n        \"default\": true\n      },\n      {\n        \"key\": \"off_retail_pct\",\n        \"label\": \"Off Retail %\",\n        \"default\": true\n      },\n      {\n        \"key\": \"markup_pct\",\n        \"label\": \"Markup %\",\n        \"default\": false\n      },\n      {\n        \"key\": \"multiplier\",\n        \"label\": \"Multiplier\",\n        \"default\": false\n      },\n      {\n        \"key\": \"minimum_order_quantity\",\n        \"label\": \"MOQ\",\n        \"default\": true\n      },\n      {\n        \"key\": \"leadtime\",\n        \"label\": \"Lead Time (days)\",\n        \"default\": true\n      },\n      {\n        \"key\": \"case_quantity\",\n        \"label\": \"Case Qty\",\n        \"default\": false\n      },\n      {\n        \"key\": \"on_hand_quantity\",\n        \"label\": \"On Hand\",\n        \"default\": true\n      },\n      {\n        \"key\": \"weight\",\n        \"label\": \"Weight\",\n        \"default\": false\n      },\n      {\n        \"key\": \"dimensions\",\n        \"label\": \"Dimensions\",\n        \"default\": false\n      },\n      {\n        \"key\": \"hts_code\",\n        \"label\": \"HTS Code\",\n        \"default\": false\n      },\n      {\n        \"key\": \"country_of_origin\",\n        \"label\": \"Country of Origin\",\n        \"default\": false\n      },\n      {\n        \"key\": \"is_default_supplier\",\n        \"label\": \"Default Supplier\",\n        \"default\": false\n      },\n      {\n        \"key\": \"archived\",\n        \"label\": \"Archived\",\n        \"default\": false\n      }\n    ]\n  }\n}"}]},{"name":"Export Supplier Price List","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/price-list/export?format=xlsx","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","price-list","export"],"query":[{"key":"format","value":"xlsx","description":"File format: xlsx (default) or csv.","disabled":true}],"variable":[{"key":"supplier","value":"14","description":"Supplier id"}]},"description":"Downloads the supplier price list as a spreadsheet.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: suppliers)\n\nScope of the file:\n- Pass `product_ids` to export an explicit set of rows.\n- Otherwise the export mirrors whatever the filter and sort parameters resolve to, so sending the same parameters as the list endpoint produces exactly the rows that list returned.\n- Send no filters at all to export the supplier's whole price list.\n\nColumns: `columns` accepts the `key` values from the metadata endpoint's column catalogue, and the file follows the order they are listed there. Omit it and the default column set is used.\n\nResponse: a file download, not JSON. `format=xlsx` returns `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`; `format=csv` returns `text/csv`. The filename is `<supplier-slug>-price-list-<YYYY-MM-DD>.<ext>`.\n\nTiers: when `sell_tier_id` / `cost_tier_id` are omitted the account's default retail and default supplier tiers are used. Call the metadata endpoint for the selectable tiers, the supplier's brands, and the column catalogue.\n\nFiltering\n---------\nFilters are sent as `filter[<name>]` query parameters.\n\nQuick filters:\n- `filter[search]` — partial match across SKU, product name, barcode, MPN and supplier SKU\n- `filter[brand_id]` — one or more brand ids, comma separated. Valid ids come from the metadata endpoint\n- `filter[only_priced]` — `1` keeps only products carrying both a retail price and a supplier cost on the selected tiers\n- `filter[archived]` — `active` (default), `only` for archived products only, `all` for both\n\nColumn filters take the form `filter[<column>.<operator>]`, for example `filter[sku.starts_with]=CB-`, `filter[margin_pct.greater_than]=50`, `filter[cost_price.between]=10,50`.\n\n- Text columns — sku, name, barcode, mpn, hts_code, country_of_origin, brand_name, supplier_sku. Operators: contains, does_not_contain, is, is_not, is_one_of, is_not_one_of, starts_with, does_not_start_with, ends_with, does_not_end_with, is_empty, is_not_empty\n- Numeric columns — sell_price, cost_price, margin_amount, margin_pct, off_retail_pct, markup_pct, multiplier, minimum_order_quantity, leadtime, case_quantity, on_hand_quantity, weight. Operators: is, is_not, is_one_of, is_not_one_of, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between, is_empty, is_not_empty\n- Boolean columns — is_default_supplier, archived. Operator: `is` (and `is_not` for archived)\n\nFor AND/OR logic and nested groups, send `filter_groups` instead: a base64-encoded JSON tree of the shape `{\"conjunction\":\"and\",\"children\":[{\"type\":\"condition\",\"condition\":{\"column\":\"margin_pct\",\"operator\":\"greater_than\",\"value\":50}}]}`. A `children` entry may also be `{\"type\":\"group\",\"group\":{...}}` to nest a sub-group with its own conjunction. Conditions naming a column or operator that is not listed above are ignored."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/price-list/export?format=xlsx","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","price-list","export"],"query":[{"key":"format","value":"xlsx","description":"File format: xlsx (default) or csv.","disabled":true}],"variable":[{"key":"supplier","value":"14","description":"Supplier id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":""}]},{"name":"List Supplier Compliance Documents","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/compliance-documents","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","compliance-documents"],"variable":[{"key":"supplier","value":"","description":"The supplier ID."}]},"description":"Compliance certificates held for a supplier — NSF, GMP, TIC documentation, insurance, audit reports and label versions, soonest expiry first.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThese belong to the supplier and expire on their own clock, independent of any lot; a lot's Certificate of Analysis is filed against the lot instead.\n\n`status` reports whether every required document type is present and current. A required type with no document reads as missing; one whose newest document has lapsed reads as expired."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/compliance-documents","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","compliance-documents"],"variable":[{"key":"supplier","value":"","description":"The supplier ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"documents\": [\n      {\n        \"id\": 517,\n        \"file_name\": \"nsf-certificate-2027.pdf\",\n        \"mime_type\": \"application/pdf\",\n        \"is_image\": false,\n        \"file_size\": 96000,\n        \"source\": \"supplier_compliance\",\n        \"description\": \"NSF facility registration\",\n        \"document_type\": \"nsf_cert\",\n        \"document_type_label\": \"NSF Certificate\",\n        \"document_type_color\": \"info\",\n        \"issued_at\": \"2026-03-01\",\n        \"expires_at\": \"2027-03-01\",\n        \"expiry_state\": \"valid\",\n        \"issuer\": \"NSF International\",\n        \"reference\": \"C-40219\",\n        \"file_url\": \"https://acme.sku.io/api/suppliers/14/compliance-documents/517/file\",\n        \"uploaded_by\": \"Dana Reyes\",\n        \"linked_records\": [\n          {\n            \"type\": \"App\\\\Models\\\\Supplier\",\n            \"id\": 14\n          }\n        ],\n        \"created_at\": \"2026-08-18T16:10:00.000000Z\"\n      }\n    ],\n    \"requirements\": [\n      \"nsf_cert\",\n      \"gmp_cert\"\n    ],\n    \"status\": {\n      \"required\": [\n        \"nsf_cert\",\n        \"gmp_cert\"\n      ],\n      \"missing\": [\n        \"gmp_cert\"\n      ],\n      \"expired\": [],\n      \"is_complete\": false\n    },\n    \"document_types\": [\n      {\n        \"value\": \"nsf_cert\",\n        \"label\": \"NSF Certificate\",\n        \"color\": \"info\"\n      },\n      {\n        \"value\": \"gmp_cert\",\n        \"label\": \"GMP Certificate\",\n        \"color\": \"info\"\n      },\n      {\n        \"value\": \"tic_doc\",\n        \"label\": \"TIC Documentation\",\n        \"color\": \"info\"\n      },\n      {\n        \"value\": \"label_version\",\n        \"label\": \"Label Version\",\n        \"color\": \"teal\"\n      },\n      {\n        \"value\": \"insurance\",\n        \"label\": \"Insurance Certificate\",\n        \"color\": \"info\"\n      },\n      {\n        \"value\": \"audit_report\",\n        \"label\": \"Audit Report\",\n        \"color\": \"info\"\n      },\n      {\n        \"value\": \"spec_sheet\",\n        \"label\": \"Specification Sheet\",\n        \"color\": \"teal\"\n      },\n      {\n        \"value\": \"other\",\n        \"label\": \"Other\",\n        \"color\": \"grey\"\n      }\n    ]\n  }\n}"}]},{"name":"Upload Supplier Compliance Document","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/compliance-documents","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","compliance-documents"],"variable":[{"key":"supplier","value":"","description":"The supplier ID."}]},"description":"Store a compliance certificate against a supplier.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nSet `expires_at` to put the certificate on the renewal schedule — reminders are raised at 90, 60, 30 and 15 days before it lapses, once per threshold.\n\nSend as multipart/form-data. Max file size 20 MB.","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]},{"key":"document_type","type":"text","value":""},{"key":"issuer","type":"text","value":""},{"key":"reference","type":"text","value":""},{"key":"issued_at","type":"text","value":""},{"key":"expires_at","type":"text","value":""},{"key":"description","type":"text","value":""}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/compliance-documents","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","compliance-documents"],"variable":[{"key":"supplier","value":"","description":"The supplier ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 517,\n    \"file_name\": \"nsf-certificate-2027.pdf\",\n    \"mime_type\": \"application/pdf\",\n    \"is_image\": false,\n    \"file_size\": 96000,\n    \"source\": \"supplier_compliance\",\n    \"description\": \"NSF facility registration\",\n    \"document_type\": \"nsf_cert\",\n    \"document_type_label\": \"NSF Certificate\",\n    \"document_type_color\": \"info\",\n    \"issued_at\": \"2026-03-01\",\n    \"expires_at\": \"2027-03-01\",\n    \"expiry_state\": \"valid\",\n    \"issuer\": \"NSF International\",\n    \"reference\": \"C-40219\",\n    \"file_url\": \"https://acme.sku.io/api/suppliers/14/compliance-documents/517/file\",\n    \"uploaded_by\": \"Dana Reyes\",\n    \"linked_records\": [\n      {\n        \"type\": \"App\\\\Models\\\\Supplier\",\n        \"id\": 14\n      }\n    ],\n    \"created_at\": \"2026-08-18T16:10:00.000000Z\"\n  }\n}"}]},{"name":"Update Supplier Compliance Document","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/compliance-documents/{document}","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","compliance-documents",":document"],"variable":[{"key":"supplier","value":"","description":"The supplier ID."},{"key":"document","value":"","description":"The document ID."}]},"description":"Correct a certificate's metadata.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nMoving `expires_at` further out clears the renewal reminders it no longer satisfies, so a renewed certificate can warn again next cycle rather than being permanently silenced.","body":{"mode":"raw","raw":"{\n  \"document_type\": \"nsf_cert\",\n  \"issuer\": \"NSF International\",\n  \"reference\": \"C-40219\",\n  \"issued_at\": \"2027-03-01\",\n  \"expires_at\": \"2028-03-01\",\n  \"description\": \"Renewed\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/compliance-documents/{document}","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","compliance-documents",":document"],"variable":[{"key":"supplier","value":"","description":"The supplier ID."},{"key":"document","value":"","description":"The document ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 517,\n  \"file_name\": \"nsf-certificate-2027.pdf\",\n  \"mime_type\": \"application/pdf\",\n  \"is_image\": false,\n  \"file_size\": 96000,\n  \"source\": \"supplier_compliance\",\n  \"description\": \"NSF facility registration\",\n  \"document_type\": \"nsf_cert\",\n  \"document_type_label\": \"NSF Certificate\",\n  \"document_type_color\": \"info\",\n  \"issued_at\": \"2027-03-01\",\n  \"expires_at\": \"2028-03-01\",\n  \"expiry_state\": \"valid\",\n  \"issuer\": \"NSF International\",\n  \"reference\": \"C-40219\",\n  \"file_url\": \"https://acme.sku.io/api/suppliers/14/compliance-documents/517/file\",\n  \"uploaded_by\": \"Dana Reyes\",\n  \"linked_records\": [\n    {\n      \"type\": \"App\\\\Models\\\\Supplier\",\n      \"id\": 14\n    }\n  ],\n  \"created_at\": \"2026-08-18T16:10:00.000000Z\"\n}"}]},{"name":"Delete Supplier Compliance Document","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/compliance-documents/{document}","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","compliance-documents",":document"],"variable":[{"key":"supplier","value":"","description":"The supplier ID."},{"key":"document","value":"","description":"The document ID."}]},"description":"Remove a compliance certificate from a supplier.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/compliance-documents/{document}","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","compliance-documents",":document"],"variable":[{"key":"supplier","value":"","description":"The supplier ID."},{"key":"document","value":"","description":"The document ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Certificate deleted.\"\n}"}]},{"name":"Download Supplier Compliance Document","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/compliance-documents/{document}/file","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","compliance-documents",":document","file"],"variable":[{"key":"supplier","value":"","description":"The supplier ID."},{"key":"document","value":"","description":"The document ID."}]},"description":"Stream a supplier compliance certificate. Returns the stored file with its original content type, not JSON.\n\n**Required scope: `suppliers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."}},{"name":"Set Supplier Compliance Requirements","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/compliance-requirements","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","compliance-requirements"],"variable":[{"key":"supplier","value":"","description":"The supplier ID."}]},"description":"Set which document types this supplier must keep current.\n\n**Required scope: `suppliers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nSend an empty list to clear every requirement. A supplier missing a required type, or holding one that has expired, is reported as incomplete — advisory only, it never blocks purchasing.","body":{"mode":"raw","raw":"{\n  \"document_types\": [\n    \"nsf_cert\",\n    \"gmp_cert\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/suppliers/{supplier}/compliance-requirements","host":["{{baseUrl}}"],"path":["api","suppliers",":supplier","compliance-requirements"],"variable":[{"key":"supplier","value":"","description":"The supplier ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"requirements\": [\n      \"nsf_cert\",\n      \"gmp_cert\"\n    ],\n    \"status\": {\n      \"required\": [\n        \"nsf_cert\",\n        \"gmp_cert\"\n      ],\n      \"missing\": [\n        \"gmp_cert\"\n      ],\n      \"expired\": [],\n      \"is_complete\": false\n    }\n  }\n}"}]}]},{"name":"Returns & RMAs","item":[{"name":"List Return Reasons","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/return-reasons?archived=0&search=&sort=name&page=1&limit=25","host":["{{baseUrl}}"],"path":["api","v2","return-reasons"],"query":[{"key":"archived","value":"0","description":"Filter by archived status: 0 (active) or 1 (archived). Default: 0","disabled":true},{"key":"search","value":"","description":"Search return reasons by name","disabled":true},{"key":"sort","value":"name","description":"Sort field. Prefix with - for descending. Allowed: id, name, created_at, updated_at","disabled":true},{"key":"page","value":"1","description":"Page number for pagination","disabled":true},{"key":"limit","value":"25","description":"Records per page","disabled":true}]},"description":"List all return reasons using the standard list interface. Supports filtering, sorting, search and pagination.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nSort fields: id, name, created_at, updated_at."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/return-reasons?archived=0&search=&sort=name&page=1&limit=25","host":["{{baseUrl}}"],"path":["api","v2","return-reasons"],"query":[{"key":"archived","value":"0","description":"Filter by archived status: 0 (active) or 1 (archived). Default: 0","disabled":true},{"key":"search","value":"","description":"Search return reasons by name","disabled":true},{"key":"sort","value":"name","description":"Sort field. Prefix with - for descending. Allowed: id, name, created_at, updated_at","disabled":true},{"key":"page","value":"1","description":"Page number for pagination","disabled":true},{"key":"limit","value":"25","description":"Records per page","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Defective Item\",\n      \"created_at\": \"2024-01-01T00:00:00Z\",\n      \"updated_at\": \"2024-01-01T00:00:00Z\",\n      \"archived_at\": null\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Wrong Item Shipped\",\n      \"created_at\": \"2024-01-01T00:00:00Z\",\n      \"updated_at\": \"2024-01-01T00:00:00Z\",\n      \"archived_at\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 2\n}"}]},{"name":"List RMAs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/rmas?page=1&per_page=10&sort=-created_at&filter[search]=&filter[status]=&filter[customer_id]=&filter[sales_order_id]=&filter[rma_number][is]=&filter[rma_number][contains]=&filter[rma_date][from]=&filter[rma_date][to]=&filter[created_at][from]=&filter[created_at][to]=","host":["{{baseUrl}}"],"path":["api","rmas"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default: 10)","disabled":true},{"key":"sort","value":"-created_at","description":"Sort field. Prefix with - for descending. Allowed: rma_number, status, rma_date, created_at","disabled":true},{"key":"filter[search]","value":"","description":"Search by RMA number or sales order number","disabled":true},{"key":"filter[status]","value":"","description":"Filter by status: draft, approved, in_transit, received, closed, canceled","disabled":true},{"key":"filter[customer_id]","value":"","description":"Filter by customer ID (searches via sales order)","disabled":true},{"key":"filter[sales_order_id]","value":"","description":"Filter by exact sales order ID","disabled":true},{"key":"filter[rma_number][is]","value":"","description":"Filter by exact RMA number","disabled":true},{"key":"filter[rma_number][contains]","value":"","description":"Filter RMA number containing value","disabled":true},{"key":"filter[rma_date][from]","value":"","description":"Filter RMA date from (Y-m-d)","disabled":true},{"key":"filter[rma_date][to]","value":"","description":"Filter RMA date to (Y-m-d)","disabled":true},{"key":"filter[created_at][from]","value":"","description":"Filter created_at from (Y-m-d)","disabled":true},{"key":"filter[created_at][to]","value":"","description":"Filter created_at to (Y-m-d)","disabled":true}]},"description":"List all RMAs with pagination, filtering, and sorting via Spatie QueryBuilder.\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFilter system: Uses Spatie QueryBuilder filter syntax.\n- filter[search]: Searches rma_number and related sales_order_number\n- filter[status]: draft | approved | in_transit | received | closed | canceled\n- filter[customer_id]: Filters via associated sales order\n- filter[sales_order_id]: Exact match\n- Advanced text filters: filter[rma_number][is], [is_not], [contains], [does_not_contain], [starts_with], [ends_with], [is_empty], [is_not_empty]\n- Advanced date filters: filter[rma_date][from], [to], filter[created_at][from], [to]\n\nAllowed sorts: rma_number, status, rma_date, created_at. Default sort: -created_at."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/rmas?page=1&per_page=10&sort=-created_at&filter[search]=&filter[status]=&filter[customer_id]=&filter[sales_order_id]=&filter[rma_number][is]=&filter[rma_number][contains]=&filter[rma_date][from]=&filter[rma_date][to]=&filter[created_at][from]=&filter[created_at][to]=","host":["{{baseUrl}}"],"path":["api","rmas"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default: 10)","disabled":true},{"key":"sort","value":"-created_at","description":"Sort field. Prefix with - for descending. Allowed: rma_number, status, rma_date, created_at","disabled":true},{"key":"filter[search]","value":"","description":"Search by RMA number or sales order number","disabled":true},{"key":"filter[status]","value":"","description":"Filter by status: draft, approved, in_transit, received, closed, canceled","disabled":true},{"key":"filter[customer_id]","value":"","description":"Filter by customer ID (searches via sales order)","disabled":true},{"key":"filter[sales_order_id]","value":"","description":"Filter by exact sales order ID","disabled":true},{"key":"filter[rma_number][is]","value":"","description":"Filter by exact RMA number","disabled":true},{"key":"filter[rma_number][contains]","value":"","description":"Filter RMA number containing value","disabled":true},{"key":"filter[rma_date][from]","value":"","description":"Filter RMA date from (Y-m-d)","disabled":true},{"key":"filter[rma_date][to]","value":"","description":"Filter RMA date to (Y-m-d)","disabled":true},{"key":"filter[created_at][from]","value":"","description":"Filter created_at from (Y-m-d)","disabled":true},{"key":"filter[created_at][to]","value":"","description":"Filter created_at to (Y-m-d)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"rma_number\": \"RMA-0001\",\n      \"status\": \"approved\",\n      \"sales_order_id\": 10,\n      \"sales_order\": {\n        \"id\": 10,\n        \"sales_order_number\": \"SO-0010\",\n        \"order_date\": \"2024-01-10\"\n      },\n      \"is_orphaned\": false,\n      \"orphan_info\": null,\n      \"customer_id\": 5,\n      \"warehouse_id\": 2,\n      \"warehouse\": {\n        \"id\": 2,\n        \"name\": \"Main Warehouse\",\n        \"address\": null\n      },\n      \"notes\": \"Customer returning defective unit\",\n      \"tracking_number\": null,\n      \"carrier\": null,\n      \"shipped_at\": null,\n      \"expected_at\": null,\n      \"approved_at\": \"2024-01-15T10:00:00.000000Z\",\n      \"approved_by_user_id\": 1,\n      \"rma_date\": \"2024-01-14\",\n      \"is_amazon_fba_return\": false,\n      \"rma_lines\": [\n        {\n          \"id\": 1,\n          \"product_id\": 20,\n          \"product\": {\n            \"id\": 20,\n            \"sku\": \"WIDGET-A\",\n            \"name\": \"Widget A\"\n          },\n          \"expected_quantity\": 2,\n          \"received_quantity\": 0,\n          \"remaining_quantity\": 2,\n          \"return_reason_id\": 1,\n          \"notes\": null\n        }\n      ],\n      \"created_at\": \"2024-01-14T08:00:00.000000Z\",\n      \"updated_at\": \"2024-01-15T10:00:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 10,\n  \"total\": 27\n}"}]},{"name":"Create RMA","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/rmas","host":["{{baseUrl}}"],"path":["api","rmas"]},"description":"Create a new RMA for a fulfilled sales order.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nValidation rules:\n- sales_order_id: required, must be an order that has been fulfilled (unless sales_credit_id is provided)\n- warehouse_id: required\n- status: optional, allowed values: draft, approved\n- notes: optional, max 1000 chars\n- sales_credit_id: optional, if provided skips fulfillment and quantity validation\n- rma_lines: required array with at least 1 line\n  - product_id: required\n  - expected_quantity: required, min 1, cannot exceed fulfilled minus already RMA'd quantity\n  - sales_order_line_id: optional, must belong to the specified sales order and match the product\n  - return_reason_id: optional\n  - notes: optional, max 500 chars\n\n**Requires permission:** `returns.create`","body":{"mode":"raw","raw":"{\n  \"sales_order_id\": 10,\n  \"warehouse_id\": 2,\n  \"status\": \"draft\",\n  \"notes\": \"Customer returning 2 units of Widget A due to defect\",\n  \"sales_credit_id\": null,\n  \"rma_lines\": [\n    {\n      \"product_id\": 20,\n      \"expected_quantity\": 2,\n      \"sales_order_line_id\": 55,\n      \"return_reason_id\": 1,\n      \"notes\": \"Unit arrived damaged\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/rmas","host":["{{baseUrl}}"],"path":["api","rmas"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 15,\n    \"rma_number\": \"RMA-0015\",\n    \"status\": \"draft\",\n    \"sales_order_id\": 10,\n    \"sales_order\": {\n      \"id\": 10,\n      \"sales_order_number\": \"SO-0010\",\n      \"order_date\": \"2024-01-10\"\n    },\n    \"is_orphaned\": false,\n    \"warehouse_id\": 2,\n    \"warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"address\": null\n    },\n    \"notes\": \"Customer returning 2 units of Widget A due to defect\",\n    \"rma_lines\": [\n      {\n        \"id\": 20,\n        \"product_id\": 20,\n        \"product\": {\n          \"id\": 20,\n          \"sku\": \"WIDGET-A\",\n          \"name\": \"Widget A\"\n        },\n        \"expected_quantity\": 2,\n        \"received_quantity\": 0,\n        \"remaining_quantity\": 2,\n        \"return_reason_id\": 1,\n        \"notes\": \"Unit arrived damaged\"\n      }\n    ],\n    \"created_at\": \"2024-01-20T10:00:00.000000Z\",\n    \"updated_at\": \"2024-01-20T10:00:00.000000Z\"\n  },\n  \"message\": \"RMA created successfully\"\n}"}]},{"name":"Get RMA","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}","host":["{{baseUrl}}"],"path":["api","rmas",":rma"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]},"description":"Get a single RMA with full detail including lines, return receipts, sales credits, and approved-by user.\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nLoads: salesOrder.customer, warehouse.address, approvedBy, rmaLines.product, rmaLines.returnReason, rmaLines.salesOrderLine, rmaLines.amazonFbaCustomerReturns, returnReceipts.receivedBy, returnReceipts.returnReceiptLines.product, returnReceipts.returnReceiptLines.returnReason, salesCredits."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/rmas/{rma}","host":["{{baseUrl}}"],"path":["api","rmas",":rma"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_number\": \"RMA-0001\",\n    \"status\": \"approved\",\n    \"sales_order_id\": 10,\n    \"sales_order\": {\n      \"id\": 10,\n      \"sales_order_number\": \"SO-0010\",\n      \"order_date\": \"2024-01-10\"\n    },\n    \"is_orphaned\": false,\n    \"orphan_info\": null,\n    \"customer_id\": 5,\n    \"customer\": {\n      \"id\": 5,\n      \"name\": \"John Doe\",\n      \"email\": \"john@example.com\",\n      \"company\": \"Acme Corp\",\n      \"phone\": \"+1-555-0100\",\n      \"address1\": \"123 Main St\",\n      \"city\": \"Beverly Hills\",\n      \"zip\": \"90210\",\n      \"country\": \"US\"\n    },\n    \"warehouse_id\": 2,\n    \"warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"address\": {\n        \"address1\": \"500 Industrial Way\",\n        \"city\": \"Glendale\",\n        \"zip\": \"91201\",\n        \"country\": \"United States\",\n        \"country_code\": \"US\"\n      }\n    },\n    \"notes\": \"Customer returning defective unit\",\n    \"tracking_number\": \"1Z999AA10123456784\",\n    \"carrier\": \"UPS\",\n    \"shipped_at\": \"2024-01-16\",\n    \"expected_at\": \"2024-01-20\",\n    \"approved_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"approved_by_user_id\": 1,\n    \"approved_by\": {\n      \"id\": 1,\n      \"name\": \"Admin User\"\n    },\n    \"rma_date\": \"2024-01-14\",\n    \"is_amazon_fba_return\": false,\n    \"rma_lines\": [\n      {\n        \"id\": 1,\n        \"rma_id\": 1,\n        \"product_id\": 20,\n        \"product\": {\n          \"id\": 20,\n          \"sku\": \"WIDGET-A\",\n          \"name\": \"Widget A\"\n        },\n        \"sales_order_line_id\": 55,\n        \"sales_order_line\": {\n          \"id\": 55,\n          \"quantity\": 5,\n          \"amount\": 49.99\n        },\n        \"return_reason_id\": 1,\n        \"return_reason\": {\n          \"id\": 1,\n          \"name\": \"Defective\"\n        },\n        \"expected_quantity\": 2,\n        \"received_quantity\": 0,\n        \"remaining_quantity\": 2,\n        \"notes\": \"Unit arrived damaged\",\n        \"amazon_fba_return\": null\n      }\n    ],\n    \"return_receipts\": [\n      {\n        \"id\": 5,\n        \"rma_id\": 1,\n        \"warehouse_id\": 2,\n        \"warehouse\": {\n          \"id\": 2,\n          \"name\": \"Main Warehouse\"\n        },\n        \"received_at\": \"2024-01-21T09:00:00.000000Z\",\n        \"received_by_user_id\": 1,\n        \"received_by\": {\n          \"id\": 1,\n          \"name\": \"Admin User\"\n        },\n        \"notes\": \"Received in good condition\",\n        \"return_receipt_lines\": [\n          {\n            \"id\": 8,\n            \"product_id\": 20,\n            \"product\": {\n              \"id\": 20,\n              \"sku\": \"WIDGET-A\",\n              \"name\": \"Widget A\"\n            },\n            \"quantity\": 2,\n            \"action\": \"added_to_stock\",\n            \"return_reason\": {\n              \"id\": 1,\n              \"name\": \"Defective\"\n            }\n          }\n        ],\n        \"created_at\": \"2024-01-21T09:00:00.000000Z\"\n      }\n    ],\n    \"sales_credits\": [],\n    \"created_at\": \"2024-01-14T08:00:00.000000Z\",\n    \"updated_at\": \"2024-01-16T09:00:00.000000Z\"\n  }\n}"}]},{"name":"Update RMA","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}","host":["{{baseUrl}}"],"path":["api","rmas",":rma"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]},"description":"Update an RMA's warehouse and/or notes.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nOnly warehouse_id (optional) and notes (optional, max 1000 chars) can be updated via this endpoint. Use the /approve, /shipping, etc. endpoints for status transitions.\n\n**Requires permission:** `returns.update`","body":{"mode":"raw","raw":"{\n  \"warehouse_id\": 3,\n  \"notes\": \"Updated return notes — item is scratched, not broken\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/rmas/{rma}","host":["{{baseUrl}}"],"path":["api","rmas",":rma"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_number\": \"RMA-0001\",\n    \"status\": \"draft\",\n    \"warehouse_id\": 3,\n    \"notes\": \"Updated return notes — item is scratched, not broken\",\n    \"updated_at\": \"2024-01-20T11:00:00.000000Z\"\n  },\n  \"message\": \"RMA updated successfully\"\n}"}]},{"name":"Delete RMA","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}","host":["{{baseUrl}}"],"path":["api","rmas",":rma"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]},"description":"Delete an RMA. Only draft RMAs can typically be deleted. Returns 422 if the RMA cannot be deleted.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `returns.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/rmas/{rma}","host":["{{baseUrl}}"],"path":["api","rmas",":rma"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"RMA deleted successfully\"\n}"}]},{"name":"Approve RMA","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}/approve","host":["{{baseUrl}}"],"path":["api","rmas",":rma","approve"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]},"description":"Approve a draft RMA. Records the approving user and approval timestamp.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nStatus transition: draft → approved\n\n**Requires permission:** `returns.approve`"},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/rmas/{rma}/approve","host":["{{baseUrl}}"],"path":["api","rmas",":rma","approve"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_number\": \"RMA-0001\",\n    \"status\": \"approved\",\n    \"approved_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"approved_by\": {\n      \"id\": 1,\n      \"name\": \"Admin User\"\n    }\n  },\n  \"message\": \"RMA approved successfully.\"\n}"}]},{"name":"Cancel RMA","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}/cancel","host":["{{baseUrl}}"],"path":["api","rmas",":rma","cancel"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]},"description":"Cancel an RMA and keep it on record (the RMA is not deleted). Only RMAs in a cancellable status (draft, approved, in_transit) with zero return receipts can be canceled — `can_be_canceled` on the resource reflects this.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. **Requires permission:** `returns.cancel`.\n\nStatus transition: draft / approved / in_transit → canceled\n\n**Request body (optional):**\n- `cancellation_reason` (string, nullable, max 500) — audit note recorded alongside `canceled_at` and `canceled_by_user_id`.\n\nThe response resource exposes the cancellation audit fields (`canceled_at`, `canceled_by_user_id`, `canceled_by`, `cancellation_reason`) and the capability flags (`can_be_canceled`, `can_be_deleted`).","body":{"mode":"raw","raw":"{\n  \"cancellation_reason\": \"Customer decided to keep the item\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/rmas/{rma}/cancel","host":["{{baseUrl}}"],"path":["api","rmas",":rma","cancel"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_number\": \"RMA-0001\",\n    \"status\": \"canceled\",\n    \"canceled_at\": \"2026-06-29T14:32:00.000000Z\",\n    \"canceled_by_user_id\": 7,\n    \"canceled_by\": {\n      \"id\": 7,\n      \"name\": \"Jane Operator\"\n    },\n    \"cancellation_reason\": \"Customer decided to keep the item\",\n    \"can_be_canceled\": false,\n    \"can_be_deleted\": true\n  },\n  \"message\": \"RMA canceled successfully.\"\n}"}]},{"name":"Close RMA","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}/close","host":["{{baseUrl}}"],"path":["api","rmas",":rma","close"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]},"description":"Close an approved or received RMA.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nStatus transition: approved/in_transit/received → closed\n\n**Requires permission:** `returns.update`"},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/rmas/{rma}/close","host":["{{baseUrl}}"],"path":["api","rmas",":rma","close"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_number\": \"RMA-0001\",\n    \"status\": \"closed\"\n  },\n  \"message\": \"RMA closed successfully.\"\n}"}]},{"name":"Reopen RMA","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}/reopen","host":["{{baseUrl}}"],"path":["api","rmas",":rma","reopen"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]},"description":"Reopen a closed RMA.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nStatus transition: closed → approved\n\n**Requires permission:** `returns.update`"},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/rmas/{rma}/reopen","host":["{{baseUrl}}"],"path":["api","rmas",":rma","reopen"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_number\": \"RMA-0001\",\n    \"status\": \"approved\"\n  },\n  \"message\": \"RMA reopened successfully.\"\n}"}]},{"name":"Uncancel RMA","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}/uncancel","host":["{{baseUrl}}"],"path":["api","rmas",":rma","uncancel"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]},"description":"Restore a canceled RMA back to draft status.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nStatus transition: canceled → draft\n\n**Requires permission:** `returns.cancel`"},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/rmas/{rma}/uncancel","host":["{{baseUrl}}"],"path":["api","rmas",":rma","uncancel"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_number\": \"RMA-0001\",\n    \"status\": \"draft\"\n  },\n  \"message\": \"RMA restored successfully.\"\n}"}]},{"name":"Revert to Draft","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}/revert-to-draft","host":["{{baseUrl}}"],"path":["api","rmas",":rma","revert-to-draft"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]},"description":"Revert an approved RMA back to draft status. Clears approved_at and approved_by_user_id.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nStatus transition: approved → draft\n\n**Requires permission:** `returns.update`"},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/rmas/{rma}/revert-to-draft","host":["{{baseUrl}}"],"path":["api","rmas",":rma","revert-to-draft"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_number\": \"RMA-0001\",\n    \"status\": \"draft\",\n    \"approved_at\": null,\n    \"approved_by_user_id\": null,\n    \"approved_by\": null\n  },\n  \"message\": \"RMA reverted to draft.\"\n}"}]},{"name":"Mark as Shipped","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}/ship","host":["{{baseUrl}}"],"path":["api","rmas",":rma","ship"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]},"description":"Mark an RMA as shipped by the customer.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nStatus transition: approved → in_transit\n\n**Requires permission:** `returns.update`"},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/rmas/{rma}/ship","host":["{{baseUrl}}"],"path":["api","rmas",":rma","ship"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_number\": \"RMA-0001\",\n    \"status\": \"in_transit\"\n  },\n  \"message\": \"RMA marked as shipped.\"\n}"}]},{"name":"Update Shipping Details","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}/shipping","host":["{{baseUrl}}"],"path":["api","rmas",":rma","shipping"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]},"description":"Update shipping details for an RMA. Automatically transitions status based on whether shipping info is added or removed:\n- Adding shipping info: APPROVED → IN_TRANSIT\n- Clearing all shipping info (when no receipts exist): IN_TRANSIT → APPROVED\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nAll fields are nullable — pass null to clear a field.\n\n**Requires permission:** `returns.update`","body":{"mode":"raw","raw":"{\n  \"carrier\": \"UPS\",\n  \"tracking_number\": \"1Z999AA10123456784\",\n  \"shipped_at\": \"2024-01-16\",\n  \"expected_at\": \"2024-01-20\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/rmas/{rma}/shipping","host":["{{baseUrl}}"],"path":["api","rmas",":rma","shipping"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_number\": \"RMA-0001\",\n    \"status\": \"in_transit\",\n    \"carrier\": \"UPS\",\n    \"tracking_number\": \"1Z999AA10123456784\",\n    \"shipped_at\": \"2024-01-16\",\n    \"expected_at\": \"2024-01-20\"\n  },\n  \"message\": \"Shipping details updated.\"\n}"}]},{"name":"Clear Shipping Details","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}/shipping","host":["{{baseUrl}}"],"path":["api","rmas",":rma","shipping"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]},"description":"Clear all shipping information from an RMA. If no receipts exist and the RMA is in IN_TRANSIT, it transitions back to APPROVED.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `returns.update`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/rmas/{rma}/shipping","host":["{{baseUrl}}"],"path":["api","rmas",":rma","shipping"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_number\": \"RMA-0001\",\n    \"status\": \"approved\",\n    \"carrier\": null,\n    \"tracking_number\": null,\n    \"shipped_at\": null,\n    \"expected_at\": null\n  },\n  \"message\": \"Shipping information cleared.\"\n}"}]},{"name":"Add Line to RMA","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}/lines","host":["{{baseUrl}}"],"path":["api","rmas",":rma","lines"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]},"description":"Add a new line to an existing RMA. Cannot add lines to terminal (closed/canceled) RMAs.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nValidation: Same quantity rules as creating an RMA — cannot exceed fulfilled minus already RMA'd.\n\n**Requires permission:** `returns.update`","body":{"mode":"raw","raw":"{\n  \"product_id\": 25,\n  \"expected_quantity\": 1,\n  \"sales_order_line_id\": 60,\n  \"return_reason_id\": 2,\n  \"notes\": \"Additional defective unit\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/rmas/{rma}/lines","host":["{{baseUrl}}"],"path":["api","rmas",":rma","lines"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_number\": \"RMA-0001\",\n    \"rma_lines\": [\n      {\n        \"id\": 1,\n        \"product_id\": 20,\n        \"expected_quantity\": 2\n      },\n      {\n        \"id\": 21,\n        \"product_id\": 25,\n        \"expected_quantity\": 1\n      }\n    ]\n  },\n  \"message\": \"RMA Line created successfully\"\n}"}]},{"name":"Update RMA Line","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}/lines/{rmaLine}","host":["{{baseUrl}}"],"path":["api","rmas",":rma","lines",":rmaLine"],"variable":[{"key":"rma","value":"1","description":"RMA ID"},{"key":"rmaLine","value":"1","description":"RMA Line ID"}]},"description":"Update an RMA line. Cannot update lines on closed or canceled RMAs.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nAll fields are optional (sometimes rule):\n- expected_quantity: min 1, cannot exceed fulfilled minus existing RMAs (excluding current line)\n- return_reason_id: nullable\n- notes: nullable, max 500 chars\n\n**Requires permission:** `returns.update`","body":{"mode":"raw","raw":"{\n  \"expected_quantity\": 1,\n  \"return_reason_id\": 3,\n  \"notes\": \"Customer changed mind — only 1 unit is defective\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/rmas/{rma}/lines/{rmaLine}","host":["{{baseUrl}}"],"path":["api","rmas",":rma","lines",":rmaLine"],"variable":[{"key":"rma","value":"1","description":"RMA ID"},{"key":"rmaLine","value":"1","description":"RMA Line ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_number\": \"RMA-0001\",\n    \"rma_lines\": [\n      {\n        \"id\": 1,\n        \"product_id\": 20,\n        \"expected_quantity\": 1,\n        \"return_reason_id\": 3,\n        \"notes\": \"Customer changed mind — only 1 unit is defective\"\n      }\n    ]\n  },\n  \"message\": \"RMA Line updated successfully\"\n}"}]},{"name":"Remove Line from RMA","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}/lines/{rmaLine}","host":["{{baseUrl}}"],"path":["api","rmas",":rma","lines",":rmaLine"],"variable":[{"key":"rma","value":"1","description":"RMA ID"},{"key":"rmaLine","value":"1","description":"RMA Line ID"}]},"description":"Remove a line from an RMA. Returns 422 if the RMA is in a terminal state or if the line doesn't belong to the RMA.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `returns.update`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/rmas/{rma}/lines/{rmaLine}","host":["{{baseUrl}}"],"path":["api","rmas",":rma","lines",":rmaLine"],"variable":[{"key":"rma","value":"1","description":"RMA ID"},{"key":"rmaLine","value":"1","description":"RMA Line ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_number\": \"RMA-0001\",\n    \"rma_lines\": []\n  },\n  \"message\": \"RMA Line deleted successfully\"\n}"}]},{"name":"Create Return Receipt from RMA","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}/receipts","host":["{{baseUrl}}"],"path":["api","rmas",":rma","receipts"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]},"description":"Record a return receipt against an RMA (items physically received at the warehouse).\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nAction values: added_to_stock, discarded, new_blemished_sku\n- For new_blemished_sku: provide blemished_configurations array with sku, condition, and reference fields\n- product_id: the product being received back (can differ from original if substituted)\n- original_product_id: the original product (if product_id differs)\n\nLot / batch capture (optional per line - only meaningful for added_to_stock on lot-tracked products):\n- lots[]: { batch_number, manufacture_date, expiry_date, quantity, lot_notes }. Each lot becomes its own FIFO layer; the lot quantities must sum to the line quantity.\n- Batch number + expiry date are REQUIRED on each lot only when the return is traceable to its original sale (the RMA is linked to a sales order). For a blind/untraceable RMA (no sales order link), a blank batch is auto-generated (AUTO-...) at persistence and an undated lot is valid - the receiver is never forced to invent a lot id.\n\n**Requires permission:** `returns.receive` rma_line_id is OPTIONAL — omit it (supply product_id) to receive an item NOT on the RMA; it is stored and flagged unexpected_item=true rather than dropped.\n\n**Serials.** Serials can be sent on every received quantity type (new, blemished or discarded). A serial SKU.io never recorded is added with the chosen disposition under Log and allow mismatch, or refused with 422 under Require exact match. Deleting the receipt puts each serial back the way it was before the return.","body":{"mode":"raw","raw":"{\n  \"received_at\": \"2024-01-21\",\n  \"notes\": \"Received in good condition\",\n  \"lines\": [\n    {\n      \"rma_line_id\": 1,\n      \"quantity\": 5,\n      \"action\": \"added_to_stock\",\n      \"product_id\": 20,\n      \"original_product_id\": null,\n      \"return_reason_id\": 1,\n      \"notes\": \"Units tested and in working order\",\n      \"blemished_configurations\": null,\n      \"lots\": [\n        {\n          \"batch_number\": \"LOT-2026-A\",\n          \"manufacture_date\": \"2026-01-01\",\n          \"expiry_date\": \"2030-01-31\",\n          \"quantity\": 5,\n          \"lot_notes\": \"Original shipped lot\"\n        }\n      ],\n      \"unexpected_item\": false\n    },\n    {\n      \"rma_line_id\": null,\n      \"product_id\": 44,\n      \"quantity\": 1,\n      \"action\": \"added_to_stock\",\n      \"unexpected_item\": true,\n      \"notes\": \"Customer returned a different item than declared\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/rmas/{rma}/receipts","host":["{{baseUrl}}"],"path":["api","rmas",":rma","receipts"],"variable":[{"key":"rma","value":"1","description":"RMA ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"rma_id\": 1,\n    \"rma\": {\n      \"id\": 1,\n      \"rma_number\": \"RMA-0001\",\n      \"status\": \"received\"\n    },\n    \"warehouse_id\": 2,\n    \"warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\"\n    },\n    \"received_at\": \"2024-01-21T00:00:00.000000Z\",\n    \"received_by_user_id\": 1,\n    \"received_by\": {\n      \"id\": 1,\n      \"name\": \"Admin User\"\n    },\n    \"notes\": \"Received in good condition\",\n    \"return_receipt_lines\": [\n      {\n        \"id\": 8,\n        \"product_id\": 20,\n        \"product\": {\n          \"id\": 20,\n          \"sku\": \"WIDGET-A\",\n          \"name\": \"Widget A\"\n        },\n        \"quantity\": 2,\n        \"action\": \"return_to_stock\"\n      }\n    ],\n    \"created_at\": \"2024-01-21T09:00:00.000000Z\"\n  },\n  \"message\": \"Return Receipt created successfully\"\n}"}]},{"name":"Bulk Approve RMAs","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/rmas/bulk/approve","host":["{{baseUrl}}"],"path":["api","rmas","bulk","approve"]},"description":"Bulk approve multiple RMAs. Returns success and failed counts.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nUse apply_to_all=true with filters to operate on all matching RMAs.\n\n**Requires permission:** `returns.approve`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/rmas/bulk/approve","host":["{{baseUrl}}"],"path":["api","rmas","bulk","approve"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"success\": [\n      1,\n      2,\n      3\n    ],\n    \"failed\": {}\n  },\n  \"message\": \"3 RMA(s) approved successfully.\"\n}"}]},{"name":"Bulk Cancel RMAs","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/rmas/bulk/cancel","host":["{{baseUrl}}"],"path":["api","rmas","bulk","cancel"]},"description":"Bulk cancel multiple RMAs.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `returns.cancel`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    4,\n    5\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/rmas/bulk/cancel","host":["{{baseUrl}}"],"path":["api","rmas","bulk","cancel"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"success\": [\n      4,\n      5\n    ],\n    \"failed\": {}\n  },\n  \"message\": \"2 RMA(s) canceled successfully.\"\n}"}]},{"name":"Bulk Close RMAs","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/rmas/bulk/close","host":["{{baseUrl}}"],"path":["api","rmas","bulk","close"]},"description":"Bulk close multiple RMAs.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `returns.update`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    6,\n    7\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/rmas/bulk/close","host":["{{baseUrl}}"],"path":["api","rmas","bulk","close"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"success\": [\n      6,\n      7\n    ],\n    \"failed\": {}\n  },\n  \"message\": \"2 RMA(s) closed successfully.\"\n}"}]},{"name":"Bulk Delete RMAs","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/rmas/bulk/delete","host":["{{baseUrl}}"],"path":["api","rmas","bulk","delete"]},"description":"Bulk delete multiple RMAs.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `returns.delete`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    8,\n    9\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/rmas/bulk/delete","host":["{{baseUrl}}"],"path":["api","rmas","bulk","delete"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"success\": [\n      8,\n      9\n    ],\n    \"failed\": {}\n  },\n  \"message\": \"2 RMA(s) deleted successfully.\"\n}"}]},{"name":"Bulk Revert RMAs to Draft","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/rmas/bulk/revert-to-draft","host":["{{baseUrl}}"],"path":["api","rmas","bulk","revert-to-draft"]},"description":"Bulk revert approved RMAs back to draft status.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `returns.update`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    10,\n    11\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/rmas/bulk/revert-to-draft","host":["{{baseUrl}}"],"path":["api","rmas","bulk","revert-to-draft"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"success\": [\n      10,\n      11\n    ],\n    \"failed\": {}\n  },\n  \"message\": \"2 RMA(s) reverted to draft successfully.\"\n}"}]},{"name":"Bulk Generate RMAs from Orders","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/rmas/bulk/generate-from-orders","host":["{{baseUrl}}"],"path":["api","rmas","bulk","generate-from-orders"]},"description":"Dispatch a tracked background job that creates one RMA per selected (or all-matching) sales order, from each order's fulfilled, not-already-RMA'd lines. Returns a tracked_job_log_id immediately; progress shows via the tracked job log endpoints.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- sales_order_ids (required without apply_to_all): explicit list of sales order IDs\n- apply_to_all (optional bool): when true, resolve target orders from the sales-orders list filters instead of an explicit list\n- filters (optional): the sales-orders list filters to resolve when apply_to_all is true\n- warehouse_id (optional): default warehouse for generated RMAs; per order, the order's fulfillment warehouse is preferred when resolvable\n- status (optional): draft | approved (default draft) — when approved, each generated RMA is approved\n- rma_date (optional date): date for the generated RMAs\n- skip_existing_open (optional bool, default true): skip orders that already have a non-terminal (open) RMA\n\nPer-order outcomes (created / skipped / failed) and the created RMA list are stored on the tracked job log for the details dialog. The completion watcher matches the job name \"Generate RMAs from Orders\".","body":{"mode":"raw","raw":"{\n  \"sales_order_ids\": [\n    42,\n    43,\n    44\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {},\n  \"warehouse_id\": 1,\n  \"status\": \"draft\",\n  \"rma_date\": \"2026-06-09\",\n  \"skip_existing_open\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/rmas/bulk/generate-from-orders","host":["{{baseUrl}}"],"path":["api","rmas","bulk","generate-from-orders"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 1234\n  },\n  \"message\": \"Generating RMAs from orders — track progress in the job tray.\"\n}"}]},{"name":"Get RMA Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}/activity-log","host":["{{baseUrl}}"],"path":["api","rmas",":rma","activity-log"],"variable":[{"key":"rma","value":"512","description":"RMA ID"}]},"description":"Get the activity log for an RMA — every recorded change on the RMA itself, its lines, and its return receipts, newest first.\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach entry includes the event, the record type it applies to (`Rma`, `RmaLine`, or `ReturnReceipt`), the changed attributes with old values, and the acting user (`causer_name` is null for system-generated changes).\n\nStandard paginated response. Requires the `returns:read` token scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/rmas/{rma}/activity-log","host":["{{baseUrl}}"],"path":["api","rmas",":rma","activity-log"],"variable":[{"key":"rma","value":"512","description":"RMA ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 48213,\n      \"description\": \"updated\",\n      \"event\": \"updated\",\n      \"subject_type\": \"Rma\",\n      \"subject_id\": 512,\n      \"properties\": {\n        \"attributes\": {\n          \"status\": \"approved\"\n        },\n        \"old\": {\n          \"status\": \"draft\"\n        },\n        \"source\": \"manual\"\n      },\n      \"causer_name\": \"Jane Smith\",\n      \"created_at\": \"2026-07-01T14:22:05.000000Z\"\n    },\n    {\n      \"id\": 48120,\n      \"description\": \"created\",\n      \"event\": \"created\",\n      \"subject_type\": \"ReturnReceipt\",\n      \"subject_id\": 88,\n      \"properties\": {\n        \"attributes\": {\n          \"warehouse_id\": 3\n        }\n      },\n      \"causer_name\": \"Jane Smith\",\n      \"created_at\": \"2026-07-01T13:10:41.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"https://acme.sku.io/api/rmas/512/activity-log?page=1\",\n  \"from\": 1,\n  \"last_page\": 3,\n  \"last_page_url\": \"https://acme.sku.io/api/rmas/512/activity-log?page=3\",\n  \"links\": [],\n  \"next_page_url\": \"https://acme.sku.io/api/rmas/512/activity-log?page=2\",\n  \"path\": \"https://acme.sku.io/api/rmas/512/activity-log\",\n  \"per_page\": 15,\n  \"prev_page_url\": null,\n  \"to\": 15,\n  \"total\": 34\n}"}]},{"name":"List RMA Inventory Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}/inventory-movements","host":["{{baseUrl}}"],"path":["api","rmas",":rma","inventory-movements"],"variable":[{"key":"rma","value":"512","description":"RMA ID"}]},"description":"List the inventory movements created by an RMA's return receipts — the stock re-entering inventory as returned units are received.\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach movement carries the product, warehouse, quantity, and unit cost. When the movement is tied to a cost layer, `unit_cost` comes from that layer and `unit_cost_provisional` is false; movements not yet tied to a layer use the product's current unit cost with `unit_cost_provisional: true`.\n\nStandard paginated response (default 15 per page, sorted by movement date descending). Requires the `returns:read` token scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/rmas/{rma}/inventory-movements","host":["{{baseUrl}}"],"path":["api","rmas",":rma","inventory-movements"],"variable":[{"key":"rma","value":"512","description":"RMA ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 77120,\n      \"type\": \"return\",\n      \"inventory_status\": \"active\",\n      \"quantity\": 2,\n      \"reference\": \"RMA-00042: Receipt 88\",\n      \"link_type\": \"App\\\\Models\\\\ReturnReceiptLine\",\n      \"link_id\": 1440,\n      \"parent_link_id\": 88,\n      \"detail_link_id\": null,\n      \"product_id\": 341,\n      \"product_sku\": \"WIDGET-BLK\",\n      \"product_name\": \"Widget - Black\",\n      \"warehouse_id\": 3,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"unit_cost\": 12.5,\n      \"unit_cost_provisional\": false,\n      \"layer\": {\n        \"name\": \"fifo\",\n        \"fifo_layer_id\": 9021\n      },\n      \"inventory_movement_date\": \"2026-06-28T16:40:12.000000Z\",\n      \"created_at\": \"2026-06-28T16:40:12.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"https://acme.sku.io/api/rmas/512/inventory-movements?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://acme.sku.io/api/rmas/512/inventory-movements?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://acme.sku.io/api/rmas/512/inventory-movements\",\n  \"per_page\": 15,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"List RMA FIFO Layers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/rmas/{rma}/fifo-layers","host":["{{baseUrl}}"],"path":["api","rmas",":rma","fifo-layers"],"variable":[{"key":"rma","value":"512","description":"RMA ID"}]},"description":"List the inventory cost layers created by receiving this RMA's returned stock. Each layer records the received quantity, its unit cost, remaining availability, and lot details (batch number, expiry) when the product is lot-tracked.\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`origin` identifies the return receipt line the layer came from; `available_quantity` is `original_quantity - fulfilled_quantity`; `days_to_expiry` is a signed day count (negative when expired, null when no expiry).\n\nStandard paginated response, sorted newest first. Requires the `returns:read` token scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/rmas/{rma}/fifo-layers","host":["{{baseUrl}}"],"path":["api","rmas",":rma","fifo-layers"],"variable":[{"key":"rma","value":"512","description":"RMA ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 9021,\n      \"product_id\": 341,\n      \"fifo_layer_date\": \"2026-06-28T16:40:12.000000Z\",\n      \"sku\": \"WIDGET-BLK\",\n      \"product_name\": \"Widget - Black\",\n      \"product_barcode\": 812345678901,\n      \"stock_uom_code\": \"EA\",\n      \"batch_number\": null,\n      \"manufacture_date\": null,\n      \"expiry_date\": null,\n      \"days_to_expiry\": null,\n      \"lot_notes\": null,\n      \"original_quantity\": 2,\n      \"fulfilled_quantity\": 0,\n      \"avg_cost\": \"12.5000\",\n      \"total_cost\": \"25.0000\",\n      \"cost_breakdown\": null,\n      \"origin\": {\n        \"link_type\": \"App\\\\Models\\\\ReturnReceiptLine\",\n        \"link_id\": 1440,\n        \"name\": \"return_receipt\",\n        \"parent_link_id\": 88,\n        \"detail_link_id\": null,\n        \"reference\": \"RMA-00042: Receipt 88\",\n        \"parent_reference\": \"RMA-00042: Receipt 88\"\n      },\n      \"warehouse\": {\n        \"id\": 3,\n        \"name\": \"Main Warehouse\"\n      },\n      \"is_fba\": false,\n      \"condition\": \"sellable\",\n      \"integration_unique_id\": null,\n      \"allocated_quantity\": 0,\n      \"available_quantity\": 2,\n      \"archived_at\": null,\n      \"created_at\": \"2026-06-28T16:40:12.000000Z\",\n      \"updated_at\": \"2026-06-28T16:40:12.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"https://acme.sku.io/api/rmas/512/fifo-layers?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://acme.sku.io/api/rmas/512/fifo-layers?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://acme.sku.io/api/rmas/512/fifo-layers\",\n  \"per_page\": 15,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"List Return Receipts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/return-receipts","host":["{{baseUrl}}"],"path":["api","return-receipts"]},"description":"List all return receipts with pagination. Supports filtering by RMA, warehouse, blind receipts (no RMA), and search by ID or RMA number.\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/return-receipts","host":["{{baseUrl}}"],"path":["api","return-receipts"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"rma_id\": 5,\n      \"rma\": {\n        \"id\": 5,\n        \"rma_number\": \"RMA-2024-0001\",\n        \"status\": \"received\"\n      },\n      \"warehouse_id\": 1,\n      \"warehouse\": {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      },\n      \"received_at\": \"2024-01-15T10:30:00.000000Z\",\n      \"received_by_user_id\": 2,\n      \"received_by\": {\n        \"id\": 2,\n        \"name\": \"Jane Smith\"\n      },\n      \"notes\": \"All items received in good condition.\",\n      \"return_receipt_lines\": [\n        {\n          \"id\": 1,\n          \"return_receipt_id\": 1,\n          \"rma_line_id\": 3,\n          \"product_id\": 10,\n          \"product\": {\n            \"id\": 10,\n            \"sku\": \"PROD-001\",\n            \"name\": \"Widget A\"\n          },\n          \"original_product_id\": null,\n          \"quantity\": 2,\n          \"action\": \"return_to_stock\",\n          \"return_reason_id\": 1,\n          \"notes\": null,\n          \"created_at\": \"2024-01-15T10:30:00.000000Z\",\n          \"updated_at\": \"2024-01-15T10:30:00.000000Z\"\n        }\n      ],\n      \"created_at\": \"2024-01-15T10:30:00.000000Z\",\n      \"updated_at\": \"2024-01-15T10:30:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 10,\n  \"total\": 25,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"Create Return Receipt","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/return-receipts","host":["{{baseUrl}}"],"path":["api","return-receipts"]},"description":"Create a new return receipt (blind or RMA-linked). Adjusts inventory based on each line's action.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- warehouse_id (required): Warehouse where items are being received\n- rma_id (optional): Link to an RMA; omit for blind receipts\n- received_at (optional): ISO 8601 datetime; defaults to now\n- notes (optional): General notes for the receipt\n- return_receipt_lines (required): Array of line items\n  - product_id (required): Product being returned\n  - original_product_id (optional): If different from product_id (e.g., substituted product)\n  - rma_line_id (optional): Link to specific RMA line\n  - quantity (required): Quantity received (min: 1)\n  - action (required): One of: return_to_stock, dispose, refurbish\n  - return_reason_id (optional): ID of return reason\n  - notes (optional): Line-level notes (max 500 chars)\n\n**Requires permission:** `returns.receive`\n\n**Serials.** Same serial rules as receiving from an RMA: unrecorded serials are added (Log and allow mismatch) or refused with 422 (Require exact match).","body":{"mode":"raw","raw":"{\n  \"warehouse_id\": 1,\n  \"rma_id\": 5,\n  \"received_at\": \"2024-01-15T10:30:00\",\n  \"notes\": \"All items received in good condition.\",\n  \"return_receipt_lines\": [\n    {\n      \"product_id\": 10,\n      \"original_product_id\": null,\n      \"rma_line_id\": 3,\n      \"quantity\": 2,\n      \"action\": \"return_to_stock\",\n      \"return_reason_id\": 1,\n      \"notes\": \"Minor cosmetic damage.\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/return-receipts","host":["{{baseUrl}}"],"path":["api","return-receipts"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"rma_id\": 5,\n    \"rma\": {\n      \"id\": 5,\n      \"rma_number\": \"RMA-2024-0001\",\n      \"status\": \"received\"\n    },\n    \"warehouse_id\": 1,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"received_at\": \"2024-01-15T10:30:00.000000Z\",\n    \"received_by_user_id\": 2,\n    \"received_by\": {\n      \"id\": 2,\n      \"name\": \"Jane Smith\"\n    },\n    \"notes\": \"All items received in good condition.\",\n    \"return_receipt_lines\": [\n      {\n        \"id\": 15,\n        \"return_receipt_id\": 12,\n        \"rma_line_id\": 3,\n        \"product_id\": 10,\n        \"product\": {\n          \"id\": 10,\n          \"sku\": \"PROD-001\",\n          \"name\": \"Widget A\"\n        },\n        \"quantity\": 2,\n        \"action\": \"return_to_stock\",\n        \"return_reason_id\": 1,\n        \"created_at\": \"2024-01-15T10:30:00.000000Z\",\n        \"updated_at\": \"2024-01-15T10:30:00.000000Z\"\n      }\n    ],\n    \"created_at\": \"2024-01-15T10:30:00.000000Z\",\n    \"updated_at\": \"2024-01-15T10:30:00.000000Z\"\n  },\n  \"message\": \"Return Receipt created successfully.\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Return Receipt","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/return-receipts/{return_receipt}","host":["{{baseUrl}}"],"path":["api","return-receipts",":return_receipt"],"variable":[{"key":"return_receipt","value":"1","description":"Return receipt ID"}]},"description":"Get a single return receipt with full detail, including RMA and sales order, warehouse, received-by user, and all receipt lines with products, original products, RMA lines, and return reasons.\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/return-receipts/{return_receipt}","host":["{{baseUrl}}"],"path":["api","return-receipts",":return_receipt"],"variable":[{"key":"return_receipt","value":"1","description":"Return receipt ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_id\": 5,\n    \"rma\": {\n      \"id\": 5,\n      \"rma_number\": \"RMA-2024-0001\",\n      \"status\": \"received\"\n    },\n    \"warehouse_id\": 1,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"received_at\": \"2024-01-15T10:30:00.000000Z\",\n    \"received_by_user_id\": 2,\n    \"received_by\": {\n      \"id\": 2,\n      \"name\": \"Jane Smith\"\n    },\n    \"notes\": \"All items received in good condition.\",\n    \"return_receipt_lines\": [\n      {\n        \"id\": 1,\n        \"return_receipt_id\": 1,\n        \"rma_line_id\": 3,\n        \"rma_line\": {\n          \"id\": 3,\n          \"expected_quantity\": 2,\n          \"product_id\": 10\n        },\n        \"product_id\": 10,\n        \"product\": {\n          \"id\": 10,\n          \"sku\": \"PROD-001\",\n          \"name\": \"Widget A\"\n        },\n        \"original_product_id\": null,\n        \"original_product\": null,\n        \"quantity\": 2,\n        \"action\": \"return_to_stock\",\n        \"return_reason_id\": 1,\n        \"return_reason\": {\n          \"id\": 1,\n          \"name\": \"Defective\"\n        },\n        \"notes\": \"Minor cosmetic damage.\",\n        \"fifo_layer_id\": 42,\n        \"created_at\": \"2024-01-15T10:30:00.000000Z\",\n        \"updated_at\": \"2024-01-15T10:30:00.000000Z\"\n      }\n    ],\n    \"created_at\": \"2024-01-15T10:30:00.000000Z\",\n    \"updated_at\": \"2024-01-15T10:30:00.000000Z\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Delete Return Receipt","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/return-receipts/{return_receipt}","host":["{{baseUrl}}"],"path":["api","return-receipts",":return_receipt"],"variable":[{"key":"return_receipt","value":"1","description":"Return receipt ID"}]},"description":"Delete a return receipt and all its lines. Reverses inventory adjustments.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `returns.receive`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/return-receipts/{return_receipt}","host":["{{baseUrl}}"],"path":["api","return-receipts",":return_receipt"],"variable":[{"key":"return_receipt","value":"1","description":"Return receipt ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Return Receipt deleted successfully.\",\n  \"status\": \"success\"\n}"}]},{"name":"Add Line to Return Receipt","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/return-receipts/{returnReceipt}/lines","host":["{{baseUrl}}"],"path":["api","return-receipts",":returnReceipt","lines"],"variable":[{"key":"returnReceipt","value":"1","description":"Return receipt ID"}]},"description":"Add a new line to an existing return receipt. Creates an inventory movement for the specified action.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- product_id (required): Product being returned\n- original_product_id (optional): Original product if different (e.g., substituted)\n- rma_line_id (optional): Link to specific RMA line\n- quantity (required): Quantity received (min: 1)\n- action (required): One of: return_to_stock, dispose, refurbish\n- return_reason_id (optional): ID of return reason\n- notes (optional): Notes (max 500 chars)\n\n**Requires permission:** `returns.receive`","body":{"mode":"raw","raw":"{\n  \"product_id\": 10,\n  \"original_product_id\": null,\n  \"rma_line_id\": 3,\n  \"quantity\": 1,\n  \"action\": \"dispose\",\n  \"return_reason_id\": 2,\n  \"notes\": \"Item is not suitable for resale.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/return-receipts/{returnReceipt}/lines","host":["{{baseUrl}}"],"path":["api","return-receipts",":returnReceipt","lines"],"variable":[{"key":"returnReceipt","value":"1","description":"Return receipt ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_id\": 5,\n    \"warehouse_id\": 1,\n    \"return_receipt_lines\": [\n      {\n        \"id\": 15,\n        \"product_id\": 10,\n        \"quantity\": 2,\n        \"action\": \"return_to_stock\"\n      },\n      {\n        \"id\": 16,\n        \"product_id\": 10,\n        \"quantity\": 1,\n        \"action\": \"dispose\"\n      }\n    ],\n    \"created_at\": \"2024-01-15T10:30:00.000000Z\"\n  },\n  \"message\": \"Return Receipt Line created successfully.\",\n  \"status\": \"success\"\n}"}]},{"name":"Update Return Receipt Line","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/return-receipts/{returnReceipt}/lines/{returnReceiptLine}","host":["{{baseUrl}}"],"path":["api","return-receipts",":returnReceipt","lines",":returnReceiptLine"],"variable":[{"key":"returnReceipt","value":"1","description":"Return receipt ID"},{"key":"returnReceiptLine","value":"15","description":"Return receipt line ID"}]},"description":"Update non-inventory-impacting fields on a return receipt line. Only notes and return_reason_id can be changed after creation. Fields that affect inventory (quantity, action, product_id) cannot be updated — delete and recreate the line to correct those.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `returns.receive`","body":{"mode":"raw","raw":"{\n  \"return_reason_id\": 2,\n  \"notes\": \"Updated note: item was cosmetically damaged.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/return-receipts/{returnReceipt}/lines/{returnReceiptLine}","host":["{{baseUrl}}"],"path":["api","return-receipts",":returnReceipt","lines",":returnReceiptLine"],"variable":[{"key":"returnReceipt","value":"1","description":"Return receipt ID"},{"key":"returnReceiptLine","value":"15","description":"Return receipt line ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"rma_id\": 5,\n    \"return_receipt_lines\": [\n      {\n        \"id\": 15,\n        \"product_id\": 10,\n        \"quantity\": 2,\n        \"action\": \"return_to_stock\",\n        \"return_reason_id\": 2,\n        \"notes\": \"Updated note: item was cosmetically damaged.\"\n      }\n    ]\n  },\n  \"message\": \"Return Receipt Line updated successfully.\",\n  \"status\": \"success\"\n}"}]},{"name":"Remove Line from Return Receipt","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/return-receipts/{returnReceipt}/lines/{returnReceiptLine}","host":["{{baseUrl}}"],"path":["api","return-receipts",":returnReceipt","lines",":returnReceiptLine"],"variable":[{"key":"returnReceipt","value":"1","description":"Return receipt ID"},{"key":"returnReceiptLine","value":"15","description":"Return receipt line ID"}]},"description":"Remove a specific line from a return receipt. Reverses the inventory adjustment for that line. The line must belong to the specified return receipt.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `returns.receive`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/return-receipts/{returnReceipt}/lines/{returnReceiptLine}","host":["{{baseUrl}}"],"path":["api","return-receipts",":returnReceipt","lines",":returnReceiptLine"],"variable":[{"key":"returnReceipt","value":"1","description":"Return receipt ID"},{"key":"returnReceiptLine","value":"15","description":"Return receipt line ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"return_receipt_lines\": []\n  },\n  \"message\": \"Return Receipt Line deleted successfully.\",\n  \"status\": \"success\"\n}"}]},{"name":"Delete Return Receipt Line (standalone)","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/return-receipts/lines/{returnReceiptLine}","host":["{{baseUrl}}"],"path":["api","return-receipts","lines",":returnReceiptLine"],"variable":[{"key":"returnReceiptLine","value":"15","description":"Return receipt line ID"}]},"description":"Delete a return receipt line directly by its ID (without specifying the parent receipt). If this is the last line on the return receipt, the entire return receipt is deleted automatically.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `returns.receive`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/return-receipts/lines/{returnReceiptLine}","host":["{{baseUrl}}"],"path":["api","return-receipts","lines",":returnReceiptLine"],"variable":[{"key":"returnReceiptLine","value":"15","description":"Return receipt line ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"return receipt line deleted successfully.\",\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Set Disposition on Lines","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/return-receipts/{returnReceipt}/lines/bulk-disposition","host":["{{baseUrl}}"],"path":["api","return-receipts",":returnReceipt","lines","bulk-disposition"],"variable":[{"key":"returnReceipt","value":"5","description":"Return Receipt ID"}]},"description":"Set the disposition (action + optional condition grade) on multiple lines of a return receipt at once. When a line's action changes, its prior inventory effect is reversed and the new action is re-applied so inventory stays consistent.\n\nAuthentication: Requires Bearer token.\n\nFields:\n- line_ids (array of integers, required, min 1) — return receipt line IDs to update; each must exist\n- action (string, required) — disposition to apply. Bulk disposition only supports: added_to_stock, discarded. (Use the per-line blemished SKU flow for new_blemished_sku.)\n- condition_grade (string, optional, nullable) — condition grade to set on the lines. One of: A, B, C, unsellable\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Requires permission:** `returns.receive`","body":{"mode":"raw","raw":"{\n  \"line_ids\": [\n    8,\n    9\n  ],\n  \"action\": \"added_to_stock\",\n  \"condition_grade\": \"A\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/return-receipts/{returnReceipt}/lines/bulk-disposition","host":["{{baseUrl}}"],"path":["api","return-receipts",":returnReceipt","lines","bulk-disposition"],"variable":[{"key":"returnReceipt","value":"5","description":"Return Receipt ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"rma_id\": 1,\n    \"rma\": {\n      \"id\": 1,\n      \"rma_number\": \"RMA-0001\",\n      \"status\": \"received\"\n    },\n    \"warehouse_id\": 2,\n    \"warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\"\n    },\n    \"received_at\": \"2026-06-09T00:00:00.000000Z\",\n    \"received_by_user_id\": 1,\n    \"received_by\": {\n      \"id\": 1,\n      \"name\": \"Dev User\"\n    },\n    \"notes\": null,\n    \"return_receipt_lines\": [\n      {\n        \"id\": 8,\n        \"product_id\": 20,\n        \"product\": {\n          \"id\": 20,\n          \"sku\": \"WIDGET-A\",\n          \"name\": \"Widget A\"\n        },\n        \"quantity\": 2,\n        \"action\": \"added_to_stock\",\n        \"condition_grade\": \"A\",\n        \"condition_grade_label\": \"Grade A — Like New\",\n        \"disposition_policy_id\": null,\n        \"return_reason_id\": 1\n      },\n      {\n        \"id\": 9,\n        \"product_id\": 21,\n        \"product\": {\n          \"id\": 21,\n          \"sku\": \"WIDGET-B\",\n          \"name\": \"Widget B\"\n        },\n        \"quantity\": 1,\n        \"action\": \"added_to_stock\",\n        \"condition_grade\": \"A\",\n        \"condition_grade_label\": \"Grade A — Like New\",\n        \"disposition_policy_id\": null,\n        \"return_reason_id\": 1\n      }\n    ],\n    \"created_at\": \"2026-06-09T09:00:00.000000Z\",\n    \"updated_at\": \"2026-06-09T09:30:00.000000Z\"\n  },\n  \"message\": \"Disposition applied to 2 line(s).\"\n}"}]},{"name":"Resolve Scan","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/return-receipts/scan-resolve","host":["{{baseUrl}}"],"path":["api","return-receipts","scan-resolve"]},"description":"Resolve a scanned token for the Receive Returns Station. Resolution order: open RMA by number → open RMA by tracking number → sales order by number → product by SKU/barcode. Returns the match type plus staged lines whose dispositions are defaulted by the active return disposition policies (the matching policy id is included so the UI can show a 'policy applied' badge). Closed/canceled RMAs are excluded and fall through to 'none'.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- token (required): The raw scanned value (RMA #, tracking #, order #, SKU, or barcode)\n- warehouse_id (required): Warehouse the operator is receiving into (used for warehouse-scoped policy matching)\n\nWhen the token matches an RMA or a sales order, the response includes `sales_order_notes` — the notes on the sales order being returned (pinned notes first, then newest first), each with `id`, `note`, `user_name`, `created_at`, and `is_pinned`. An RMA match also includes the linked `sales_order_id`, `sales_order_number`, and the RMA's own free-text `notes`.","body":{"mode":"raw","raw":"{\n  \"token\": \"RMA-2026-00012\",\n  \"warehouse_id\": 1\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/return-receipts/scan-resolve","host":["{{baseUrl}}"],"path":["api","return-receipts","scan-resolve"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"match_type\": \"product\",\n    \"rma\": null,\n    \"order\": null,\n    \"staged_lines\": [\n      {\n        \"product_id\": 10,\n        \"sku\": \"WIDGET-BLK\",\n        \"name\": \"Widget - Black\",\n        \"rma_line_id\": null,\n        \"suggested_quantity\": 1,\n        \"suggested_action\": \"added_to_stock\",\n        \"policy_id\": 4,\n        \"policy_name\": \"Main Warehouse Grade A → Restock\",\n        \"return_reason_id\": null\n      }\n    ],\n    \"sales_order_notes\": []\n  }\n}"}]},{"name":"Resolve Line Disposition","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/return-receipts/resolve-line-disposition","host":["{{baseUrl}}"],"path":["api","return-receipts","resolve-line-disposition"]},"description":"Re-resolve the policy-defaulted disposition for a single staged line once more criteria are known (e.g. after the operator selects a condition grade), so grade-specific disposition policies apply at the Receive Station.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- product_id (required)\n- warehouse_id (required)\n- return_reason_id (optional)\n- condition_grade (optional): A|B|C|unsellable","body":{"mode":"raw","raw":"{\n  \"product_id\": 10,\n  \"warehouse_id\": 1,\n  \"return_reason_id\": null,\n  \"condition_grade\": \"C\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/return-receipts/resolve-line-disposition","host":["{{baseUrl}}"],"path":["api","return-receipts","resolve-line-disposition"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"action\": \"new_blemished_sku\",\n    \"policy_id\": 2,\n    \"policy_name\": \"Grade C electronics → Open Box SKU\"\n  }\n}"}]},{"name":"Get Session Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/return-receipts/session-summary","host":["{{baseUrl}}"],"path":["api","return-receipts","session-summary"]},"description":"Aggregate the return receipts the current authenticated user has committed since a timestamp, powering the station's running 'this session' totals (receipts, units, and units by disposition).\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nQuery parameters:\n- since (optional): ISO 8601 datetime; defaults to the start of today"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/return-receipts/session-summary","host":["{{baseUrl}}"],"path":["api","return-receipts","session-summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"receipt_count\": 7,\n    \"unit_count\": 23,\n    \"restocked_units\": 18,\n    \"discarded_units\": 3,\n    \"blemished_units\": 2\n  }\n}"}]},{"name":"Preview Return Receipt Delete Impact","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/return-receipts/{returnReceipt}/delete-impact","host":["{{baseUrl}}"],"path":["api","return-receipts",":returnReceipt","delete-impact"],"variable":[{"key":"returnReceipt","value":"1","description":"Return receipt ID"}]},"description":"Read-only preview of the allocation-demotion impact of deleting this return receipt. Because deleting it removes the stock it added, this behaves like a manual negative adjustment and returns one row per affected product: how much of the removed quantity is unallocated vs soft-allocated vs hard-allocated, plus every sales-order allocation and fulfillment (printed shipping label) the removal would disturb. Use it to decide which fulfillments to authorize voiding via `void_confirmations` on the deleting call. Nothing is mutated. A removal that only re-adds stock returns an empty `data` array.\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/return-receipts/{returnReceipt}/delete-impact","host":["{{baseUrl}}"],"path":["api","return-receipts",":returnReceipt","delete-impact"],"variable":[{"key":"returnReceipt","value":"1","description":"Return receipt ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 5567,\n      \"product_sku\": \"WIDGET-BLUE\",\n      \"product_name\": \"Blue Widget\",\n      \"shortfall_quantity\": 0,\n      \"unallocated_quantity\": 10,\n      \"soft_allocated_quantity\": 6,\n      \"hard_allocated_quantity\": 4,\n      \"affected_allocations\": [\n        {\n          \"allocation_id\": 88201,\n          \"allocation_type\": \"soft\",\n          \"sales_order_id\": 3120,\n          \"order_number\": \"SO-3120\",\n          \"customer_name\": \"Acme Co.\",\n          \"fulfillment_id\": null,\n          \"carrier\": null,\n          \"carrier_void_supported\": null,\n          \"voidable_status\": null,\n          \"voidable_detail\": null,\n          \"quantity\": 6\n        },\n        {\n          \"allocation_id\": 88205,\n          \"allocation_type\": \"hard\",\n          \"sales_order_id\": 3125,\n          \"order_number\": \"SO-3125\",\n          \"customer_name\": \"Beta LLC\",\n          \"fulfillment_id\": 90142,\n          \"carrier\": \"ups\",\n          \"carrier_void_supported\": true,\n          \"voidable_status\": \"voidable\",\n          \"voidable_detail\": null,\n          \"quantity\": 4,\n          \"allocation_ids\": [\n            88205\n          ]\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"Preview Return Receipt Release Impact","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/return-receipts/preview-release-impact","host":["{{baseUrl}}"],"path":["api","return-receipts","preview-release-impact"]},"description":"Read-only preview of which back-ordered sales orders this return receipt would release, per restock-to-sellable line. Send the same payload you would POST to create the receipt; each line that restocks sellable stock is resolved to its product and the receive warehouse and evaluated against waiting back-ordered demand. Nothing is written.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach row describes one product the receive would restock. `releasable_quantity` is the number of incoming units that can be released to back-ordered demand; `orders[]` are the back-ordered sales orders that would be filled; `held_back[]` is demand that stays back-ordered, with `reason_kept` explaining why (e.g. \"Insufficient supply\").\n\nBody: the same shape as Create Return Receipt. Minimum: `warehouse_id` and one `return_receipt_lines` entry with `product_id`, `quantity`, and `action`. `release_backorders` (true/false/null) mirrors the receive call's release control.\n\nAuthentication: Requires Bearer token. Requires the returns.receive permission.","body":{"mode":"raw","raw":"{\n  \"warehouse_id\": 1,\n  \"release_backorders\": true,\n  \"return_receipt_lines\": [\n    {\n      \"product_id\": 123,\n      \"rma_line_id\": 3,\n      \"quantity\": 8,\n      \"action\": \"return_to_stock\",\n      \"return_reason_id\": 1\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/return-receipts/preview-release-impact","host":["{{baseUrl}}"],"path":["api","return-receipts","preview-release-impact"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 123,\n      \"product_sku\": \"WIDGET-001\",\n      \"product_name\": \"Blue Widget 500ml\",\n      \"releasable_quantity\": 8,\n      \"orders\": [\n        {\n          \"sales_order_id\": 4602,\n          \"order_number\": \"SO-10501\",\n          \"customer_name\": \"Acme Retail\",\n          \"quantity\": 5\n        },\n        {\n          \"sales_order_id\": 4610,\n          \"order_number\": \"SO-10509\",\n          \"customer_name\": \"Gamma Foods\",\n          \"quantity\": 3\n        }\n      ],\n      \"held_back\": [\n        {\n          \"allocation_id\": 90311,\n          \"product_id\": 123,\n          \"sales_order_id\": 4620,\n          \"sales_order_line_id\": 55123,\n          \"sales_order_number\": \"SO-10518\",\n          \"customer_name\": \"Delta Mart\",\n          \"quantity\": 4,\n          \"from_status\": \"planned\",\n          \"to_status\": \"allocated\",\n          \"reason_kept\": \"Insufficient supply\"\n        }\n      ]\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"List Return Reasons","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/return-reasons?archived=0&search=&sort=name&page=1&limit=25","host":["{{baseUrl}}"],"path":["api","return-reasons"],"query":[{"key":"archived","value":"0","description":"Filter by archived status: 0 (active) or 1 (archived). Default: 0","disabled":true},{"key":"search","value":"","description":"Search return reasons by name","disabled":true},{"key":"sort","value":"name","description":"Sort field. Prefix with - for descending. Allowed: id, name, created_at, updated_at","disabled":true},{"key":"page","value":"1","description":"Page number for pagination","disabled":true},{"key":"limit","value":"25","description":"Records per page","disabled":true}]},"description":"List all return reasons with standard list query parameters. Supports filtering, sorting, search and pagination.\n\nAuthentication: Requires Bearer token.\n\nSort fields: id, name, created_at, updated_at."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/return-reasons?archived=0&search=&sort=name&page=1&limit=25","host":["{{baseUrl}}"],"path":["api","return-reasons"],"query":[{"key":"archived","value":"0","description":"Filter by archived status: 0 (active) or 1 (archived). Default: 0","disabled":true},{"key":"search","value":"","description":"Search return reasons by name","disabled":true},{"key":"sort","value":"name","description":"Sort field. Prefix with - for descending. Allowed: id, name, created_at, updated_at","disabled":true},{"key":"page","value":"1","description":"Page number for pagination","disabled":true},{"key":"limit","value":"25","description":"Records per page","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Defective Item\",\n      \"created_at\": \"2024-01-01T00:00:00Z\",\n      \"updated_at\": \"2024-01-01T00:00:00Z\",\n      \"archived_at\": null\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Wrong Item Shipped\",\n      \"created_at\": \"2024-01-01T00:00:00Z\",\n      \"updated_at\": \"2024-01-01T00:00:00Z\",\n      \"archived_at\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 2\n}"}]},{"name":"Create Return Reason","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/return-reasons","host":["{{baseUrl}}"],"path":["api","return-reasons"]},"description":"Create a new return reason.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- name (required): Unique return reason name, max 255 characters\n\n**Requires permission:** `return_reasons.create`","body":{"mode":"raw","raw":"{\n  \"name\": \"Customer Changed Mind\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/return-reasons","host":["{{baseUrl}}"],"path":["api","return-reasons"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"return reason has been successfully created\",\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Customer Changed Mind\",\n    \"created_at\": \"2024-03-15T14:30:00Z\",\n    \"updated_at\": \"2024-03-15T14:30:00Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Bulk Delete Return Reasons","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/return-reasons","host":["{{baseUrl}}"],"path":["api","return-reasons"]},"description":"Bulk delete multiple return reasons by ID.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `return_reasons.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/return-reasons","host":["{{baseUrl}}"],"path":["api","return-reasons"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 return reasons deleted successfully\"\n}"}]},{"name":"Get Return Reason","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/return-reasons/{return_reason}","host":["{{baseUrl}}"],"path":["api","return-reasons",":return_reason"],"variable":[{"key":"return_reason","value":"1","description":"Return reason ID"}]},"description":"Retrieve a single return reason by ID.\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/return-reasons/{return_reason}","host":["{{baseUrl}}"],"path":["api","return-reasons",":return_reason"],"variable":[{"key":"return_reason","value":"1","description":"Return reason ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Defective Item\",\n    \"created_at\": \"2024-01-01T00:00:00Z\",\n    \"updated_at\": \"2024-01-01T00:00:00Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Update Return Reason","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/return-reasons/{return_reason}","host":["{{baseUrl}}"],"path":["api","return-reasons",":return_reason"],"variable":[{"key":"return_reason","value":"5","description":"Return reason ID"}]},"description":"Update an existing return reason.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- name (required): New unique return reason name, max 255 characters\n\n**Requires permission:** `return_reasons.update`","body":{"mode":"raw","raw":"{\n  \"name\": \"Changed Mind / No Longer Needed\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/return-reasons/{return_reason}","host":["{{baseUrl}}"],"path":["api","return-reasons",":return_reason"],"variable":[{"key":"return_reason","value":"5","description":"Return reason ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"return reason Changed Mind / No Longer Needed has been successfully updated\",\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Changed Mind / No Longer Needed\",\n    \"created_at\": \"2024-03-15T14:30:00Z\",\n    \"updated_at\": \"2024-03-15T15:00:00Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Delete Return Reason","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/return-reasons/{return_reason}","host":["{{baseUrl}}"],"path":["api","return-reasons",":return_reason"],"variable":[{"key":"return_reason","value":"5","description":"Return reason ID"}]},"description":"Delete a return reason. Will fail if reason is linked to existing return receipt lines.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `return_reasons.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/return-reasons/{return_reason}","host":["{{baseUrl}}"],"path":["api","return-reasons",":return_reason"],"variable":[{"key":"return_reason","value":"5","description":"Return reason ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"return reason Changed Mind / No Longer Needed has been successfully deleted\"\n}"}]},{"name":"Check Return Reasons Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/return-reasons/deletable","host":["{{baseUrl}}"],"path":["api","return-reasons","deletable"]},"description":"Check which return reasons can be safely deleted. Returns deletability status per ID.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody:\n- ids (required): Array of integer return reason IDs\n\n**Requires permission:** `return_reasons.delete`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    5\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/return-reasons/deletable","host":["{{baseUrl}}"],"path":["api","return-reasons","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Defective Item\",\n      \"deletable\": false,\n      \"reason\": \"In use by 12 return receipts\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Wrong Item Shipped\",\n      \"deletable\": false,\n      \"reason\": \"In use by 5 return receipts\"\n    },\n    {\n      \"id\": 5,\n      \"name\": \"Changed Mind\",\n      \"deletable\": true,\n      \"reason\": null\n    }\n  ]\n}"}]},{"name":"Archive Return Reason","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/return-reasons/{return_reason}/archive","host":["{{baseUrl}}"],"path":["api","return-reasons",":return_reason","archive"],"variable":[{"key":"return_reason","value":"5","description":"Return reason ID"}]},"description":"Archive a single return reason.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `return_reasons.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/return-reasons/{return_reason}/archive","host":["{{baseUrl}}"],"path":["api","return-reasons",":return_reason","archive"],"variable":[{"key":"return_reason","value":"5","description":"Return reason ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"return reason Changed Mind has been archived\",\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Changed Mind\",\n    \"archived_at\": \"2024-03-16T10:00:00Z\"\n  }\n}"}]},{"name":"Unarchive Return Reason","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/return-reasons/{return_reason}/unarchived","host":["{{baseUrl}}"],"path":["api","return-reasons",":return_reason","unarchived"],"variable":[{"key":"return_reason","value":"5","description":"Return reason ID"}]},"description":"Unarchive a single return reason.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `return_reasons.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/return-reasons/{return_reason}/unarchived","host":["{{baseUrl}}"],"path":["api","return-reasons",":return_reason","unarchived"],"variable":[{"key":"return_reason","value":"5","description":"Return reason ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"return reason Changed Mind has been unarchived\",\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Changed Mind\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Bulk Archive Return Reasons","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/return-reasons/archive","host":["{{baseUrl}}"],"path":["api","return-reasons","archive"]},"description":"Bulk archive multiple return reasons.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `return_reasons.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    5,\n    6\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/return-reasons/archive","host":["{{baseUrl}}"],"path":["api","return-reasons","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 return reasons archived successfully\"\n}"}]},{"name":"Bulk Unarchive Return Reasons","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/return-reasons/unarchive","host":["{{baseUrl}}"],"path":["api","return-reasons","unarchive"]},"description":"Bulk unarchive multiple return reasons.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `return_reasons.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    5,\n    6\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/return-reasons/unarchive","host":["{{baseUrl}}"],"path":["api","return-reasons","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 return reasons unarchived successfully\"\n}"}]},{"name":"List Disposition Policies","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/return-disposition-policies","host":["{{baseUrl}}"],"path":["api","return-disposition-policies"]},"description":"List all return disposition policies, ordered by precedence (priority ascending, then id ascending).\n\nAuthentication: Requires Bearer token.\n\nDisposition policies are evaluated top-to-bottom at return-receiving time to auto-assign a line's disposition (added to stock, discarded, or new blemished SKU). The first active policy whose match_* criteria all match wins; if none match, the default is added_to_stock.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/return-disposition-policies","host":["{{baseUrl}}"],"path":["api","return-disposition-policies"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Like-new returns → restock\",\n      \"is_active\": true,\n      \"priority\": 0,\n      \"match_product_id\": null,\n      \"match_product_category_id\": null,\n      \"match_return_reason_id\": null,\n      \"match_warehouse_id\": null,\n      \"match_condition_grade\": \"A\",\n      \"match_condition_grade_label\": \"Grade A — Like New\",\n      \"action\": \"added_to_stock\",\n      \"action_label\": \"Added to Stock\",\n      \"blemished_condition\": null,\n      \"created_at\": \"2026-06-09T09:00:00.000000Z\",\n      \"updated_at\": \"2026-06-09T09:00:00.000000Z\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Grade B → blemished SKU\",\n      \"is_active\": true,\n      \"priority\": 1,\n      \"match_product_id\": null,\n      \"match_product_category_id\": null,\n      \"match_return_reason_id\": null,\n      \"match_warehouse_id\": null,\n      \"match_condition_grade\": \"B\",\n      \"match_condition_grade_label\": \"Grade B — Good\",\n      \"action\": \"new_blemished_sku\",\n      \"action_label\": \"New Blemished SKU\",\n      \"blemished_condition\": \"Open box — good condition\",\n      \"created_at\": \"2026-06-09T09:00:00.000000Z\",\n      \"updated_at\": \"2026-06-09T09:00:00.000000Z\"\n    }\n  ]\n}"}]},{"name":"Create Disposition Policy","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/return-disposition-policies","host":["{{baseUrl}}"],"path":["api","return-disposition-policies"]},"description":"Create a new return disposition policy.\n\nAuthentication: Requires Bearer token.\n\nFields:\n- name (string, required, max 255) — human-readable policy name\n- action (string, required) — disposition to apply when this policy matches. One of: added_to_stock, discarded, new_blemished_sku\n- is_active (boolean, optional, default true) — only active policies are evaluated at resolve time\n- priority (integer, optional, min 0) — lower numbers are evaluated first; ties broken by id\n- match_product_id (integer, optional, nullable) — restrict to a single product\n- match_product_category_id (integer, optional, nullable) — restrict to a product category (matches the product's category and any ancestor category)\n- match_return_reason_id (integer, optional, nullable) — restrict to a return reason\n- match_warehouse_id (integer, optional, nullable) — restrict to a warehouse\n- match_condition_grade (string, optional, nullable) — restrict to a condition grade. One of: A, B, C, unsellable\n- blemished_condition (string, optional, nullable, max 255) — condition note applied when action is new_blemished_sku\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.","body":{"mode":"raw","raw":"{\n  \"name\": \"Damaged electronics → discard\",\n  \"action\": \"discarded\",\n  \"is_active\": true,\n  \"priority\": 0,\n  \"match_product_id\": null,\n  \"match_product_category_id\": 12,\n  \"match_return_reason_id\": 4,\n  \"match_warehouse_id\": null,\n  \"match_condition_grade\": \"unsellable\",\n  \"blemished_condition\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/return-disposition-policies","host":["{{baseUrl}}"],"path":["api","return-disposition-policies"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Damaged electronics → discard\",\n    \"is_active\": true,\n    \"priority\": 0,\n    \"match_product_id\": null,\n    \"match_product_category_id\": 12,\n    \"match_product_category\": {\n      \"id\": 12,\n      \"name\": \"Electronics\"\n    },\n    \"match_return_reason_id\": 4,\n    \"match_return_reason\": {\n      \"id\": 4,\n      \"name\": \"Arrived damaged\"\n    },\n    \"match_warehouse_id\": null,\n    \"match_condition_grade\": \"unsellable\",\n    \"match_condition_grade_label\": \"Unsellable\",\n    \"action\": \"discarded\",\n    \"action_label\": \"Discarded\",\n    \"blemished_condition\": null,\n    \"created_at\": \"2026-06-09T09:00:00.000000Z\",\n    \"updated_at\": \"2026-06-09T09:00:00.000000Z\"\n  },\n  \"message\": \"Disposition Policy created successfully\"\n}"}]},{"name":"Get Disposition Policy","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/return-disposition-policies/{returnDispositionPolicy}","host":["{{baseUrl}}"],"path":["api","return-disposition-policies",":returnDispositionPolicy"],"variable":[{"key":"returnDispositionPolicy","value":"3","description":"Disposition policy ID"}]},"description":"Get a single disposition policy with its matched product, category, return reason, and warehouse summaries.\n\nAuthentication: Requires Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/return-disposition-policies/{returnDispositionPolicy}","host":["{{baseUrl}}"],"path":["api","return-disposition-policies",":returnDispositionPolicy"],"variable":[{"key":"returnDispositionPolicy","value":"3","description":"Disposition policy ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Damaged electronics → discard\",\n    \"is_active\": true,\n    \"priority\": 0,\n    \"match_product_id\": null,\n    \"match_product_category_id\": 12,\n    \"match_product_category\": {\n      \"id\": 12,\n      \"name\": \"Electronics\"\n    },\n    \"match_return_reason_id\": 4,\n    \"match_return_reason\": {\n      \"id\": 4,\n      \"name\": \"Arrived damaged\"\n    },\n    \"match_warehouse_id\": null,\n    \"match_condition_grade\": \"unsellable\",\n    \"match_condition_grade_label\": \"Unsellable\",\n    \"action\": \"discarded\",\n    \"action_label\": \"Discarded\",\n    \"blemished_condition\": null,\n    \"created_at\": \"2026-06-09T09:00:00.000000Z\",\n    \"updated_at\": \"2026-06-09T09:00:00.000000Z\"\n  }\n}"}]},{"name":"Update Disposition Policy","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/return-disposition-policies/{returnDispositionPolicy}","host":["{{baseUrl}}"],"path":["api","return-disposition-policies",":returnDispositionPolicy"],"variable":[{"key":"returnDispositionPolicy","value":"3","description":"Disposition policy ID"}]},"description":"Update an existing disposition policy. Send the full set of fields (same shape as create).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- name (string, required, max 255) — human-readable policy name\n- action (string, required) — disposition to apply when this policy matches. One of: added_to_stock, discarded, new_blemished_sku\n- is_active (boolean, optional, default true) — only active policies are evaluated at resolve time\n- priority (integer, optional, min 0) — lower numbers are evaluated first; ties broken by id\n- match_product_id (integer, optional, nullable) — restrict to a single product\n- match_product_category_id (integer, optional, nullable) — restrict to a product category (matches the product's category and any ancestor category)\n- match_return_reason_id (integer, optional, nullable) — restrict to a return reason\n- match_warehouse_id (integer, optional, nullable) — restrict to a warehouse\n- match_condition_grade (string, optional, nullable) — restrict to a condition grade. One of: A, B, C, unsellable\n- blemished_condition (string, optional, nullable, max 255) — condition note applied when action is new_blemished_sku\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.","body":{"mode":"raw","raw":"{\n  \"name\": \"Damaged electronics → discard\",\n  \"action\": \"discarded\",\n  \"is_active\": true,\n  \"priority\": 0,\n  \"match_product_id\": null,\n  \"match_product_category_id\": 12,\n  \"match_return_reason_id\": 4,\n  \"match_warehouse_id\": null,\n  \"match_condition_grade\": \"unsellable\",\n  \"blemished_condition\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/return-disposition-policies/{returnDispositionPolicy}","host":["{{baseUrl}}"],"path":["api","return-disposition-policies",":returnDispositionPolicy"],"variable":[{"key":"returnDispositionPolicy","value":"3","description":"Disposition policy ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Damaged electronics → discard\",\n    \"is_active\": true,\n    \"priority\": 0,\n    \"match_product_id\": null,\n    \"match_product_category_id\": 12,\n    \"match_product_category\": {\n      \"id\": 12,\n      \"name\": \"Electronics\"\n    },\n    \"match_return_reason_id\": 4,\n    \"match_return_reason\": {\n      \"id\": 4,\n      \"name\": \"Arrived damaged\"\n    },\n    \"match_warehouse_id\": null,\n    \"match_condition_grade\": \"unsellable\",\n    \"match_condition_grade_label\": \"Unsellable\",\n    \"action\": \"discarded\",\n    \"action_label\": \"Discarded\",\n    \"blemished_condition\": null,\n    \"created_at\": \"2026-06-09T09:00:00.000000Z\",\n    \"updated_at\": \"2026-06-09T09:00:00.000000Z\"\n  },\n  \"message\": \"Disposition Policy updated successfully\"\n}"}]},{"name":"Delete Disposition Policy","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/return-disposition-policies/{returnDispositionPolicy}","host":["{{baseUrl}}"],"path":["api","return-disposition-policies",":returnDispositionPolicy"],"variable":[{"key":"returnDispositionPolicy","value":"3","description":"Disposition policy ID"}]},"description":"Delete a disposition policy. Already-received receipt lines retain their assigned disposition; only future return-receiving evaluation is affected.\n\nAuthentication: Requires Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/return-disposition-policies/{returnDispositionPolicy}","host":["{{baseUrl}}"],"path":["api","return-disposition-policies",":returnDispositionPolicy"],"variable":[{"key":"returnDispositionPolicy","value":"3","description":"Disposition policy ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Disposition Policy deleted successfully\"\n}"}]},{"name":"Reorder Disposition Policies","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/return-disposition-policies/reorder","host":["{{baseUrl}}"],"path":["api","return-disposition-policies","reorder"]},"description":"Persist a new precedence order for the disposition policies. The position of each id in the ordered_ids array becomes its priority (first = priority 0, evaluated first).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- ordered_ids (array of integers, required, min 1) — policy IDs in the desired evaluation order. Must be distinct and each must exist.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"ordered_ids\": [\n    2,\n    1,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/return-disposition-policies/reorder","host":["{{baseUrl}}"],"path":["api","return-disposition-policies","reorder"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Like-new returns → restock\",\n      \"is_active\": true,\n      \"priority\": 0,\n      \"match_product_id\": null,\n      \"match_product_category_id\": null,\n      \"match_return_reason_id\": null,\n      \"match_warehouse_id\": null,\n      \"match_condition_grade\": \"A\",\n      \"match_condition_grade_label\": \"Grade A — Like New\",\n      \"action\": \"added_to_stock\",\n      \"action_label\": \"Added to Stock\",\n      \"blemished_condition\": null,\n      \"created_at\": \"2026-06-09T09:00:00.000000Z\",\n      \"updated_at\": \"2026-06-09T09:00:00.000000Z\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Grade B → blemished SKU\",\n      \"is_active\": true,\n      \"priority\": 1,\n      \"match_product_id\": null,\n      \"match_product_category_id\": null,\n      \"match_return_reason_id\": null,\n      \"match_warehouse_id\": null,\n      \"match_condition_grade\": \"B\",\n      \"match_condition_grade_label\": \"Grade B — Good\",\n      \"action\": \"new_blemished_sku\",\n      \"action_label\": \"New Blemished SKU\",\n      \"blemished_condition\": \"Open box — good condition\",\n      \"created_at\": \"2026-06-09T09:00:00.000000Z\",\n      \"updated_at\": \"2026-06-09T09:00:00.000000Z\"\n    }\n  ],\n  \"message\": \"Disposition policy order updated.\"\n}"}]},{"name":"Get Returns Summary KPIs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/returns/analytics/summary","host":["{{baseUrl}}"],"path":["api","returns","analytics","summary"]},"description":"KPI summary: return rate, returned/shipped units, RMA count, restock recovery rate, recovered COGS value, discard rate, and average days to disposition.\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. All query params are optional; dates are app-timezone and converted to UTC. warehouse_ids / product_ids are comma-separated."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/returns/analytics/summary","host":["{{baseUrl}}"],"path":["api","returns","analytics","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"return_rate\": 0.084,\n    \"returned_units\": 412,\n    \"shipped_units\": 4904,\n    \"rma_count\": 118,\n    \"restock_recovery_rate\": 0.74,\n    \"recovered_value\": 12480.5,\n    \"discard_rate\": 0.18,\n    \"avg_days_to_disposition\": 6.2\n  }\n}"}]},{"name":"Get Returns by Reason","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/returns/analytics/by-reason","host":["{{baseUrl}}"],"path":["api","returns","analytics","by-reason"]},"description":"Counts and units grouped by return reason, ordered by units desc.\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. All query params are optional; dates are app-timezone and converted to UTC. warehouse_ids / product_ids are comma-separated."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/returns/analytics/by-reason","host":["{{baseUrl}}"],"path":["api","returns","analytics","by-reason"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"reason_id\": 3,\n      \"reason\": \"Defective\",\n      \"line_count\": 48,\n      \"units\": 169\n    },\n    {\n      \"reason_id\": 1,\n      \"reason\": \"Wrong size\",\n      \"line_count\": 31,\n      \"units\": 107\n    }\n  ]\n}"}]},{"name":"Get Returns by Disposition","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/returns/analytics/by-disposition","host":["{{baseUrl}}"],"path":["api","returns","analytics","by-disposition"]},"description":"Units grouped by disposition action (added_to_stock / discarded / new_blemished_sku).\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. All query params are optional; dates are app-timezone and converted to UTC. warehouse_ids / product_ids are comma-separated."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/returns/analytics/by-disposition","host":["{{baseUrl}}"],"path":["api","returns","analytics","by-disposition"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"action\": \"added_to_stock\",\n      \"label\": \"Add to Stock\",\n      \"line_count\": 210,\n      \"units\": 305\n    },\n    {\n      \"action\": \"discarded\",\n      \"label\": \"Discard\",\n      \"line_count\": 60,\n      \"units\": 74\n    }\n  ]\n}"}]},{"name":"Get Top Returned Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/returns/analytics/by-product","host":["{{baseUrl}}"],"path":["api","returns","analytics","by-product"]},"description":"Top returned products by units (limit 20).\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. All query params are optional; dates are app-timezone and converted to UTC. warehouse_ids / product_ids are comma-separated."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/returns/analytics/by-product","host":["{{baseUrl}}"],"path":["api","returns","analytics","by-product"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 10,\n      \"sku\": \"WIDGET-BLK\",\n      \"name\": \"Widget - Black\",\n      \"line_count\": 40,\n      \"units\": 88\n    }\n  ]\n}"}]},{"name":"Get Returns Trend","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/returns/analytics/trend","host":["{{baseUrl}}"],"path":["api","returns","analytics","trend"]},"description":"Returns over time, grouped by week (Monday-start) or month.\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. All query params are optional; dates are app-timezone and converted to UTC. warehouse_ids / product_ids are comma-separated."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/returns/analytics/trend","host":["{{baseUrl}}"],"path":["api","returns","analytics","trend"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"period\": \"2026-05-04\",\n      \"units\": 61,\n      \"receipt_count\": 22\n    },\n    {\n      \"period\": \"2026-05-11\",\n      \"units\": 58,\n      \"receipt_count\": 19\n    }\n  ]\n}"}]},{"name":"List Returns Inbox","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/returns/inbox","host":["{{baseUrl}}"],"path":["api","returns","inbox"]},"description":"Paginated, enriched marketplace returns across channels. Each row has link status (an RMA already linked via external_source/external_return_id) and, when unlinked, a suggested order match.\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. All filters optional."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/returns/inbox","host":["{{baseUrl}}"],"path":["api","returns","inbox"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"source\": \"trackstar\",\n      \"integration_instance_id\": 4,\n      \"external_return_id\": \"TS-10293\",\n      \"channel_order_id\": \"SO-10293\",\n      \"status\": \"requested\",\n      \"customer_name\": \"Acme Co\",\n      \"returned_at\": \"2026-06-01T10:00:00.000000Z\",\n      \"is_linked\": false,\n      \"linked_rma_id\": null,\n      \"linked_rma_number\": null,\n      \"suggested_order_id\": 42,\n      \"suggested_order_number\": \"SO-10293\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Bulk Link Returns","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/returns/inbox/bulk-link","host":["{{baseUrl}}"],"path":["api","returns","inbox","bulk-link"]},"description":"Link the selected marketplace returns to RMAs (creating/linking per the match service; idempotent and conflict-safe).\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- returns[] (required): { source (shopify|walmart|trackstar), external_return_id, channel_order_id? }","body":{"mode":"raw","raw":"{\n  \"returns\": [\n    {\n      \"source\": \"trackstar\",\n      \"external_return_id\": \"TS-10293\",\n      \"channel_order_id\": \"SO-10293\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/returns/inbox/bulk-link","host":["{{baseUrl}}"],"path":["api","returns","inbox","bulk-link"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"created\": 1,\n    \"linked\": 0,\n    \"conflict\": 0,\n    \"no_match\": 0,\n    \"already_linked\": 0\n  },\n  \"message\": \"Linked 1 return(s): 1 new RMA(s), 0 existing, 0 unmatched, 0 conflicts.\"\n}"}]},{"name":"Match Returns Now","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/returns/inbox/match-now","host":["{{baseUrl}}"],"path":["api","returns","inbox","match-now"]},"description":"Dispatch the tracked auto-match job for unlinked marketplace returns (optionally scoped to a source / integration). Returns a tracked_job_log_id; progress shows via the tracked job log endpoints. Job name: \"Match Marketplace Returns\".\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. source and integration_instance_id are optional (omit for all).","body":{"mode":"raw","raw":"{\n  \"source\": \"trackstar\",\n  \"integration_instance_id\": 4\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/returns/inbox/match-now","host":["{{baseUrl}}"],"path":["api","returns","inbox","match-now"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 4310\n  },\n  \"message\": \"Matching marketplace returns — track progress in the job tray.\"\n}"}]},{"name":"List Unmatched Returns","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/returns/reconciliation/unmatched","host":["{{baseUrl}}"],"path":["api","returns","reconciliation","unmatched"]},"description":"Paginated list of received returns whose lines are not all linked to a return authorization (RMA) — returns received without a matching authorization, partially matched batches, plus ambiguous matches flagged for review. Use it to reconcile or write off aged, unmatched returns.\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA receipt stays listed while any one of its lines is unlinked, so a batch that is otherwise fully reconciled can be held open by a single stray item. Dismiss such a receipt (see Dismiss Unmatched Return) to acknowledge the residual lines and drop it from this list; filter[dismissed] controls whether dismissed receipts are returned.\n\nFilter by warehouse, reconciliation status, dismissal scope, aging, received date, or product, and search by SKU/name.\n\nRequires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/returns/reconciliation/unmatched","host":["{{baseUrl}}"],"path":["api","returns","reconciliation","unmatched"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 4821,\n      \"received_at\": \"2026-07-21T02:14:00.000000Z\",\n      \"age_days\": 9,\n      \"reconciliation_status\": {\n        \"value\": \"partially_matched\",\n        \"label\": \"Partially Matched\"\n      },\n      \"needs_review\": false,\n      \"is_blind\": true,\n      \"is_dismissed\": false,\n      \"dismissed_at\": null,\n      \"dismissal_reason\": null,\n      \"dismissed_by\": null,\n      \"notes\": null,\n      \"warehouse\": {\n        \"id\": 12,\n        \"name\": \"Returns Warehouse\"\n      },\n      \"received_by\": {\n        \"id\": 3,\n        \"name\": \"Warehouse Operator\"\n      },\n      \"rma\": null,\n      \"lines\": [\n        {\n          \"id\": 9901,\n          \"product_id\": 55,\n          \"sku\": \"BRIEF-BUR-M\",\n          \"product_name\": \"Burgundy Brief M\",\n          \"quantity\": 1,\n          \"unexpected_item\": false,\n          \"rma_line_id\": 3310,\n          \"matched_rma\": {\n            \"id\": 812,\n            \"rma_number\": \"RMA-2026-15452\",\n            \"link_route\": \"/rma/812\"\n          }\n        },\n        {\n          \"id\": 9902,\n          \"product_id\": 61,\n          \"sku\": \"SOCK-NVY-L\",\n          \"product_name\": \"Navy Sock L\",\n          \"quantity\": 1,\n          \"unexpected_item\": true,\n          \"rma_line_id\": null,\n          \"matched_rma\": null\n        }\n      ],\n      \"matched_line_count\": 1,\n      \"line_count\": 2\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/returns/reconciliation/unmatched?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/returns/reconciliation/unmatched?page=1\",\n  \"next_page_url\": null,\n  \"path\": \"{{protocol}}{{domain}}/api/returns/reconciliation/unmatched\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"List Recently Received Returns","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/returns/reconciliation/recent?warehouse_id=12","host":["{{baseUrl}}"],"path":["api","returns","reconciliation","recent"],"query":[{"key":"warehouse_id","value":"12","description":"Warehouse to list recent returns for (required).","disabled":true}]},"description":"Returns committed in a warehouse within the given window (today, or the last 7 days), across all operators — for confirming what has been received. Read-only.\n\n**Required scope: `returns:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/returns/reconciliation/recent?warehouse_id=12","host":["{{baseUrl}}"],"path":["api","returns","reconciliation","recent"],"query":[{"key":"warehouse_id","value":"12","description":"Warehouse to list recent returns for (required).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 4821,\n      \"received_at\": \"2026-07-21T02:14:00.000000Z\",\n      \"age_days\": 9,\n      \"reconciliation_status\": {\n        \"value\": \"partially_matched\",\n        \"label\": \"Partially Matched\"\n      },\n      \"needs_review\": false,\n      \"is_blind\": true,\n      \"is_dismissed\": false,\n      \"dismissed_at\": null,\n      \"dismissal_reason\": null,\n      \"dismissed_by\": null,\n      \"notes\": null,\n      \"warehouse\": {\n        \"id\": 12,\n        \"name\": \"Returns Warehouse\"\n      },\n      \"received_by\": {\n        \"id\": 3,\n        \"name\": \"Warehouse Operator\"\n      },\n      \"rma\": null,\n      \"lines\": [\n        {\n          \"id\": 9901,\n          \"product_id\": 55,\n          \"sku\": \"BRIEF-BUR-M\",\n          \"product_name\": \"Burgundy Brief M\",\n          \"quantity\": 1,\n          \"unexpected_item\": false,\n          \"rma_line_id\": 3310,\n          \"matched_rma\": {\n            \"id\": 812,\n            \"rma_number\": \"RMA-2026-15452\",\n            \"link_route\": \"/rma/812\"\n          }\n        },\n        {\n          \"id\": 9902,\n          \"product_id\": 61,\n          \"sku\": \"SOCK-NVY-L\",\n          \"product_name\": \"Navy Sock L\",\n          \"quantity\": 1,\n          \"unexpected_item\": true,\n          \"rma_line_id\": null,\n          \"matched_rma\": null\n        }\n      ],\n      \"matched_line_count\": 1,\n      \"line_count\": 2\n    }\n  ]\n}"}]},{"name":"Link Return Receipt to RMA","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/returns/reconciliation/{returnReceipt}/link","host":["{{baseUrl}}"],"path":["api","returns","reconciliation",":returnReceipt","link"],"variable":[{"key":"returnReceipt","value":"","description":"The return receipt ID."}]},"description":"Link an unmatched return receipt to a return authorization (RMA), recording its received quantity without adding inventory a second time (the goods were already received). Provide exactly one of rma_id or sales_order_id; with sales_order_id the single open, unreceived RMA on that order is used. Fails with 422 if the target RMA is already received (which would double-count) or the order has zero/multiple open RMAs.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequires a Bearer token.","body":{"mode":"raw","raw":"{\n  \"rma_id\": 812\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/returns/reconciliation/{returnReceipt}/link","host":["{{baseUrl}}"],"path":["api","returns","reconciliation",":returnReceipt","link"],"variable":[{"key":"returnReceipt","value":"","description":"The return receipt ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Return receipt linked.\",\n  \"data\": {\n    \"id\": 4821,\n    \"received_at\": \"2026-07-21T02:14:00.000000Z\",\n    \"age_days\": 0,\n    \"reconciliation_status\": {\n      \"value\": \"matched\",\n      \"label\": \"Matched\"\n    },\n    \"needs_review\": false,\n    \"is_blind\": false,\n    \"notes\": null,\n    \"warehouse\": {\n      \"id\": 12,\n      \"name\": \"Returns Warehouse\"\n    },\n    \"received_by\": {\n      \"id\": 3,\n      \"name\": \"Warehouse Operator\"\n    },\n    \"rma\": {\n      \"id\": 812,\n      \"rma_number\": \"RMA-2026-15452\",\n      \"sales_order_id\": 7781,\n      \"link_route\": \"/rma/812\",\n      \"link_type_label\": \"RMA\",\n      \"link_reference\": \"RMA-2026-15452\"\n    },\n    \"lines\": [\n      {\n        \"id\": 9901,\n        \"product_id\": 55,\n        \"sku\": \"BRIEF-BUR-M\",\n        \"product_name\": \"Burgundy Brief M\",\n        \"quantity\": 1,\n        \"unexpected_item\": false\n      }\n    ]\n  }\n}"}]},{"name":"Dismiss Unmatched Return","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/returns/reconciliation/{returnReceipt}/dismiss","host":["{{baseUrl}}"],"path":["api","returns","reconciliation",":returnReceipt","dismiss"],"variable":[{"key":"returnReceipt","value":"","description":"The return receipt ID."}]},"description":"Acknowledge a received return's residual unlinked lines and drop it from the unmatched returns list.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA receipt stays unmatched while any one of its lines is unlinked, so a batch with (say) 31 of 32 lines linked stays open indefinitely because of one item that will never match. Dismissing records who dismissed it, when, and an optional reason.\n\nDismissal is bookkeeping only: inventory, the authorization links already made, and the receipt's reconciliation status are all left untouched, and the receipt can be restored at any time. Dismissed receipts stay retrievable via filter[dismissed]=only (or =all) on List Unmatched Returns.\n\nBody: reason (optional string, max 500 characters).\n\nReturns 422 if the receipt is already dismissed, or if it is linked to a return authorization and therefore never appeared on the unmatched list.\n\nRequires a Bearer token and the returns.update permission.","body":{"mode":"raw","raw":"{\n  \"reason\": \"Unlabelled item returned by mistake - written off at stock take\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/returns/reconciliation/{returnReceipt}/dismiss","host":["{{baseUrl}}"],"path":["api","returns","reconciliation",":returnReceipt","dismiss"],"variable":[{"key":"returnReceipt","value":"","description":"The return receipt ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Return receipt dismissed from the unmatched report.\",\n  \"data\": {\n    \"id\": 4821,\n    \"received_at\": \"2026-07-21T02:14:00.000000Z\",\n    \"age_days\": 9,\n    \"reconciliation_status\": {\n      \"value\": \"partially_matched\",\n      \"label\": \"Partially Matched\"\n    },\n    \"needs_review\": false,\n    \"is_blind\": true,\n    \"is_dismissed\": true,\n    \"dismissed_at\": \"2026-07-30T09:12:44.000000Z\",\n    \"dismissal_reason\": \"Unlabelled item returned by mistake - written off at stock take\",\n    \"dismissed_by\": {\n      \"id\": 3,\n      \"name\": \"Warehouse Operator\"\n    },\n    \"notes\": null,\n    \"warehouse\": {\n      \"id\": 12,\n      \"name\": \"Returns Warehouse\"\n    },\n    \"received_by\": {\n      \"id\": 3,\n      \"name\": \"Warehouse Operator\"\n    },\n    \"rma\": null,\n    \"lines\": [\n      {\n        \"id\": 9901,\n        \"product_id\": 55,\n        \"sku\": \"BRIEF-BUR-M\",\n        \"product_name\": \"Burgundy Brief M\",\n        \"quantity\": 1,\n        \"unexpected_item\": false,\n        \"rma_line_id\": 3310,\n        \"matched_rma\": {\n          \"id\": 812,\n          \"rma_number\": \"RMA-2026-15452\",\n          \"link_route\": \"/rma/812\"\n        }\n      },\n      {\n        \"id\": 9902,\n        \"product_id\": 61,\n        \"sku\": \"SOCK-NVY-L\",\n        \"product_name\": \"Navy Sock L\",\n        \"quantity\": 1,\n        \"unexpected_item\": true,\n        \"rma_line_id\": null,\n        \"matched_rma\": null\n      }\n    ],\n    \"matched_line_count\": 1,\n    \"line_count\": 2\n  }\n}"}]},{"name":"Restore Dismissed Return","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/returns/reconciliation/{returnReceipt}/restore","host":["{{baseUrl}}"],"path":["api","returns","reconciliation",":returnReceipt","restore"],"variable":[{"key":"returnReceipt","value":"","description":"The return receipt ID."}]},"description":"Undo a dismissal and put the received return back on the unmatched returns list. Clears the dismissal timestamp, the user who dismissed it, and the reason; nothing else about the receipt changes.\n\n**Required scope: `returns:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns 422 if the receipt is not currently dismissed.\n\nRequires a Bearer token and the returns.update permission."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/returns/reconciliation/{returnReceipt}/restore","host":["{{baseUrl}}"],"path":["api","returns","reconciliation",":returnReceipt","restore"],"variable":[{"key":"returnReceipt","value":"","description":"The return receipt ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Return receipt restored to the unmatched report.\",\n  \"data\": {\n    \"id\": 4821,\n    \"received_at\": \"2026-07-21T02:14:00.000000Z\",\n    \"age_days\": 9,\n    \"reconciliation_status\": {\n      \"value\": \"partially_matched\",\n      \"label\": \"Partially Matched\"\n    },\n    \"needs_review\": false,\n    \"is_blind\": true,\n    \"is_dismissed\": false,\n    \"dismissed_at\": null,\n    \"dismissal_reason\": null,\n    \"dismissed_by\": null,\n    \"notes\": null,\n    \"warehouse\": {\n      \"id\": 12,\n      \"name\": \"Returns Warehouse\"\n    },\n    \"received_by\": {\n      \"id\": 3,\n      \"name\": \"Warehouse Operator\"\n    },\n    \"rma\": null,\n    \"lines\": [\n      {\n        \"id\": 9901,\n        \"product_id\": 55,\n        \"sku\": \"BRIEF-BUR-M\",\n        \"product_name\": \"Burgundy Brief M\",\n        \"quantity\": 1,\n        \"unexpected_item\": false,\n        \"rma_line_id\": 3310,\n        \"matched_rma\": {\n          \"id\": 812,\n          \"rma_number\": \"RMA-2026-15452\",\n          \"link_route\": \"/rma/812\"\n        }\n      },\n      {\n        \"id\": 9902,\n        \"product_id\": 61,\n        \"sku\": \"SOCK-NVY-L\",\n        \"product_name\": \"Navy Sock L\",\n        \"quantity\": 1,\n        \"unexpected_item\": true,\n        \"rma_line_id\": null,\n        \"matched_rma\": null\n      }\n    ],\n    \"matched_line_count\": 1,\n    \"line_count\": 2\n  }\n}"}]}]},{"name":"Inventory Adjustments","item":[{"name":"List Inventory Adjustments","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-adjustments?per_page=10&page=1","host":["{{baseUrl}}"],"path":["api","v2","inventory-adjustments"],"query":[{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"page","value":"1","description":"Page number (default 1).","disabled":true}]},"description":"Returns a paginated list of inventory adjustments.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-adjustments?per_page=10&page=1","host":["{{baseUrl}}"],"path":["api","v2","inventory-adjustments"],"query":[{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"page","value":"1","description":"Page number (default 1).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"product_id\": 15,\n      \"warehouse_id\": 2,\n      \"quantity\": -5,\n      \"reason\": \"Damaged\",\n      \"created_at\": \"2024-03-10T10:00:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 10,\n  \"per_page\": 10,\n  \"total\": 95\n}"}]},{"name":"Search Inventory Adjustments","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-adjustments","host":["{{baseUrl}}"],"path":["api","v2","inventory-adjustments"]},"description":"Returns the same paginated list of inventory adjustments as the GET version of this endpoint, but accepts filters as a JSON request body instead of the query string. Use this POST variant when the filter set is too large or too complex to fit in a URL.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nProvide advanced filters under `filter_groups`, a tree of conditions combined with a `conjunction` of `and` or `or`. Each condition names a `column`, an `operator`, and a `value`. A free-text `search` term matches against id, notes, SKU, product name, and warehouse.\n\nText columns (`notes`, `sku`, `product_name`, `warehouse`) accept: is, is_not, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, is_one_of, is_not_one_of.\nNumeric and date columns (`id`, `quantity`, `adjustment_date`, `created_at`, `updated_at`) accept: is, is_not, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between, is_empty, is_not_empty. The `direction` filter accepts `positive` or `negative`, `is_correction` accepts `yes` or `no`, and `archived` toggles archived records.\n\nSortable columns (via `sort`, prefix with `-` for descending): id, adjustment_date, quantity, archived_at, created_at, updated_at. Results default to `-created_at`. `per_page` is capped at 100.","body":{"mode":"raw","raw":"{\n  \"per_page\": 25,\n  \"page\": 1,\n  \"sort\": \"-adjustment_date\",\n  \"filter_groups\": {\n    \"conjunction\": \"and\",\n    \"children\": [\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"direction\",\n          \"operator\": \"is\",\n          \"value\": \"negative\"\n        }\n      },\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"adjustment_date\",\n          \"operator\": \"between\",\n          \"value\": [\n            \"2026-06-01\",\n            \"2026-06-30\"\n          ]\n        }\n      }\n    ]\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-adjustments","host":["{{baseUrl}}"],"path":["api","v2","inventory-adjustments"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 55231,\n      \"adjustment_date\": \"2026-06-15T00:00:00.000000Z\",\n      \"product_id\": 3312,\n      \"sku\": \"WIDGET-BLUE-01\",\n      \"product_name\": \"Blue Widget\",\n      \"stock_uom_code\": \"EA\",\n      \"is_lot_tracked\": false,\n      \"lot_tracking_method\": null,\n      \"product_average_cost\": 13.75,\n      \"quantity\": -4,\n      \"warehouse\": {\n        \"id\": 2,\n        \"name\": \"Main Warehouse\"\n      },\n      \"notes\": \"Damaged in receiving\",\n      \"reason\": \"damaged\",\n      \"is_correction\": false,\n      \"type\": {\n        \"link_type\": \"App\\\\Models\\\\InventoryAdjustment\",\n        \"link_id\": 55231,\n        \"parent_link_type\": null,\n        \"parent_link_id\": null\n      },\n      \"reference\": 55231,\n      \"unit_cost\": 13.75,\n      \"archived_at\": null,\n      \"created_at\": \"2026-06-15T08:41:00.000000Z\",\n      \"updated_at\": \"2026-06-15T08:41:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"per_page\": 25,\n  \"from\": 1,\n  \"to\": 1,\n  \"last_page\": 9,\n  \"total\": 214,\n  \"next_page_url\": \"https://api.example.com/api/v2/inventory-adjustments?page=2\",\n  \"prev_page_url\": null\n}"}]},{"name":"Export Inventory Adjustments","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-adjustments/export","host":["{{baseUrl}}"],"path":["api","v2","inventory-adjustments","export"]},"description":"Exports inventory adjustments as a CSV file.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.export`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-adjustments/export","host":["{{baseUrl}}"],"path":["api","v2","inventory-adjustments","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"id,product_id,warehouse_id,quantity,reason,created_at\n1,15,2,-5,Damaged,2024-03-10 10:00:00"}]},{"name":"Show Inventory Adjustment","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-adjustments/{inventoryAdjustment}","host":["{{baseUrl}}"],"path":["api","v2","inventory-adjustments",":inventoryAdjustment"],"variable":[{"key":"inventoryAdjustment","value":"1","description":"Inventory Adjustment ID"}]},"description":"Returns a single inventory adjustment.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-adjustments/{inventoryAdjustment}","host":["{{baseUrl}}"],"path":["api","v2","inventory-adjustments",":inventoryAdjustment"],"variable":[{"key":"inventoryAdjustment","value":"1","description":"Inventory Adjustment ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"product_id\": 15,\n    \"warehouse_id\": 2,\n    \"quantity\": -5,\n    \"reason\": \"Damaged\",\n    \"created_at\": \"2024-03-10T10:00:00.000000Z\"\n  }\n}"}]}]},{"name":"Inventory Movements","item":[{"name":"List Inventory Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-movements?per_page=10&page=1","host":["{{baseUrl}}"],"path":["api","v2","inventory-movements"],"query":[{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"page","value":"1","description":"Page number (default 1).","disabled":true}]},"description":"Returns a paginated list of inventory movements.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach movement includes its cost basis: `unit_cost` is the per-unit cost the movement was booked at, and `value_impact` is the signed value it moved into (positive) or out of (negative) inventory — quantity x unit_cost, rounded to the cent. Summing `value_impact` over a period reconciles the change in inventory value.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-movements?per_page=10&page=1","host":["{{baseUrl}}"],"path":["api","v2","inventory-movements"],"query":[{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"page","value":"1","description":"Page number (default 1).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 201,\n      \"type\": \"fulfillment\",\n      \"quantity\": -5,\n      \"unit_cost\": 12.24,\n      \"value_impact\": -61.2,\n      \"inventory_status\": \"committed\",\n      \"warehouse_id\": 2,\n      \"product_id\": 15,\n      \"created_at\": \"2024-03-12T11:00:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 50,\n  \"per_page\": 10,\n  \"total\": 498\n}"}]},{"name":"Search Inventory Movements","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-movements","host":["{{baseUrl}}"],"path":["api","v2","inventory-movements"]},"description":"Returns the same paginated inventory movement audit trail as the GET version of this endpoint, but accepts filters as a JSON request body instead of the query string. Use this POST variant when the filter set is too large or too complex to fit in a URL.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nProvide advanced filters under `filter_groups`, a tree of conditions combined with a `conjunction` of `and` or `or`. Each condition names a `column`, an `operator`, and a `value`. A free-text `search` term matches against id, SKU, product name, warehouse, and reference.\n\nText columns (`type`, `inventory_status`, `link_type`, `reference`, `product_name`, `sku`) accept: is, is_not, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, is_one_of, is_not_one_of.\nNumeric and date columns (`id`, `quantity`, `inventory_movement_date`, `created_at`, `updated_at`) accept: is, is_not, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between, is_empty, is_not_empty. The `warehouse` filter narrows by warehouse.\n\nSortable columns (via `sort`, prefix with `-` for descending): id, inventory_movement_date, quantity, type, inventory_status, reference, created_at, updated_at, warehouse_name, sku, product_name. Results default to `-inventory_movement_date`.","body":{"mode":"raw","raw":"{\n  \"per_page\": 25,\n  \"page\": 1,\n  \"sort\": \"-inventory_movement_date\",\n  \"filter_groups\": {\n    \"conjunction\": \"and\",\n    \"children\": [\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"type\",\n          \"operator\": \"is_one_of\",\n          \"value\": [\n            \"sale\",\n            \"adjustment\"\n          ]\n        }\n      },\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"quantity\",\n          \"operator\": \"less_than\",\n          \"value\": 0\n        }\n      }\n    ]\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-movements","host":["{{baseUrl}}"],"path":["api","v2","inventory-movements"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 442190,\n      \"inventory_movement_date\": \"2026-06-30T09:15:00.000000Z\",\n      \"product_id\": 3312,\n      \"sku\": \"WIDGET-BLUE-01\",\n      \"barcode\": \"8801234567890\",\n      \"product_name\": \"Blue Widget\",\n      \"stock_uom_code\": \"EA\",\n      \"type\": {\n        \"name\": \"sale\",\n        \"link_type\": \"App\\\\Models\\\\SalesOrderLine\",\n        \"link_id\": 88231,\n        \"parent_link_id\": 6021,\n        \"detail_link_id\": null\n      },\n      \"inventory_status\": \"available\",\n      \"quantity\": -3,\n      \"unit_cost\": 12.24,\n      \"value_impact\": -36.72,\n      \"warehouse\": {\n        \"id\": 2,\n        \"name\": \"Main Warehouse\"\n      },\n      \"layer\": {\n        \"fifo_layer_id\": 90312,\n        \"name\": \"PO-1042\"\n      },\n      \"fifo_layer_id\": 90312,\n      \"reference\": \"SO-6021\",\n      \"created_at\": \"2026-06-30T09:15:00.000000Z\",\n      \"updated_at\": \"2026-06-30T09:15:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"per_page\": 25,\n  \"from\": 1,\n  \"to\": 1,\n  \"last_page\": 120,\n  \"total\": 2984,\n  \"next_page_url\": \"https://api.example.com/api/v2/inventory-movements?page=2\",\n  \"prev_page_url\": null\n}"}]},{"name":"Export Inventory Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-movements/list/export?format=xlsx&scope=filtered","host":["{{baseUrl}}"],"path":["api","v2","inventory-movements","list","export"],"query":[{"key":"format","value":"xlsx","description":"Output format: xlsx (default) or csv. Any other value falls back to xlsx.","disabled":true},{"key":"scope","value":"filtered","description":"Export scope: all (ignore filters, including any filter_groups tree), filtered (apply current filters/search/sort), current_page (rows on the current page), selected (specific IDs via the ids parameter)","disabled":true}]},"description":"Exports inventory movements as an XLSX or CSV file download. Reuses the same Spatie QueryBuilder filters and sorts as the List Inventory Movements endpoint when scope=filtered.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nScopes:\n- all: ignore active filters/sort and export every record\n- filtered (default): apply the current filter/sort/search query params\n- current_page: export the rows on the current page (matches the visible table)\n- selected: export only the IDs supplied via the ids query param\n\nThe response depends on the size of the export:\n\n1. INLINE (up to 1,000 rows, or scope=current_page) — the file is the response body. Content-Type is application/vnd.openxmlformats-officedocument.spreadsheetml.sheet (xlsx) or text/csv (csv).\n\n2. QUEUED (more than 1,000 rows) — the export runs as a background job and the response is a JSON envelope carrying data.queued=true and data.tracked_job_log_id. Poll the tracked job log endpoints; when the job completes, its results.file names the finished file, which is fetched via Download Inventory Movements Export. The queued file is ordered by movement ID; xlsx exports above 15,000 rows are written as CSV instead. For scope=selected the queued path is used when more than 1,000 ids are supplied.\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.export`\n\nReconciliation columns (keys `valuation_value`, `in_valuation`; included by default when no `columns` param is sent, or via the UI's 'Reconciliation' export mode): 'Valuation Value (GL basis)' is the booked value plus the movement's recognized revaluation deltas — the same atom the Inventory Valuation / Inventory Value Change reports book — and 'In Valuation' states whether those reports count the row ('yes', or the scope exclusion that removes it: non-active status, consignment layer, Amazon FBA warehouse, deleted product, or no FIFO layer). Together they let a manual export reconciliation re-derive the report's population and values. 'Valuation Value (GL basis)' is bounded by the export's own inventory_movement_date filter: revaluation deltas recognized after the end of the filter's latest day (converted exactly as the valuation reports convert their cutoffs) are excluded, so a historical month's export carries that month's valuation basis. With no date filter the column carries all recognitions to date. When the valuation columns are exported AND the inventory_movement_date filter pins a full window (between/is), the export also appends one typed row per cost revision recognized inside that window on pre-period stock (Type 'revaluation_recognition', ID 'REV-{revaluation}-M{movement}') — the rows the report's 'Revaluations recognised on pre-period stock' line counts — so the file sums to the report's on-hand change exactly."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-movements/list/export?format=xlsx&scope=filtered","host":["{{baseUrl}}"],"path":["api","v2","inventory-movements","list","export"],"query":[{"key":"format","value":"xlsx","description":"Output format: xlsx (default) or csv. Any other value falls back to xlsx.","disabled":true},{"key":"scope","value":"filtered","description":"Export scope: all (ignore filters, including any filter_groups tree), filtered (apply current filters/search/sort), current_page (rows on the current page), selected (specific IDs via the ids parameter)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 48213,\n    \"queued\": true\n  },\n  \"message\": \"Export started — track progress in the job tray\"\n}"}]},{"name":"Download Inventory Movements Export","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-movements/list/export/download?file=inventory-movements-2026-09-16-10-15-30.csv","host":["{{baseUrl}}"],"path":["api","v2","inventory-movements","list","export","download"],"query":[{"key":"file","value":"inventory-movements-2026-09-16-10-15-30.csv","description":"Filename returned in the completed tracked job's results.file. The server basenames this value and serves the stored export; unknown filenames yield 404.","disabled":true}]},"description":"Downloads a completed background export produced by Export Inventory Movements (queued path). The filename is the one reported in the tracked job log's results.file field.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nResponse is the binary file (CSV or XLSX) with an attachment Content-Disposition header. Returns 404 if the file param is empty or the file no longer exists.\n\n**Requires permission:** `inventory.export`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-movements/list/export/download?file=inventory-movements-2026-09-16-10-15-30.csv","host":["{{baseUrl}}"],"path":["api","v2","inventory-movements","list","export","download"],"query":[{"key":"file","value":"inventory-movements-2026-09-16-10-15-30.csv","description":"Filename returned in the completed tracked job's results.file. The server basenames this value and serves the stored export; unknown filenames yield 404.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"<binary file>"}]},{"name":"Get Inventory Movement Aggregates","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-movements/aggregates?aggregates[value_impact]=sum","host":["{{baseUrl}}"],"path":["api","v2","inventory-movements","aggregates"],"query":[{"key":"aggregates[value_impact]","value":"sum","description":"Aggregate a column, as aggregates[&lt;column>]=&lt;calc>. Columns: quantity, unit_cost, value_impact. Calcs: sum, avg, min, max, range, count_all, count_values, count_unique, count_empty, count_not_empty.","disabled":true}]},"description":"Column-summary aggregates for the list's \"Calculate\" footer — computes Sum, Average, Min, Max, Range, or a Count variant per column over the FULL filtered set (not just the current page), in one query.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAggregatable columns: quantity, unit_cost, value_impact. Supported calcs: sum, avg, min, max, range, count_all, count_values, count_unique, count_empty, count_not_empty. Pass them as aggregates[&lt;column>]=&lt;calc>. Value Impact rounds per movement, so its sum equals the sum of the displayed Value Impact cells.\n\nApply the same filters and search as the list endpoint to scope the totals. An unknown calc returns 422. Authentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-movements/aggregates?aggregates[value_impact]=sum","host":["{{baseUrl}}"],"path":["api","v2","inventory-movements","aggregates"],"query":[{"key":"aggregates[value_impact]","value":"sum","description":"Aggregate a column, as aggregates[&lt;column>]=&lt;calc>. Columns: quantity, unit_cost, value_impact. Calcs: sum, avg, min, max, range, count_all, count_values, count_unique, count_empty, count_not_empty.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"value_impact\": {\n      \"calc\": \"sum\",\n      \"value\": -19865.37\n    },\n    \"quantity\": {\n      \"calc\": \"sum\",\n      \"value\": 439\n    },\n    \"unit_cost\": {\n      \"calc\": \"avg\",\n      \"value\": 12.24\n    }\n  }\n}"}]}]},{"name":"Listings","item":[{"name":"List Listings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listings?per_page=10&page=1","host":["{{baseUrl}}"],"path":["api","v2","listings"],"query":[{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"page","value":"1","description":"Page number (default 1).","disabled":true}]},"description":"Returns a paginated list of channel listings (products listed on sales channels).\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Channel state.** Every row carries `remote_status` (the reconciled channel-side state) plus its channel-agnostic reading: `remote_status_label` (\"Live\", \"Publishing\", \"Ended on channel\", \"Unpublished on channel\", \"Rejected\", \"Removing\", \"Unknown\"), `remote_status_color`, `ended_on_channel` (true when the channel no longer hosts the listing) and `remote_status_reason` (the channel's own explanation when it gave one, e.g. \"This item is unpublished because the End Date has passed\"). Filter with `filter[remote_status]`; `missing` finds every listing ended on its channel across all channels.\n\n**Amazon brand monitoring.** Amazon listings of watched brand ASINs also carry the latest offer census: `doc_amazon_offer_count` (distinct sellers; null unless Competition monitoring is on for the brand), `doc_amazon_offer_count_capped` (Amazon returned its 20-offer maximum), `doc_amazon_prime_offer_count`, `doc_amazon_fba_offer_count`, `doc_amazon_our_offer_present`, `doc_amazon_retail_present`, `doc_amazon_map_violation`, `doc_amazon_map_price`, `doc_amazon_lowest_price` and `doc_amazon_offer_observed_at`. They are null for listings that are not watched. Filter with `filter[doc_amazon_map_violation]`, `filter[doc_amazon_retail_present]`, `filter[doc_amazon_our_offer_present]`, or numerically on `doc_amazon_offer_count`, `doc_amazon_prime_offer_count`, `doc_amazon_map_price` and `doc_amazon_lowest_price` through filter_groups."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listings?per_page=10&page=1","host":["{{baseUrl}}"],"path":["api","v2","listings"],"query":[{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"page","value":"1","description":"Page number (default 1).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"product_id\": 15,\n      \"sales_channel_id\": 5,\n      \"channel_product_id\": \"shopify-12345\",\n      \"status\": \"active\",\n      \"remote_status\": \"missing\",\n      \"remote_status_label\": \"Ended on channel\",\n      \"remote_status_color\": \"grey-darken-1\",\n      \"ended_on_channel\": true,\n      \"remote_status_reason\": \"This item is unpublished because the End Date has passed.\",\n      \"remote_status_checked_at\": \"2026-09-16T00:00:52+00:00\",\n      \"doc_amazon_offer_count\": 7,\n      \"doc_amazon_offer_count_capped\": false,\n      \"doc_amazon_prime_offer_count\": 5,\n      \"doc_amazon_fba_offer_count\": 5,\n      \"doc_amazon_our_offer_present\": true,\n      \"doc_amazon_retail_present\": false,\n      \"doc_amazon_map_violation\": true,\n      \"doc_amazon_map_price\": 39.95,\n      \"doc_amazon_lowest_price\": 36.99,\n      \"doc_amazon_offer_observed_at\": \"2026-10-03T14:07:00+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 20,\n  \"per_page\": 10,\n  \"total\": 195\n}"}]},{"name":"Listings Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listings/summary","host":["{{baseUrl}}"],"path":["api","v2","listings","summary"]},"description":"Returns summary counts for listings by status.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listings/summary","host":["{{baseUrl}}"],"path":["api","v2","listings","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total\": 195,\n    \"active\": 180,\n    \"archived\": 15\n  }\n}"}]},{"name":"Bulk Archive Listings","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listings/bulk-archive","host":["{{baseUrl}}"],"path":["api","v2","listings","bulk-archive"]},"description":"Archives multiple listings.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listings/bulk-archive","host":["{{baseUrl}}"],"path":["api","v2","listings","bulk-archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 listings archived successfully.\"\n}"}]},{"name":"Bulk Unarchive Listings","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listings/bulk-unarchive","host":["{{baseUrl}}"],"path":["api","v2","listings","bulk-unarchive"]},"description":"Unarchives multiple listings.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listings/bulk-unarchive","host":["{{baseUrl}}"],"path":["api","v2","listings","bulk-unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 listings unarchived successfully.\"\n}"}]},{"name":"Fetch Listings for Bulk Edit","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listings/bulk-edit-fetch?ids=1201,1202,1203","host":["{{baseUrl}}"],"path":["api","v2","listings","bulk-edit-fetch"],"query":[{"key":"ids","value":"1201,1202,1203","description":"Comma-separated listing ids to fetch","disabled":true}]},"description":"Fetches a specific set of listings by id together with their current inventory settings, ready to populate a bulk-edit form. Pass the listing ids as a comma-separated string. For each listing you get its SKU, title, FBA flag, integration instance name, master-of-stock source, selected warehouse ids, fulfillment latency and whether it inherits settings from its integration.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listings/bulk-edit-fetch?ids=1201,1202,1203","host":["{{baseUrl}}"],"path":["api","v2","listings","bulk-edit-fetch"],"query":[{"key":"ids","value":"1201,1202,1203","description":"Comma-separated listing ids to fetch","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1201,\n      \"listing_sku\": \"ACME-WIDGET-BLU\",\n      \"title\": \"Acme Widget - Blue\",\n      \"is_fba\": false,\n      \"integration_instance_name\": \"Shopify US\",\n      \"master_of_stock\": \"sku.io\",\n      \"warehouse_ids\": [\n        3,\n        7\n      ],\n      \"fulfillment_latency\": 2,\n      \"inherit_settings\": false\n    },\n    {\n      \"id\": 1202,\n      \"listing_sku\": \"ACME-WIDGET-RED\",\n      \"title\": \"Acme Widget - Red\",\n      \"is_fba\": false,\n      \"integration_instance_name\": \"Shopify US\",\n      \"master_of_stock\": null,\n      \"warehouse_ids\": [],\n      \"fulfillment_latency\": null,\n      \"inherit_settings\": true\n    }\n  ],\n  \"status\": \"success\"\n}"}]}]},{"name":"Product Listings","item":[{"name":"List Channel Inventory","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listings/channel-inventory","host":["{{baseUrl}}"],"path":["api","v2","listings","channel-inventory"]},"description":"Returns paginated channel inventory, aggregated to one row per listing across all of that listing's channel locations. Each row compares the quantity calculated by SKU.io (total_sku_quantity) against the quantity last reported by the sales channel (total_channel_quantity) and exposes the variance between them. Archived listings are excluded.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRows are ordered by listing ID descending; custom sorting is not supported on this endpoint.\n\nFilters: filter[search] (listing SKU / title / channel listing ID / product SKU; numeric values also match the listing ID), filter[integration_instance_id], filter[integration_id], filter[is_fba] (true/false), filter[is_sku_recalculation_needed] (true/false), filter[sync_status] (in_sync, mismatch, never_synced, pending_recalc), filter[product_listing_id].\n\nPagination: page, per_page (default: 15).\n\nmaster_of_stock resolves the effective value: a listing-level override wins, otherwise the value is inherited from the channel account's settings.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listings/channel-inventory","host":["{{baseUrl}}"],"path":["api","v2","listings","channel-inventory"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 4521,\n      \"product_listing_id\": 4521,\n      \"total_sku_quantity\": 120,\n      \"total_channel_quantity\": 118,\n      \"total_variance\": 2,\n      \"location_count\": 2,\n      \"any_recalc_needed\": false,\n      \"oldest_synced_at\": \"2026-07-05 14:22:10\",\n      \"latest_sku_qty_updated\": \"2026-07-06 02:15:44\",\n      \"listing_sku\": \"WIDGET-BLUE-01\",\n      \"listing_title\": \"Blue Widget - 2 Pack\",\n      \"sales_channel_listing_id\": \"B0B12345XY\",\n      \"is_fba\": false,\n      \"master_of_stock\": \"sku.io\",\n      \"product_id\": 42,\n      \"product_sku\": \"WIDGET-BLUE\",\n      \"product_name\": \"Blue Widget\",\n      \"integration_id\": 3,\n      \"integration_name\": \"Amazon\",\n      \"integration_instance_id\": 7,\n      \"integration_instance_name\": \"Amazon US\",\n      \"created_at\": \"2025-11-02T09:14:33.000000Z\",\n      \"updated_at\": \"2026-07-06T02:15:44.000000Z\"\n    }\n  ],\n  \"first_page_url\": \"https://example.sku.io/api/v2/listings/channel-inventory?page=1\",\n  \"from\": 1,\n  \"last_page\": 349,\n  \"last_page_url\": \"https://example.sku.io/api/v2/listings/channel-inventory?page=349\",\n  \"links\": [\n    {\n      \"url\": null,\n      \"label\": \"&laquo; Previous\",\n      \"active\": false\n    },\n    {\n      \"url\": \"https://example.sku.io/api/v2/listings/channel-inventory?page=1\",\n      \"label\": \"1\",\n      \"active\": true\n    },\n    {\n      \"url\": \"https://example.sku.io/api/v2/listings/channel-inventory?page=2\",\n      \"label\": \"Next &raquo;\",\n      \"active\": false\n    }\n  ],\n  \"next_page_url\": \"https://example.sku.io/api/v2/listings/channel-inventory?page=2\",\n  \"path\": \"https://example.sku.io/api/v2/listings/channel-inventory\",\n  \"per_page\": 15,\n  \"prev_page_url\": null,\n  \"to\": 15,\n  \"total\": 5231\n}"}]},{"name":"Export Channel Inventory","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listings/channel-inventory/export","host":["{{baseUrl}}"],"path":["api","v2","listings","channel-inventory","export"]},"description":"Streams a CSV file download of channel inventory records (one row per listing location, not aggregated per listing). Archived listings are excluded.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe response is a file download with Content-Type text/csv and a Content-Disposition attachment filename of the form channel-inventory-YYYY-MM-DD.csv.\n\nColumns: ID, Listing SKU, Listing ID, Title, Product SKU, Product Name, Integration, Channel, Location ID, SKU Qty, Channel Qty, Variance, Needs Recalc, FBA, Master of Stock, SKU Qty Updated, Last Synced.\n\nSupported filters: filter[search], filter[integration_instance_id], filter[integration_id], filter[sync_status] (in_sync, mismatch, never_synced, pending_recalc).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listings/channel-inventory/export","host":["{{baseUrl}}"],"path":["api","v2","listings","channel-inventory","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"ID,Listing SKU,Listing ID,Title,Product SKU,Product Name,Integration,Channel,Location ID,SKU Qty,Channel Qty,Variance,Needs Recalc,FBA,Master of Stock,SKU Qty Updated,Last Synced\n88213,WIDGET-BLUE-01,B0B12345XY,Blue Widget - 2 Pack,WIDGET-BLUE,Blue Widget,Amazon,Amazon US,12345678,120,118,2,No,No,sku.io,2026-07-06 02:15:44,2026-07-05 14:22:10\n"}]},{"name":"Get Channel Inventory Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listings/channel-inventory/summary","host":["{{baseUrl}}"],"path":["api","v2","listings","channel-inventory","summary"]},"description":"Returns channel inventory counts grouped by integration, the list of connected channel accounts (integration instances) that have at least one active listing, and a breakdown of inventory records by sync status.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nstatus_counts fields: in_sync (SKU.io and channel quantities match and no recalculation is pending), mismatch (quantities differ or a recalculation is pending), never_synced (the channel has never reported a quantity), pending_recalc (flagged for quantity recalculation).\n\nIntegrations with listings but no channel inventory records yet (fresh setup) still appear with a count of 0. Archived listings are excluded throughout.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listings/channel-inventory/summary","host":["{{baseUrl}}"],"path":["api","v2","listings","channel-inventory","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total\": 5231,\n    \"integrations\": [\n      {\n        \"integration_name\": \"Amazon\",\n        \"integration_id\": 3,\n        \"count\": 3120\n      },\n      {\n        \"integration_name\": \"Shopify\",\n        \"integration_id\": 5,\n        \"count\": 1685\n      },\n      {\n        \"integration_name\": \"Walmart\",\n        \"integration_id\": 9,\n        \"count\": 426\n      }\n    ],\n    \"instances\": [\n      {\n        \"integration_instance_id\": 7,\n        \"integration_instance_name\": \"Amazon US\",\n        \"integration_id\": 3\n      },\n      {\n        \"integration_instance_id\": 8,\n        \"integration_instance_name\": \"Shopify Main Store\",\n        \"integration_id\": 5\n      },\n      {\n        \"integration_instance_id\": 12,\n        \"integration_instance_name\": \"Walmart US\",\n        \"integration_id\": 9\n      }\n    ],\n    \"status_counts\": {\n      \"in_sync\": 4655,\n      \"mismatch\": 310,\n      \"never_synced\": 221,\n      \"pending_recalc\": 45\n    }\n  }\n}"}]},{"name":"Export Listings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listings/export","host":["{{baseUrl}}"],"path":["api","v2","listings","export"]},"description":"Streams a CSV file download of all non-archived listings across every connected sales channel. This endpoint does not accept filters — it always exports the full active listing set, ordered by most recently created first.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe response is a file download with Content-Type text/csv and a Content-Disposition attachment filename of the form listings-YYYY-MM-DD.csv.\n\nColumns: ID, Listing SKU, Listing ID, Title, Product SKU, Product Name, Integration, Channel, Price, Quantity, FBA, Master of Price, Master of Stock, Inherit from Integration, Warehouses, Fulfillment Latency (days), Max Rule Type, Max Rule Value, Min Rule Type, Min Rule Value, Buffer Stock, ASIN, Shopify Status, Walmart WPID, Archived At, Created At, Updated At.\n\nWarehouse IDs from each listing's inventory settings are resolved to warehouse names.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listings/export","host":["{{baseUrl}}"],"path":["api","v2","listings","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"ID,Listing SKU,Listing ID,Title,Product SKU,Product Name,Integration,Channel,Price,Quantity,FBA,Master of Price,Master of Stock,Inherit from Integration,Warehouses,Fulfillment Latency (days),Max Rule Type,Max Rule Value,Min Rule Type,Min Rule Value,Buffer Stock,ASIN,Shopify Status,Walmart WPID,Archived At,Created At,Updated At\n4521,WIDGET-BLUE-01,B0B12345XY,Blue Widget - 2 Pack,WIDGET-BLUE,Blue Widget,Amazon,Amazon US,24.99,120,No,Inherited,sku.io,No,\"Main Warehouse, East Coast FC\",2,Fixed Allocation,100,None,,10,B0B12345XY,,,,2025-11-02T09:14:33.000000Z,2026-07-06T02:15:44.000000Z\n"}]},{"name":"Download Inventory Settings Import Template","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listings/inventory-settings-import-template","host":["{{baseUrl}}"],"path":["api","v2","listings","inventory-settings-import-template"]},"description":"Downloads an XLSX import template for bulk-editing listing inventory settings via spreadsheet import.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe workbook contains sample rows (a full settings override, a partial update, and a reset-to-inherit example) plus a reference sheet listing the valid values for your account: warehouse names, connected channel names, master-of-stock values (sku.io, sales_channel, neither), rule types (None, Fixed Allocation, % of Available), and up to 50 sample listing SKUs.\n\nThe response is a file download with Content-Type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet and a filename of the form listing-inventory-settings-import-template-YYYY-MM-DD.xlsx.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listings/inventory-settings-import-template","host":["{{baseUrl}}"],"path":["api","v2","listings","inventory-settings-import-template"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary XLSX file download)"}]},{"name":"List Listing Inventory Caches","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listings/{productListing}/inventory-caches","host":["{{baseUrl}}"],"path":["api","v2","listings",":productListing","inventory-caches"],"variable":[{"key":"productListing","value":"4521","description":"Product listing ID"}]},"description":"Returns the per-location channel inventory records for a single listing, ordered by channel location ID. Each record compares the quantity calculated by SKU.io (sku_quantity) against the quantity last reported by the sales channel (sales_channel_quantity) and includes the variance.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nlocation_name is resolved from the channel account's configured inventory locations and is null when the location is not configured there. variance is null when the channel has never reported a quantity.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listings/{productListing}/inventory-caches","host":["{{baseUrl}}"],"path":["api","v2","listings",":productListing","inventory-caches"],"variable":[{"key":"productListing","value":"4521","description":"Product listing ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 88213,\n      \"sales_channel_location_id\": \"12345678\",\n      \"location_name\": \"Default Location\",\n      \"sku_quantity\": 120,\n      \"sales_channel_quantity\": 118,\n      \"variance\": 2,\n      \"is_sku_recalculation_needed\": false,\n      \"sku_quantity_updated_at\": \"2026-07-06T02:15:44.000000Z\",\n      \"sales_channel_quantity_synced_at\": \"2026-07-05T14:22:10.000000Z\"\n    },\n    {\n      \"id\": 88214,\n      \"sales_channel_location_id\": \"87654321\",\n      \"location_name\": \"East Coast FC\",\n      \"sku_quantity\": 45,\n      \"sales_channel_quantity\": null,\n      \"variance\": null,\n      \"is_sku_recalculation_needed\": true,\n      \"sku_quantity_updated_at\": \"2026-07-06T02:15:44.000000Z\",\n      \"sales_channel_quantity_synced_at\": null\n    }\n  ]\n}"}]},{"name":"Preview Bulk Listing Inventory Settings Update","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listings/bulk-edit-inventory-preview","host":["{{baseUrl}}"],"path":["api","v2","listings","bulk-edit-inventory-preview"]},"description":"Dry run for Bulk Update Listing Inventory Settings. Computes exactly which listings would change and what each field's old and new values would be, without applying anything. Use it to validate a bulk update before committing it.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody fields:\n- ids (required, array of integers) — listing IDs to evaluate\n- inventory_settings (required, object) — the settings that would be applied\n\ninventory_settings fields (all optional within the object):\n- inherit (boolean) — when true, clears all listing-level overrides so the listing inherits settings from its channel account; all other settings fields are ignored\n- master_of_stock (string|null) — one of: sku.io, sales_channel, amazon, neither\n- warehouse_ids (array of integers|null) — warehouse IDs used for the listing's available quantity\n- warehouse_action (string) — how warehouse_ids are applied: replace (default), add, or clear\n- fulfillment_latency (integer|null, 0-30) — handling time in days\n- max_rule_type (string|null) — one of: None, Fixed Allocation, % of Available\n- max_rule_value (number|null, min 0) — value paired with max_rule_type\n- min_rule_type (string|null) — one of: None, Fixed Allocation, % of Available\n- min_rule_value (number|null, min 0) — value paired with min_rule_type\n- subtract_buffer_stock (integer|null, min 0) — buffer quantity subtracted from availability\n\nFBA listings are excluded from the evaluation (their inventory is managed by the fulfillment network); the count of excluded listings is returned as skipped_fba. Listings whose settings already match report no changes and are not included in the changes array.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    4521,\n    4522,\n    4523\n  ],\n  \"inventory_settings\": {\n    \"inherit\": false,\n    \"master_of_stock\": \"sku.io\",\n    \"warehouse_ids\": [\n      1,\n      4\n    ],\n    \"warehouse_action\": \"replace\",\n    \"fulfillment_latency\": 2,\n    \"max_rule_type\": \"Fixed Allocation\",\n    \"max_rule_value\": 100,\n    \"min_rule_type\": \"None\",\n    \"min_rule_value\": null,\n    \"subtract_buffer_stock\": 10\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listings/bulk-edit-inventory-preview","host":["{{baseUrl}}"],"path":["api","v2","listings","bulk-edit-inventory-preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_listings\": 3,\n    \"affected_listings\": 2,\n    \"skipped_fba\": 1,\n    \"changes\": [\n      {\n        \"id\": 4521,\n        \"listing_sku\": \"WIDGET-BLUE-01\",\n        \"title\": \"Blue Widget - 2 Pack\",\n        \"changes\": [\n          {\n            \"field\": \"Master of Stock\",\n            \"old_value\": \"Not set\",\n            \"new_value\": \"SKU.io\"\n          },\n          {\n            \"field\": \"Fulfillment Latency\",\n            \"old_value\": \"Not set\",\n            \"new_value\": \"2 days\"\n          },\n          {\n            \"field\": \"Warehouses\",\n            \"old_value\": \"0 warehouse(s)\",\n            \"new_value\": \"2 warehouse(s) (Replace)\"\n          },\n          {\n            \"field\": \"Max Rule\",\n            \"old_value\": \"None\",\n            \"new_value\": \"Fixed Allocation: 100\"\n          },\n          {\n            \"field\": \"Buffer Stock\",\n            \"old_value\": \"0\",\n            \"new_value\": \"10\"\n          }\n        ]\n      },\n      {\n        \"id\": 4522,\n        \"listing_sku\": \"WIDGET-RED-01\",\n        \"title\": \"Red Widget - 2 Pack\",\n        \"changes\": [\n          {\n            \"field\": \"Master of Stock\",\n            \"old_value\": \"Sales Channel\",\n            \"new_value\": \"SKU.io\"\n          }\n        ]\n      }\n    ]\n  }\n}"}]},{"name":"Bulk Update Listing Inventory Settings","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listings/bulk-update-inventory-settings","host":["{{baseUrl}}"],"path":["api","v2","listings","bulk-update-inventory-settings"]},"description":"Applies inventory settings to many listings at once. Runs synchronously — the response reports the number of listings updated.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nTargeting (one of):\n- ids (array of integers, required unless apply_to_all is true) — explicit listing IDs\n- apply_to_all: true — target every listing matching the optional filters object: search (listing SKU / title / channel listing ID), integration_id, is_fba (true/false). Only non-archived listings are targeted in this mode.\n\ninventory_settings (required, object):\ninventory_settings fields (all optional within the object):\n- inherit (boolean) — when true, clears all listing-level overrides so the listing inherits settings from its channel account; all other settings fields are ignored\n- master_of_stock (string|null) — one of: sku.io, sales_channel, amazon, neither\n- warehouse_ids (array of integers|null) — warehouse IDs used for the listing's available quantity\n- warehouse_action (string) — how warehouse_ids are applied: replace (default), add, or clear\n- fulfillment_latency (integer|null, 0-30) — handling time in days\n- max_rule_type (string|null) — one of: None, Fixed Allocation, % of Available\n- max_rule_value (number|null, min 0) — value paired with max_rule_type\n- min_rule_type (string|null) — one of: None, Fixed Allocation, % of Available\n- min_rule_value (number|null, min 0) — value paired with min_rule_type\n- subtract_buffer_stock (integer|null, min 0) — buffer quantity subtracted from availability\n\nBehavior notes:\n- Only the fields present in inventory_settings are changed; omitted fields keep their current values.\n- inherit: true is destructive — it deletes all listing-level overrides so the listing inherits from its channel account; any other settings sent alongside it are ignored.\n- warehouse_action controls how warehouse_ids merge with existing selections: replace (default) overwrites, add unions, clear empties the selection.\n- FBA listings are always skipped (their inventory is managed by the fulfillment network) and do not count toward the affected total.\n\nUse Preview Bulk Listing Inventory Settings Update first for a dry run of the same payload.\n\nAuthentication: Requires Bearer token.\n\nNote: inventory_settings.inherit = true resets the listing's inventory overrides but PRESERVES any per-listing FBM Backup rule (inventory_rules.fbmBackup) — that rule is orthogonal to inventory inheritance.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    4521,\n    4522,\n    4523\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {\n    \"search\": \"WIDGET\",\n    \"integration_id\": 3,\n    \"is_fba\": false\n  },\n  \"inventory_settings\": {\n    \"inherit\": false,\n    \"master_of_stock\": \"sku.io\",\n    \"warehouse_ids\": [\n      1,\n      4\n    ],\n    \"warehouse_action\": \"replace\",\n    \"fulfillment_latency\": 2,\n    \"max_rule_type\": \"Fixed Allocation\",\n    \"max_rule_value\": 100,\n    \"min_rule_type\": \"None\",\n    \"min_rule_value\": null,\n    \"subtract_buffer_stock\": 10\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listings/bulk-update-inventory-settings","host":["{{baseUrl}}"],"path":["api","v2","listings","bulk-update-inventory-settings"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Updated inventory settings for 2 listing(s).\",\n  \"data\": {\n    \"affected\": 2\n  }\n}"}]},{"name":"Trigger Channel Inventory Recalculation","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listings/channel-inventory/trigger-recalc","host":["{{baseUrl}}"],"path":["api","v2","listings","channel-inventory","trigger-recalc"]},"description":"Flags channel inventory records for quantity recalculation. This is asynchronous: the request only marks the records; the quantities are recalculated by a scheduled background process on its next cycle, not immediately within this request.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nTargeting modes (checked in this order):\n- apply_to_all: true — flag every record matching the optional filters object (search, integration_instance_id, integration_id, is_sku_recalculation_needed). Archived listings are excluded.\n- product_listing_ids (array of integers) — flag all channel inventory records belonging to the given listing IDs.\n- ids (array of integers) — flag specific channel inventory record IDs.\n\nAll body fields are optional, but supply at least one targeting mode; otherwise 0 records are affected.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    88213,\n    88214\n  ],\n  \"product_listing_ids\": [\n    4521\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {\n    \"search\": \"WIDGET\",\n    \"integration_instance_id\": 7,\n    \"integration_id\": 3,\n    \"is_sku_recalculation_needed\": false\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listings/channel-inventory/trigger-recalc","host":["{{baseUrl}}"],"path":["api","v2","listings","channel-inventory","trigger-recalc"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Marked 3 record(s) for recalculation.\",\n  \"data\": {\n    \"affected\": 3\n  }\n}"}]},{"name":"Get Product Listing","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/product-listings/{productListing}","host":["{{baseUrl}}"],"path":["api","v2","product-listings",":productListing"],"variable":[{"key":"productListing","value":"1","description":"Product listing ID"}]},"description":"Returns a single product listing with its sales channel, integration, pricing tier, inventory cache, and document relations loaded. The integration-specific resource class is used based on the sales channel.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFBM Backup Mode (Amazon MFN listings only): send inventory_rules.fbmBackup = { inherit: bool, enabled: bool, fbaThreshold: int>=0, scope: \"same_country\"|\"all_marketplaces\" }. inherit:true (or omitting fbmBackup) inherits the Amazon integration default; inherit:false is a per-listing override. The response fbm_backup carries the EFFECTIVE config, `inherited` (bool), the `integration_default`, and (when effectively enabled) the live gate status. Enabling an override on a non-Amazon or FBA listing returns 422. fbm_backup is null only for non-Amazon-MFN listings.\n\nPricing sync profile: the response carries `pricing_sync_profile` — the profile that governs this listing's outbound price push, as `{id, name, source, is_archived}` where `source` is `listing` (the listing's own), `product` (inherited from the product) or `channel` (the channel default); null when the channel pushes with its inline settings. `pricing_sync_profile_override_id` is the listing's OWN profile (null while it inherits), and `product_pricing_sync_profile` / `channel_pricing_sync_profile` (`{id, name}` or null) are the two levels it falls back to.\n\nShared FBA pools: Amazon holds fulfillable units against the FNSKU, not the merchant SKU, so several `fba_listings` entries can point at ONE physical stack of units — they share an `integration_instance_id`, `country_code` and `fnsku`, and each is reported with that stack's full `available`. `fba_available_total` counts such a group once, which is why the entries can add up to more than the total."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/product-listings/{productListing}","host":["{{baseUrl}}"],"path":["api","v2","product-listings",":productListing"],"variable":[{"key":"productListing","value":"1","description":"Product listing ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"product_id\": 42,\n    \"product_sku\": \"WIDGET-001\",\n    \"product_name\": \"Premium Widget\",\n    \"channel_name\": \"Amazon US - Main\",\n    \"integration_name\": \"amazon\",\n    \"integration_instance_id\": 2,\n    \"listing_sku\": \"WIDGET-001-MFN\",\n    \"sales_channel_listing_id\": \"B0EXAMPLE12\",\n    \"document_id\": 4501,\n    \"listing_url\": \"https://www.amazon.com/dp/B0EXAMPLE12?psc=1\",\n    \"title\": \"Premium Widget\",\n    \"price\": 149.99,\n    \"channel_price\": 25,\n    \"channel_price_checked_at\": \"2026-09-18T17:53:21+00:00\",\n    \"is_fba\": false,\n    \"price_inherit\": false,\n    \"master_of_price\": \"sku.io\",\n    \"pricing_tier_id\": 2,\n    \"pricing_tier_name\": \"Retail\",\n    \"master_of_price_override\": \"sku.io\",\n    \"product_pricing_tier_id_override\": 2,\n    \"pricing_sync_profile\": {\n      \"id\": 3,\n      \"name\": \"MAP protected\",\n      \"source\": \"channel\",\n      \"is_archived\": false\n    },\n    \"pricing_sync_profile_override_id\": null,\n    \"product_pricing_sync_profile\": null,\n    \"channel_pricing_sync_profile\": {\n      \"id\": 3,\n      \"name\": \"MAP protected\"\n    },\n    \"stock_inherit\": true,\n    \"master_of_stock\": \"sku.io\",\n    \"fulfillment_latency\": 2,\n    \"inventory_rules\": {\n      \"fbmBackup\": {\n        \"inherit\": false,\n        \"enabled\": true,\n        \"fbaThreshold\": 0,\n        \"scope\": \"same_country\"\n      }\n    },\n    \"sales_channel_settings\": {\n      \"inventory\": {\n        \"masterOfStock\": \"sku.io\",\n        \"selectedWarehouses\": [\n          1,\n          2\n        ],\n        \"fulfillmentLatency\": 2\n      }\n    },\n    \"inventory_caches\": [\n      {\n        \"id\": 901,\n        \"sales_channel_location_id\": 0,\n        \"sku_quantity\": 50,\n        \"sales_channel_quantity\": 0,\n        \"is_sku_recalculation_needed\": false,\n        \"sku_quantity_updated_at\": \"2026-07-16T10:00:00.000000Z\",\n        \"sales_channel_quantity_updated_at\": \"2026-07-16T10:04:00.000000Z\",\n        \"sales_channel_quantity_synced_at\": \"2026-07-16T10:04:00.000000Z\"\n      }\n    ],\n    \"quantity\": 50,\n    \"fbm_backup\": {\n      \"enabled\": true,\n      \"fba_threshold\": 0,\n      \"scope\": \"same_country\",\n      \"inherited\": false,\n      \"integration_default\": {\n        \"enabled\": true,\n        \"fbaThreshold\": 0,\n        \"scope\": \"same_country\"\n      },\n      \"gate_active\": true,\n      \"fba_available_total\": 142,\n      \"pushed_quantity\": 0,\n      \"warning\": null,\n      \"fba_listings\": [\n        {\n          \"listing_id\": 4512,\n          \"listing_sku\": \"WIDGET-001-FBA\",\n          \"country_code\": \"US\",\n          \"integration_instance_id\": 2,\n          \"fnsku\": \"X001ABCDEF\",\n          \"available\": 142\n        },\n        {\n          \"listing_id\": 4519,\n          \"listing_sku\": \"WIDGET-001-FBA-OLD\",\n          \"country_code\": \"US\",\n          \"integration_instance_id\": 2,\n          \"fnsku\": \"X001ABCDEF\",\n          \"available\": 142\n        }\n      ]\n    },\n    \"archived_at\": null,\n    \"created_at\": \"2026-01-15T08:30:00.000000Z\",\n    \"updated_at\": \"2026-07-16T10:04:00.000000Z\"\n  }\n}"}]},{"name":"Update Product Listing","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/product-listings/{productListing}","host":["{{baseUrl}}"],"path":["api","v2","product-listings",":productListing"],"variable":[{"key":"productListing","value":"1","description":"Product listing ID"}]},"description":"Updates a product listing. If master_of_price or product_pricing_tier_id change, the price is pushed to the sales channel.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nmaster_of_price / master_of_stock accept sku.io, sales_channel, or neither (null inherits). The effective master_of_stock is driven by inventory_rules.masterOfStock.\n\nAuthentication: Requires Bearer token.\n\nFBM Backup Mode (Amazon MFN listings only): send inventory_rules.fbmBackup = { inherit: bool, enabled: bool, fbaThreshold: int>=0, scope: \"same_country\"|\"all_marketplaces\" }. inherit:true (or omitting fbmBackup) inherits the Amazon integration default; inherit:false is a per-listing override. The response fbm_backup carries the EFFECTIVE config, `inherited` (bool), the `integration_default`, and (when effectively enabled) the live gate status. Enabling an override on a non-Amazon or FBA listing returns 422. fbm_backup is null only for non-Amazon-MFN listings.\n\nInventory modification rules: send inventory_rules.inventoryModificationRules = { maxRuleType: string, maxRuleTypeValue: numeric|null, minRuleType: string, minRuleTypeValue: numeric|null, subtractBufferStock: numeric|null }. All value fields are nullable; subtractBufferStock subtracts a buffer from the quantity pushed to the channel (null/0 = no buffer).\n\nPricing sync profile: send `pricing_sync_profile_id` (integer|null) to give the listing its own outbound price-push profile, or null to clear it so the listing falls back to its product's profile, then the channel default. A change re-prices this listing immediately. This never changes master_of_price — the master decides WHETHER a listing pushes, the profile decides WHAT is sent. Assigning an ARCHIVED profile is refused with 422 on `pricing_sync_profile_id` (re-sending the profile the listing already carries is tolerated, so a listing whose profile was archived after assignment still saves).\n\nPricing sync profile: the response carries `pricing_sync_profile` — the profile that governs this listing's outbound price push, as `{id, name, source, is_archived}` where `source` is `listing` (the listing's own), `product` (inherited from the product) or `channel` (the channel default); null when the channel pushes with its inline settings. `pricing_sync_profile_override_id` is the listing's OWN profile (null while it inherits), and `product_pricing_sync_profile` / `channel_pricing_sync_profile` (`{id, name}` or null) are the two levels it falls back to.\n\nShared FBA pools: Amazon holds fulfillable units against the FNSKU, not the merchant SKU, so several `fba_listings` entries can point at ONE physical stack of units — they share an `integration_instance_id`, `country_code` and `fnsku`, and each is reported with that stack's full `available`. `fba_available_total` counts such a group once, which is why the entries can add up to more than the total.","body":{"mode":"raw","raw":"{\n  \"master_of_price\": \"sku.io\",\n  \"product_pricing_tier_id\": 2,\n  \"pricing_sync_profile_id\": 2,\n  \"inventory_rules\": {\n    \"masterOfStock\": \"sku.io\",\n    \"selectedWarehouses\": [\n      1,\n      2\n    ],\n    \"inventoryModificationRules\": {\n      \"maxRuleType\": \"None\",\n      \"maxRuleTypeValue\": null,\n      \"minRuleType\": \"None\",\n      \"minRuleTypeValue\": null,\n      \"subtractBufferStock\": null\n    },\n    \"fbmBackup\": {\n      \"inherit\": false,\n      \"enabled\": true,\n      \"fbaThreshold\": 0,\n      \"scope\": \"same_country\"\n    }\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/product-listings/{productListing}","host":["{{baseUrl}}"],"path":["api","v2","product-listings",":productListing"],"variable":[{"key":"productListing","value":"1","description":"Product listing ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Product listing updated successfully.\",\n  \"data\": {\n    \"id\": 1,\n    \"product_id\": 42,\n    \"product_sku\": \"WIDGET-001\",\n    \"product_name\": \"Premium Widget\",\n    \"channel_name\": \"Amazon US - Main\",\n    \"integration_name\": \"amazon\",\n    \"integration_instance_id\": 2,\n    \"listing_sku\": \"WIDGET-001-MFN\",\n    \"sales_channel_listing_id\": \"B0EXAMPLE12\",\n    \"document_id\": 4501,\n    \"listing_url\": \"https://www.amazon.com/dp/B0EXAMPLE12?psc=1\",\n    \"title\": \"Premium Widget\",\n    \"price\": 149.99,\n    \"is_fba\": false,\n    \"price_inherit\": false,\n    \"master_of_price\": \"sku.io\",\n    \"pricing_tier_id\": 2,\n    \"pricing_tier_name\": \"Retail\",\n    \"master_of_price_override\": \"sku.io\",\n    \"product_pricing_tier_id_override\": 2,\n    \"pricing_sync_profile\": {\n      \"id\": 2,\n      \"name\": \"MSRP ceiling\",\n      \"source\": \"listing\",\n      \"is_archived\": false\n    },\n    \"pricing_sync_profile_override_id\": 2,\n    \"product_pricing_sync_profile\": null,\n    \"channel_pricing_sync_profile\": {\n      \"id\": 3,\n      \"name\": \"MAP protected\"\n    },\n    \"stock_inherit\": false,\n    \"master_of_stock\": \"sku.io\",\n    \"fulfillment_latency\": null,\n    \"inventory_rules\": {\n      \"masterOfStock\": \"sku.io\",\n      \"selectedWarehouses\": [\n        1,\n        2\n      ],\n      \"inventoryModificationRules\": {\n        \"maxRuleType\": \"None\",\n        \"maxRuleTypeValue\": null,\n        \"minRuleType\": \"None\",\n        \"minRuleTypeValue\": null,\n        \"subtractBufferStock\": null\n      },\n      \"fbmBackup\": {\n        \"inherit\": false,\n        \"enabled\": true,\n        \"fbaThreshold\": 0,\n        \"scope\": \"same_country\"\n      }\n    },\n    \"sales_channel_settings\": {\n      \"inventory\": {\n        \"masterOfStock\": \"sku.io\",\n        \"selectedWarehouses\": [\n          1,\n          2\n        ],\n        \"fulfillmentLatency\": 2\n      }\n    },\n    \"inventory_caches\": [\n      {\n        \"id\": 901,\n        \"sales_channel_location_id\": 0,\n        \"sku_quantity\": 50,\n        \"sales_channel_quantity\": 0,\n        \"is_sku_recalculation_needed\": false,\n        \"sku_quantity_updated_at\": \"2026-07-16T10:00:00.000000Z\",\n        \"sales_channel_quantity_updated_at\": \"2026-07-16T10:04:00.000000Z\",\n        \"sales_channel_quantity_synced_at\": \"2026-07-16T10:04:00.000000Z\"\n      }\n    ],\n    \"quantity\": 50,\n    \"fbm_backup\": {\n      \"enabled\": true,\n      \"fba_threshold\": 0,\n      \"scope\": \"same_country\",\n      \"inherited\": false,\n      \"integration_default\": {\n        \"enabled\": true,\n        \"fbaThreshold\": 0,\n        \"scope\": \"same_country\"\n      },\n      \"gate_active\": true,\n      \"fba_available_total\": 142,\n      \"pushed_quantity\": 0,\n      \"warning\": null,\n      \"fba_listings\": [\n        {\n          \"listing_id\": 4512,\n          \"listing_sku\": \"WIDGET-001-FBA\",\n          \"country_code\": \"US\",\n          \"integration_instance_id\": 2,\n          \"fnsku\": \"X001ABCDEF\",\n          \"available\": 142\n        },\n        {\n          \"listing_id\": 4519,\n          \"listing_sku\": \"WIDGET-001-FBA-OLD\",\n          \"country_code\": \"US\",\n          \"integration_instance_id\": 2,\n          \"fnsku\": \"X001ABCDEF\",\n          \"available\": 142\n        }\n      ]\n    },\n    \"archived_at\": null,\n    \"created_at\": \"2026-01-15T08:30:00.000000Z\",\n    \"updated_at\": \"2026-07-16T10:04:00.000000Z\"\n  }\n}"}]},{"name":"List Drafts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listing-drafts?page=1&per_page=25","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts"],"query":[{"key":"page","value":"1","description":"Page number.","disabled":true},{"key":"per_page","value":"25","description":"Items per page (max 100, default 25).","disabled":true}]},"description":"Paginated listing drafts for the drafts list. Slim payload — the heavy `fields[]` array is only served by show/update/validate.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nSearch covers draft ID, product name/SKU, and channel name (resolved through the channel's integration instance). Advanced `filter_groups` trees support nested AND/OR groups over the columns listed on the parameter.\n\nAuthentication: Requires Bearer token.\n\n**variant_context** — describes whether this draft targets a single VARIANT of a matrix family rather than the family itself. When `is_variant_child` is `true`, `parent_sku` / `parent_name` identify the family and `sibling_count` is how many variants it has. Publishing a single child is permitted, but it creates a standalone channel item with no variant group — publish the parent to list the whole family as one grouped listing. `null` when the draft has no product."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listing-drafts?page=1&per_page=25","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts"],"query":[{"key":"page","value":"1","description":"Page number.","disabled":true},{"key":"per_page","value":"25","description":"Items per page (max 100, default 25).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 181,\n      \"product_id\": 99,\n      \"sales_channel_id\": 34,\n      \"sales_channel_category_id\": 26,\n      \"category_provenance\": \"manual\",\n      \"sales_channel_product_template_id\": null,\n      \"sales_channel_listing_profile_id\": null,\n      \"product_listing_id\": null,\n      \"purpose\": \"create\",\n      \"state\": \"ready\",\n      \"scheduled_at\": null,\n      \"image_urls\": [],\n      \"validation_errors\": null,\n      \"channel_errors\": null,\n      \"published_at\": null,\n      \"created_at\": \"2026-07-06T05:16:00+00:00\",\n      \"updated_at\": \"2026-07-06T07:19:00+00:00\",\n      \"product\": {\n        \"id\": 99,\n        \"name\": \"Tiny Threads Cardigan\",\n        \"sku\": \"TT019\"\n      },\n      \"variant_context\": {\n        \"is_variant_child\": false,\n        \"parent_id\": null,\n        \"parent_sku\": null,\n        \"parent_name\": null,\n        \"sibling_count\": 0\n      },\n      \"sales_channel\": {\n        \"id\": 34,\n        \"name\": \"BigCommerce2\",\n        \"integration_name\": \"BigCommerce\"\n      },\n      \"category\": {\n        \"id\": 26,\n        \"name\": \"General (Baseline)\",\n        \"path\": \"General (Baseline)\",\n        \"external_id\": \"23\"\n      },\n      \"variation_theme\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 11,\n  \"per_page\": 25,\n  \"total\": 259,\n  \"from\": 1,\n  \"to\": 25\n}"}]},{"name":"Create Draft","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listing-drafts","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts"]},"description":"Create a listing draft.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields:\n- `purpose` (required, `create` | `update`) — `create` builds a new-listing draft; `update` seeds a draft from an existing live listing.\n- `product_id` (required if purpose=create, integer) — product to list.\n- `sales_channel_id` (required if purpose=create, integer) — target channel.\n- `sales_channel_category_id` (nullable, integer) — channel category driving the field schema. Omit it to have the product's channel category resolved automatically: the product's own override on this channel, else the category its product type is mapped to on the channel (that category's default template is used unless `sales_channel_product_template_id` is given). A draft that still has no category records a `missing_category` validation error and stays in `draft` state — it can never reach `ready` until a category is set.\n- `category_provenance` (nullable, `override` | `type_map` | `manual`) — where a supplied `sales_channel_category_id` came from, so the draft records it; pass the value returned by Resolve Draft Categories or List Product Channel Categories. A category supplied without it is recorded as `manual`; a category resolved automatically records its own provenance. Echoed as `category_provenance` in the response (null when the draft has no category).\n- `sales_channel_product_template_id` (nullable, integer) — template pre-mapping category fields. Optional: a draft is fully publishable without one (fill fields via overrides).\n- `sales_channel_listing_profile_id` (nullable, integer) — channel listing profile.\n- `product_listing_id` (required if purpose=update, integer) — live listing to seed from.\n\nThe draft is resolved + validated on creation; the response includes `fields[]` (schema + resolved value + provenance + raw `override`), `resolved_values` and `validation_errors`.\n\nAuthentication: Requires Bearer token.\n\n**variant_context** — describes whether this draft targets a single VARIANT of a matrix family rather than the family itself. When `is_variant_child` is `true`, `parent_sku` / `parent_name` identify the family and `sibling_count` is how many variants it has. Publishing a single child is permitted, but it creates a standalone channel item with no variant group — publish the parent to list the whole family as one grouped listing. `null` when the draft has no product.\n\nEach field's `schema` reports the channel's constraints. `cardinality` is the MAXIMUM number of values the field accepts (0 = unbounded) and `min_cardinality` is the MINIMUM (0 = none). A channel can require several entries on one field - Walmart's Cloth Baby Diapers category asks for at least 4 Key Features and 4 additional images - and rejects the item after accepting the submission, so supply at least `min_cardinality` values before publishing.\n\n**Refusals (422):** the draft is refused, with the reason in `message`, when the product is outside the channel's catalog, or when the account was connected through the Shopify App Store and the channel is a marketplace (Amazon, eBay, Walmart, Temu, TikTok Shop, Faire) or the product is already listed on a different Shopify store. Update drafts (`purpose: update`) follow the same rules.","body":{"mode":"raw","raw":"{\n  \"purpose\": \"create\",\n  \"product_id\": 88,\n  \"sales_channel_id\": 30,\n  \"sales_channel_category_id\": 7,\n  \"category_provenance\": \"manual\",\n  \"sales_channel_product_template_id\": null,\n  \"sales_channel_listing_profile_id\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listing-drafts","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 42,\n  \"product_id\": 88,\n  \"sales_channel_id\": 30,\n  \"sales_channel_category_id\": 7,\n  \"category_provenance\": \"manual\",\n  \"sales_channel_product_template_id\": null,\n  \"sales_channel_listing_profile_id\": null,\n  \"product_listing_id\": null,\n  \"purpose\": \"create\",\n  \"state\": \"ready\",\n  \"scheduled_at\": null,\n  \"image_urls\": [\n    \"https://cdn.example.com/img/beanie-1.jpg\"\n  ],\n  \"validation_errors\": null,\n  \"channel_errors\": null,\n  \"published_at\": null,\n  \"created_at\": \"2026-07-06T12:00:00+00:00\",\n  \"updated_at\": \"2026-07-06T12:05:00+00:00\",\n  \"product\": {\n    \"id\": 88,\n    \"name\": \"Tiny Threads Beanie\",\n    \"sku\": \"TT008\"\n  },\n  \"variant_context\": {\n    \"is_variant_child\": false,\n    \"parent_id\": null,\n    \"parent_sku\": null,\n    \"parent_name\": null,\n    \"sibling_count\": 0\n  },\n  \"sales_channel\": {\n    \"id\": 30,\n    \"name\": \"eBay Store\",\n    \"integration_name\": \"Ebay\"\n  },\n  \"category\": {\n    \"id\": 7,\n    \"name\": \"Hoodies\",\n    \"path\": \"QA / Hoodies\",\n    \"external_id\": \"155184\"\n  },\n  \"template\": null,\n  \"listing_profile\": null,\n  \"variation_theme\": null,\n  \"variation_data\": null,\n  \"fields\": [\n    {\n      \"name\": \"Brand\",\n      \"label\": \"Brand\",\n      \"schema\": {\n        \"type\": \"text\",\n        \"usage\": \"required\",\n        \"cardinality\": 1,\n        \"min_cardinality\": 0,\n        \"allowed_values\": null,\n        \"allows_custom_value\": true,\n        \"max_length\": null,\n        \"min_length\": null,\n        \"object_schema\": null\n      },\n      \"value\": \"Tiny Threads\",\n      \"provenance\": \"override\",\n      \"has_override\": true,\n      \"override\": {\n        \"source_type\": \"product_field\",\n        \"source_value\": \"brand\"\n      },\n      \"errors\": []\n    },\n    {\n      \"name\": \"Color\",\n      \"label\": \"Color\",\n      \"schema\": {\n        \"type\": \"text\",\n        \"usage\": \"required\",\n        \"cardinality\": 1,\n        \"min_cardinality\": 0,\n        \"allowed_values\": null,\n        \"allows_custom_value\": true,\n        \"max_length\": null,\n        \"min_length\": null,\n        \"object_schema\": null\n      },\n      \"value\": \"Sage\",\n      \"provenance\": \"template\",\n      \"has_override\": false,\n      \"override\": null,\n      \"errors\": []\n    }\n  ],\n  \"field_overrides\": {\n    \"Brand\": {\n      \"source_type\": \"product_field\",\n      \"source_value\": \"brand\"\n    }\n  },\n  \"resolved_values\": {\n    \"Brand\": {\n      \"value\": \"Tiny Threads\",\n      \"provenance\": \"override\"\n    },\n    \"Color\": {\n      \"value\": \"Sage\",\n      \"provenance\": \"template\"\n    }\n  }\n}"}]},{"name":"List Mappable Product Fields","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/mappable-product-fields","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts","mappable-product-fields"]},"description":"Product columns you can use as a `product_field` mapping source in listing-draft field overrides and template field defaults. The list is served from the same definition the value resolver reads, so a picker can never offer a column that won't resolve.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`brand` resolves through the product's brand relation. `price` resolves through the product's DEFAULT pricing tier.\n\nThe response also includes one entry per named pricing tier, with a value of `pricing_tier:{id}` and a label of `Price — {tier name} tier`. Use these to pin a specific tier (for example, always send wholesale pricing to one channel) instead of following whichever tier is the default. A product with no price on the selected tier resolves to null rather than 0, so a required channel field surfaces as missing instead of publishing a zero price. Tiers are per-account data, so this part of the list differs between accounts.\n\nEach item includes `type` — the column's data type (`text` or `number`) — so a mapping picker can show what kind of value a column carries.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/mappable-product-fields","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts","mappable-product-fields"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"value\": \"name\",\n      \"label\": \"Name\",\n      \"type\": \"text\"\n    },\n    {\n      \"value\": \"sku\",\n      \"label\": \"SKU\",\n      \"type\": \"text\"\n    },\n    {\n      \"value\": \"barcode\",\n      \"label\": \"Barcode (UPC / EAN)\",\n      \"type\": \"text\"\n    },\n    {\n      \"value\": \"mpn\",\n      \"label\": \"MPN\",\n      \"type\": \"text\"\n    },\n    {\n      \"value\": \"brand\",\n      \"label\": \"Brand\",\n      \"type\": \"text\"\n    },\n    {\n      \"value\": \"price\",\n      \"label\": \"Price (default tier)\",\n      \"type\": \"number\"\n    },\n    {\n      \"value\": \"weight\",\n      \"label\": \"Weight\",\n      \"type\": \"number\"\n    },\n    {\n      \"value\": \"weight_unit\",\n      \"label\": \"Weight unit\",\n      \"type\": \"text\"\n    },\n    {\n      \"value\": \"length\",\n      \"label\": \"Length\",\n      \"type\": \"number\"\n    },\n    {\n      \"value\": \"width\",\n      \"label\": \"Width\",\n      \"type\": \"number\"\n    },\n    {\n      \"value\": \"height\",\n      \"label\": \"Height\",\n      \"type\": \"number\"\n    },\n    {\n      \"value\": \"dimension_unit\",\n      \"label\": \"Dimension unit\",\n      \"type\": \"text\"\n    },\n    {\n      \"value\": \"case_quantity\",\n      \"label\": \"Case quantity\",\n      \"type\": \"number\"\n    },\n    {\n      \"value\": \"fba_prep_instructions\",\n      \"label\": \"FBA prep instructions\",\n      \"type\": \"text\"\n    },\n    {\n      \"value\": \"pricing_tier:1\",\n      \"label\": \"Price — Retail tier\",\n      \"type\": \"number\"\n    },\n    {\n      \"value\": \"pricing_tier:2\",\n      \"label\": \"Price — Wholesale tier\",\n      \"type\": \"number\"\n    }\n  ]\n}"}]},{"name":"Get Draft","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/{draft}","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts",":draft"],"variable":[{"key":"draft","value":"42","description":"Listing draft ID"}]},"description":"Show a draft with the full per-field payload.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach `fields[]` row merges the cached category schema with the draft's resolved value:\n- `value` — resolved value after the precedence chain (override > template > channel mapping > integration mapping > blank)\n- `provenance` — where the value came from (`override`, `template`, `channel_mapping`, `integration_mapping`, `blank`)\n- `has_override` — whether the draft overrides this field\n- `override` — the raw override: a literal value, or a mapped source object `{source_type, source_value}`\n- `errors` — validation/channel errors scoped to this field\n\n`category_provenance` says where the draft's category came from: `override` (the product's own override on the channel), `type_map` (its product type's channel category map) or `manual` (chosen when the draft was created); null when the draft has no category.\n\nAuthentication: Requires Bearer token.\n\n**variant_context** — describes whether this draft targets a single VARIANT of a matrix family rather than the family itself. When `is_variant_child` is `true`, `parent_sku` / `parent_name` identify the family and `sibling_count` is how many variants it has. Publishing a single child is permitted, but it creates a standalone channel item with no variant group — publish the parent to list the whole family as one grouped listing. `null` when the draft has no product.\n\nEach field's `schema` reports the channel's constraints. `cardinality` is the MAXIMUM number of values the field accepts (0 = unbounded) and `min_cardinality` is the MINIMUM (0 = none). A channel can require several entries on one field - Walmart's Cloth Baby Diapers category asks for at least 4 Key Features and 4 additional images - and rejects the item after accepting the submission, so supply at least `min_cardinality` values before publishing.\n\n**`fields[].variant_values`** — a matrix product publishes as one item per variant, each resolving its own data, so a single value cannot describe what will be sent. This array carries the field's value on every variant, in child order: `sku`, `value`, and the `provenance` it resolved from. Empty for a product with no variants, where `value` alone is the whole answer.\n\nUse it to see which variants are missing a required field: in the example above the listing is blocked because one of the two variants has no image, and `variant_values` names which one.\n\n**`fields[].source_type` / `source_value`** identify what the value resolved from (a product column, a product attribute, a fixed value), so a client can show the mapping rather than just the result."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/{draft}","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts",":draft"],"variable":[{"key":"draft","value":"42","description":"Listing draft ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 42,\n  \"product_id\": 88,\n  \"sales_channel_id\": 30,\n  \"sales_channel_category_id\": 7,\n  \"category_provenance\": \"manual\",\n  \"sales_channel_product_template_id\": null,\n  \"sales_channel_listing_profile_id\": null,\n  \"product_listing_id\": null,\n  \"purpose\": \"create\",\n  \"state\": \"ready\",\n  \"scheduled_at\": null,\n  \"image_urls\": [\n    \"https://cdn.example.com/img/beanie-1.jpg\"\n  ],\n  \"validation_errors\": null,\n  \"channel_errors\": null,\n  \"published_at\": null,\n  \"created_at\": \"2026-07-06T12:00:00+00:00\",\n  \"updated_at\": \"2026-07-06T12:05:00+00:00\",\n  \"product\": {\n    \"id\": 88,\n    \"name\": \"Tiny Threads Beanie\",\n    \"sku\": \"TT008\"\n  },\n  \"variant_context\": {\n    \"is_variant_child\": false,\n    \"parent_id\": null,\n    \"parent_sku\": null,\n    \"parent_name\": null,\n    \"sibling_count\": 0\n  },\n  \"sales_channel\": {\n    \"id\": 30,\n    \"name\": \"eBay Store\",\n    \"integration_name\": \"Ebay\"\n  },\n  \"category\": {\n    \"id\": 7,\n    \"name\": \"Hoodies\",\n    \"path\": \"QA / Hoodies\",\n    \"external_id\": \"155184\"\n  },\n  \"template\": null,\n  \"listing_profile\": null,\n  \"variation_theme\": null,\n  \"variation_data\": null,\n  \"fields\": [\n    {\n      \"name\": \"Brand\",\n      \"label\": \"Brand\",\n      \"schema\": {\n        \"type\": \"text\",\n        \"usage\": \"required\",\n        \"cardinality\": 1,\n        \"min_cardinality\": 0,\n        \"allowed_values\": null,\n        \"allows_custom_value\": true,\n        \"max_length\": null,\n        \"min_length\": null,\n        \"object_schema\": null\n      },\n      \"value\": \"Tiny Threads\",\n      \"provenance\": \"override\",\n      \"has_override\": true,\n      \"override\": {\n        \"source_type\": \"product_field\",\n        \"source_value\": \"brand\"\n      },\n      \"errors\": [],\n      \"source_type\": \"product_field\",\n      \"source_value\": \"brand\",\n      \"variant_values\": []\n    },\n    {\n      \"name\": \"Color\",\n      \"label\": \"Color\",\n      \"schema\": {\n        \"type\": \"text\",\n        \"usage\": \"required\",\n        \"cardinality\": 1,\n        \"min_cardinality\": 0,\n        \"allowed_values\": null,\n        \"allows_custom_value\": true,\n        \"max_length\": null,\n        \"min_length\": null,\n        \"object_schema\": null\n      },\n      \"value\": \"Sage\",\n      \"provenance\": \"template\",\n      \"has_override\": false,\n      \"override\": null,\n      \"errors\": [],\n      \"source_type\": \"product_field\",\n      \"source_value\": \"brand\",\n      \"variant_values\": []\n    },\n    {\n      \"name\": \"mainImageUrl\",\n      \"label\": \"Main Image URL\",\n      \"schema\": {\n        \"type\": \"text\",\n        \"usage\": \"required\",\n        \"cardinality\": 1,\n        \"min_cardinality\": 0,\n        \"allowed_values\": null,\n        \"allows_custom_value\": true,\n        \"max_length\": 2500,\n        \"min_length\": null,\n        \"object_schema\": null\n      },\n      \"value\": null,\n      \"provenance\": \"template\",\n      \"source_type\": \"product_field\",\n      \"source_value\": \"featured_image_url\",\n      \"has_override\": false,\n      \"override\": null,\n      \"errors\": [\n        {\n          \"code\": \"required\",\n          \"message\": \"Missing on 1 of 2 variants: TEE-BLUE-M.\"\n        }\n      ],\n      \"variant_values\": [\n        {\n          \"sku\": \"TEE-RED-M\",\n          \"value\": \"https://cdn.example.com/tee-red.jpg\",\n          \"provenance\": \"template\"\n        },\n        {\n          \"sku\": \"TEE-BLUE-M\",\n          \"value\": null,\n          \"provenance\": \"template\"\n        }\n      ]\n    }\n  ],\n  \"field_overrides\": {\n    \"Brand\": {\n      \"source_type\": \"product_field\",\n      \"source_value\": \"brand\"\n    }\n  },\n  \"resolved_values\": {\n    \"Brand\": {\n      \"value\": \"Tiny Threads\",\n      \"provenance\": \"override\"\n    },\n    \"Color\": {\n      \"value\": \"Sage\",\n      \"provenance\": \"template\"\n    }\n  }\n}"}]},{"name":"Update Draft (overrides, images, structure)","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/{draft}","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts",":draft"],"variable":[{"key":"draft","value":"42","description":"Listing draft ID"}]},"description":"Update a draft — saves `field_overrides`, `image_urls`, and structural changes (`sales_channel_category_id`, `sales_channel_product_template_id`, `sales_channel_listing_profile_id`), then re-resolves and re-validates. The draft transitions to `ready` when no validation errors remain.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`field_overrides` values come in two shapes:\n- **Literal value** (string / number / bool / array for multiselect) — used verbatim, provenance `override`.\n- **Mapped source** — an object `{\"source_type\": ..., \"source_value\": ...}` that re-resolves from product data on every validate/publish. `source_type` must be one of `product_field` (whitelisted product columns — see GET /api/v2/listing-drafts/mappable-product-fields), `product_attribute` (an Attribute id), `static` (literal string), or `expression` (mustache tokens, e.g. `{{product.sku}} - {{product.name}}`). A mapped override that resolves to null stays null (no fall-through to template/mapping values) so required-field validation can flag it.\n\nPassing an invalid `source_type`, or a `source_value` that is not a scalar, returns 422.\n\nFields:\n- `field_overrides` (nullable, object) — map of field name to literal value or mapped source (see above).\n- `sales_channel_category_id` (nullable, integer)\n- `sales_channel_product_template_id` (nullable, integer)\n- `sales_channel_listing_profile_id` (nullable, integer)\n- `image_urls` (nullable, array of strings)\n\nAuthentication: Requires Bearer token.\n\n**variant_context** — describes whether this draft targets a single VARIANT of a matrix family rather than the family itself. When `is_variant_child` is `true`, `parent_sku` / `parent_name` identify the family and `sibling_count` is how many variants it has. Publishing a single child is permitted, but it creates a standalone channel item with no variant group — publish the parent to list the whole family as one grouped listing. `null` when the draft has no product.\n\nEach field's `schema` reports the channel's constraints. `cardinality` is the MAXIMUM number of values the field accepts (0 = unbounded) and `min_cardinality` is the MINIMUM (0 = none). A channel can require several entries on one field - Walmart's Cloth Baby Diapers category asks for at least 4 Key Features and 4 additional images - and rejects the item after accepting the submission, so supply at least `min_cardinality` values before publishing.\n\n`category_provenance` (optional: `override` | `type_map` | `manual`) records where a changed category came from. Omitting `field_overrides` leaves the draft's saved overrides untouched; send an empty array to clear them.","body":{"mode":"raw","raw":"{\n  \"field_overrides\": {\n    \"Brand\": {\n      \"source_type\": \"product_field\",\n      \"source_value\": \"brand\"\n    },\n    \"Color\": \"Sage\",\n    \"Title\": {\n      \"source_type\": \"expression\",\n      \"source_value\": \"{{product.sku}} - {{product.name}}\"\n    }\n  },\n  \"image_urls\": [\n    \"https://cdn.example.com/img/beanie-1.jpg\"\n  ],\n  \"category_provenance\": \"override\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/{draft}","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts",":draft"],"variable":[{"key":"draft","value":"42","description":"Listing draft ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 42,\n  \"product_id\": 88,\n  \"sales_channel_id\": 30,\n  \"sales_channel_category_id\": 7,\n  \"category_provenance\": \"manual\",\n  \"sales_channel_product_template_id\": null,\n  \"sales_channel_listing_profile_id\": null,\n  \"product_listing_id\": null,\n  \"purpose\": \"create\",\n  \"state\": \"ready\",\n  \"scheduled_at\": null,\n  \"image_urls\": [\n    \"https://cdn.example.com/img/beanie-1.jpg\"\n  ],\n  \"validation_errors\": null,\n  \"channel_errors\": null,\n  \"published_at\": null,\n  \"created_at\": \"2026-07-06T12:00:00+00:00\",\n  \"updated_at\": \"2026-07-06T12:05:00+00:00\",\n  \"product\": {\n    \"id\": 88,\n    \"name\": \"Tiny Threads Beanie\",\n    \"sku\": \"TT008\"\n  },\n  \"variant_context\": {\n    \"is_variant_child\": false,\n    \"parent_id\": null,\n    \"parent_sku\": null,\n    \"parent_name\": null,\n    \"sibling_count\": 0\n  },\n  \"sales_channel\": {\n    \"id\": 30,\n    \"name\": \"eBay Store\",\n    \"integration_name\": \"Ebay\"\n  },\n  \"category\": {\n    \"id\": 7,\n    \"name\": \"Hoodies\",\n    \"path\": \"QA / Hoodies\",\n    \"external_id\": \"155184\"\n  },\n  \"template\": null,\n  \"listing_profile\": null,\n  \"variation_theme\": null,\n  \"variation_data\": null,\n  \"fields\": [\n    {\n      \"name\": \"Brand\",\n      \"label\": \"Brand\",\n      \"schema\": {\n        \"type\": \"text\",\n        \"usage\": \"required\",\n        \"cardinality\": 1,\n        \"min_cardinality\": 0,\n        \"allowed_values\": null,\n        \"allows_custom_value\": true,\n        \"max_length\": null,\n        \"min_length\": null,\n        \"object_schema\": null\n      },\n      \"value\": \"Tiny Threads\",\n      \"provenance\": \"override\",\n      \"has_override\": true,\n      \"override\": {\n        \"source_type\": \"product_field\",\n        \"source_value\": \"brand\"\n      },\n      \"errors\": []\n    },\n    {\n      \"name\": \"Color\",\n      \"label\": \"Color\",\n      \"schema\": {\n        \"type\": \"text\",\n        \"usage\": \"required\",\n        \"cardinality\": 1,\n        \"min_cardinality\": 0,\n        \"allowed_values\": null,\n        \"allows_custom_value\": true,\n        \"max_length\": null,\n        \"min_length\": null,\n        \"object_schema\": null\n      },\n      \"value\": \"Sage\",\n      \"provenance\": \"template\",\n      \"has_override\": false,\n      \"override\": null,\n      \"errors\": []\n    }\n  ],\n  \"field_overrides\": {\n    \"Brand\": {\n      \"source_type\": \"product_field\",\n      \"source_value\": \"brand\"\n    }\n  },\n  \"resolved_values\": {\n    \"Brand\": {\n      \"value\": \"Tiny Threads\",\n      \"provenance\": \"override\"\n    },\n    \"Color\": {\n      \"value\": \"Sage\",\n      \"provenance\": \"template\"\n    }\n  }\n}"}]},{"name":"Validate Draft","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/{draft}/validate","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts",":draft","validate"],"variable":[{"key":"draft","value":"42","description":"Listing draft ID"}]},"description":"Re-resolve the draft's values against current product data + mappings and re-run validation. Mapped overrides (`{source_type, source_value}`) are re-evaluated against the product on every call. State transitions to `ready` when validation passes, back to `draft` when errors appear. A draft without a category records a `missing_category` validation error.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**variant_context** — describes whether this draft targets a single VARIANT of a matrix family rather than the family itself. When `is_variant_child` is `true`, `parent_sku` / `parent_name` identify the family and `sibling_count` is how many variants it has. Publishing a single child is permitted, but it creates a standalone channel item with no variant group — publish the parent to list the whole family as one grouped listing. `null` when the draft has no product.\n\nEach field's `schema` reports the channel's constraints. `cardinality` is the MAXIMUM number of values the field accepts (0 = unbounded) and `min_cardinality` is the MINIMUM (0 = none). A channel can require several entries on one field - Walmart's Cloth Baby Diapers category asks for at least 4 Key Features and 4 additional images - and rejects the item after accepting the submission, so supply at least `min_cardinality` values before publishing."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/{draft}/validate","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts",":draft","validate"],"variable":[{"key":"draft","value":"42","description":"Listing draft ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 42,\n  \"product_id\": 88,\n  \"sales_channel_id\": 30,\n  \"sales_channel_category_id\": 7,\n  \"category_provenance\": \"manual\",\n  \"sales_channel_product_template_id\": null,\n  \"sales_channel_listing_profile_id\": null,\n  \"product_listing_id\": null,\n  \"purpose\": \"create\",\n  \"state\": \"ready\",\n  \"scheduled_at\": null,\n  \"image_urls\": [\n    \"https://cdn.example.com/img/beanie-1.jpg\"\n  ],\n  \"validation_errors\": null,\n  \"channel_errors\": null,\n  \"published_at\": null,\n  \"created_at\": \"2026-07-06T12:00:00+00:00\",\n  \"updated_at\": \"2026-07-06T12:05:00+00:00\",\n  \"product\": {\n    \"id\": 88,\n    \"name\": \"Tiny Threads Beanie\",\n    \"sku\": \"TT008\"\n  },\n  \"variant_context\": {\n    \"is_variant_child\": false,\n    \"parent_id\": null,\n    \"parent_sku\": null,\n    \"parent_name\": null,\n    \"sibling_count\": 0\n  },\n  \"sales_channel\": {\n    \"id\": 30,\n    \"name\": \"eBay Store\",\n    \"integration_name\": \"Ebay\"\n  },\n  \"category\": {\n    \"id\": 7,\n    \"name\": \"Hoodies\",\n    \"path\": \"QA / Hoodies\",\n    \"external_id\": \"155184\"\n  },\n  \"template\": null,\n  \"listing_profile\": null,\n  \"variation_theme\": null,\n  \"variation_data\": null,\n  \"fields\": [\n    {\n      \"name\": \"Brand\",\n      \"label\": \"Brand\",\n      \"schema\": {\n        \"type\": \"text\",\n        \"usage\": \"required\",\n        \"cardinality\": 1,\n        \"min_cardinality\": 0,\n        \"allowed_values\": null,\n        \"allows_custom_value\": true,\n        \"max_length\": null,\n        \"min_length\": null,\n        \"object_schema\": null\n      },\n      \"value\": \"Tiny Threads\",\n      \"provenance\": \"override\",\n      \"has_override\": true,\n      \"override\": {\n        \"source_type\": \"product_field\",\n        \"source_value\": \"brand\"\n      },\n      \"errors\": []\n    },\n    {\n      \"name\": \"Color\",\n      \"label\": \"Color\",\n      \"schema\": {\n        \"type\": \"text\",\n        \"usage\": \"required\",\n        \"cardinality\": 1,\n        \"min_cardinality\": 0,\n        \"allowed_values\": null,\n        \"allows_custom_value\": true,\n        \"max_length\": null,\n        \"min_length\": null,\n        \"object_schema\": null\n      },\n      \"value\": \"Sage\",\n      \"provenance\": \"template\",\n      \"has_override\": false,\n      \"override\": null,\n      \"errors\": []\n    }\n  ],\n  \"field_overrides\": {\n    \"Brand\": {\n      \"source_type\": \"product_field\",\n      \"source_value\": \"brand\"\n    }\n  },\n  \"resolved_values\": {\n    \"Brand\": {\n      \"value\": \"Tiny Threads\",\n      \"provenance\": \"override\"\n    },\n    \"Color\": {\n      \"value\": \"Sage\",\n      \"provenance\": \"template\"\n    }\n  }\n}"}]},{"name":"Preview Draft Against Product","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/{draft}/preview-against-product","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts",":draft","preview-against-product"],"variable":[{"key":"draft","value":"42","description":"Listing draft ID"}]},"description":"Resolve this draft's mappings (its template + field overrides) against a DIFFERENT product, returning the value each channel field would receive for that product — with no persistence. Useful for a \"preview against another product\" control: mapped fields (`product_field` / `product_attribute` / `expression`) re-evaluate against the given product, while typed literal values stay as-is.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest body:\n- `product_id` (integer, required) — the product to resolve against; must exist.\n\nResponse: `data.product` echoes the previewed product, and `data.values` is keyed by channel field name, each with the resolved `value` and its `provenance` (override / template / channel_mapping / integration_mapping / channel_default / blank).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"product_id\": 87\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/{draft}/preview-against-product","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts",":draft","preview-against-product"],"variable":[{"key":"draft","value":"42","description":"Listing draft ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product\": {\n      \"id\": 87,\n      \"name\": \"Blue Widget - 2 Pack\",\n      \"sku\": \"WIDGET-BLUE-2PK\"\n    },\n    \"values\": {\n      \"Brand\": {\n        \"value\": \"Acme\",\n        \"provenance\": \"override\"\n      },\n      \"Color\": {\n        \"value\": \"Blue\",\n        \"provenance\": \"template\"\n      },\n      \"Size\": {\n        \"value\": null,\n        \"provenance\": \"blank\"\n      }\n    }\n  }\n}"}]},{"name":"Get Draft Publish Attempts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/{draft}/attempts","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts",":draft","attempts"],"variable":[{"key":"draft","value":"42","description":"Listing draft ID"}]},"description":"Publish-attempt history for a listing draft: one entry per publish/update attempt, oldest first. A retried publish appends a new attempt rather than overwriting the last error, so you can see each transient retry (retryable = true) and the final terminal failure (retryable = false).\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/{draft}/attempts","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts",":draft","attempts"],"variable":[{"key":"draft","value":"42","description":"Listing draft ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"attempt_no\": 1,\n      \"status\": \"failed\",\n      \"retryable\": true,\n      \"errors\": [\n        {\n          \"code\": \"rate_limited\",\n          \"message\": \"Too many requests, retry later.\"\n        }\n      ],\n      \"created_at\": \"2026-07-31T09:00:00.000000Z\"\n    },\n    {\n      \"id\": 2,\n      \"attempt_no\": 2,\n      \"status\": \"published\",\n      \"retryable\": false,\n      \"errors\": [],\n      \"created_at\": \"2026-07-31T09:05:00.000000Z\"\n    }\n  ]\n}"}]},{"name":"Export Draft Template Fields","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/{draft}/fields/export","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts",":draft","fields","export"],"variable":[{"key":"draft","value":"42","description":"Listing draft ID"}]},"description":"Download the draft category's field requirements as a styled Excel (.xlsx) workbook. The workbook has one row per channel field with its priority (required / recommended / optional), type, allowed values, plain-language guidance, what SKU.io currently maps into it, and the resolved sample value — so the still-empty fields can be collected from a supplier. Required fields lead the sheet; fixed-option fields get a dropdown of the allowed values on the sample cell.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe draft must have a category selected — otherwise a 422 is returned.\n\nResponse: an .xlsx file download (Content-Type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/{draft}/fields/export","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts",":draft","fields","export"],"variable":[{"key":"draft","value":"42","description":"Listing draft ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"Binary .xlsx file download — a single worksheet listing every channel field with its priority, type, allowed values, guidance, mapping, and sample value."}]},{"name":"Get Draft Variation Matrix","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/{draft}/variation-themes","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts",":draft","variation-themes"],"variable":[{"key":"draft","value":"42","description":"Listing draft ID"}]},"description":"The variation matrix for a draft: the variation theme (e.g. \"Color-Size\"), its axes, and the per-child grid. Each child carries its axis values plus per-variant identity — `upc` (the variant's barcode, null when not set), `price` (null when the variant has no own price), `image_url` (the variant's primary image, null when it has none) — an `is_primary` flag marking the variant published as the group's primary, and a readiness verdict: `status` is `ready` or `incomplete`, with `issues` listing human-readable reasons (e.g. \"Missing Color\", \"Needs UPC\") that would block publishing the variation family. Empty axes/children for a single-variation product.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/{draft}/variation-themes","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts",":draft","variation-themes"],"variable":[{"key":"draft","value":"42","description":"Listing draft ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"variation_theme\": \"Color-Size\",\n    \"axes\": [\n      \"Color\",\n      \"Size\"\n    ],\n    \"children\": [\n      {\n        \"sku\": \"TEE-RED-M\",\n        \"theme_values\": {\n          \"Color\": \"Red\",\n          \"Size\": \"M\"\n        },\n        \"upc\": 12345678905,\n        \"price\": 24.99,\n        \"image_url\": \"https://cdn.example.com/products/tee-red-m.jpg\",\n        \"is_primary\": true,\n        \"status\": \"ready\",\n        \"issues\": []\n      },\n      {\n        \"sku\": \"TEE-BLUE-L\",\n        \"theme_values\": {\n          \"Color\": \"Blue\",\n          \"Size\": \"L\"\n        },\n        \"upc\": null,\n        \"price\": 26.99,\n        \"image_url\": null,\n        \"is_primary\": false,\n        \"status\": \"incomplete\",\n        \"issues\": [\n          \"Needs UPC\"\n        ]\n      }\n    ]\n  }\n}"}]},{"name":"Validate Draft Variation Matrix","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/{draft}/variations/validate","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts",":draft","variations","validate"],"variable":[{"key":"draft","value":"42","description":"Listing draft ID"}]},"description":"Validate a draft's variation matrix locally (before publishing): theme completeness, duplicate combinations, per-channel variation caps, and axis consistency. Returns whether the matrix is valid plus any errors keyed to a child/axis.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/{draft}/variations/validate","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts",":draft","variations","validate"],"variable":[{"key":"draft","value":"42","description":"Listing draft ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"valid\": false,\n    \"errors\": [\n      {\n        \"field\": \"variation:TEE-RED-M:Size\",\n        \"code\": \"missing_aspect\",\n        \"message\": \"Child 'TEE-RED-M' is missing a value for Size.\"\n      }\n    ]\n  }\n}"}]},{"name":"Create Drafts in Bulk","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/bulk","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts","bulk"]},"description":"Create one `create`-purpose draft per product on a sales channel and return them, then pass the returned ids to Bulk Publish Drafts. Each draft is resolved and validated on creation.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nSend either `products[]` (per-product category, template and provenance — typically what Resolve Draft Categories returned) or plain `product_ids[]`, or both; a product in `product_ids` that has no `products[]` row uses the top-level `sales_channel_category_id` / `sales_channel_product_template_id`. A product that ends up with no category at all is resolved like a single draft: its override on the channel, else its product type's mapped category, with that category's default template.\n\nProducts the channel's catalog excludes do not fail the request — they are returned in `skipped` with a reason and the remaining drafts are still created.\n\nAuthentication: Requires Bearer token.\n\nFields:\n- `sales_channel_id` (required, integer)\n- `products` (required unless `product_ids` is sent, array, 1–500)\n- `products[].product_id` (required, integer)\n- `products[].sales_channel_category_id` (nullable, integer)\n- `products[].sales_channel_product_template_id` (nullable, integer)\n- `products[].category_provenance` (nullable, `override` | `type_map` | `manual`) — where that row's category came from; a supplied category without it is recorded as `manual`\n- `product_ids` (required unless `products` is sent, array of integers, 1–500)\n- `sales_channel_category_id` (nullable, integer) — fallback category for rows that do not set their own\n- `sales_channel_product_template_id` (nullable, integer) — fallback template\n- `sales_channel_listing_profile_id` (nullable, integer) — applied to every draft\n\nThe returned drafts are the compact form (no `fields`, `field_overrides` or `resolved_values`); read a draft to get those. `skipped[]` rows carry `product_id`, `sku`, `name`, `reason`.\n\n**Requires permission:** `listings.create`","body":{"mode":"raw","raw":"{\n  \"sales_channel_id\": 30,\n  \"products\": [\n    {\n      \"product_id\": 88,\n      \"sales_channel_category_id\": 1184,\n      \"sales_channel_product_template_id\": 21,\n      \"category_provenance\": \"type_map\"\n    },\n    {\n      \"product_id\": 89,\n      \"sales_channel_category_id\": 2231,\n      \"sales_channel_product_template_id\": null,\n      \"category_provenance\": \"manual\"\n    },\n    {\n      \"product_id\": 90\n    }\n  ],\n  \"sales_channel_category_id\": null,\n  \"sales_channel_product_template_id\": null,\n  \"sales_channel_listing_profile_id\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/bulk","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts","bulk"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 501,\n      \"product_id\": 88,\n      \"sales_channel_id\": 30,\n      \"sales_channel_category_id\": 1184,\n      \"category_provenance\": \"type_map\",\n      \"sales_channel_product_template_id\": 21,\n      \"sales_channel_listing_profile_id\": null,\n      \"product_listing_id\": null,\n      \"purpose\": \"create\",\n      \"state\": \"ready\",\n      \"scheduled_at\": null,\n      \"image_urls\": [\n        \"https://cdn.example.com/img/cd-os-sage-1.jpg\"\n      ],\n      \"validation_errors\": null,\n      \"channel_errors\": null,\n      \"published_at\": null,\n      \"created_at\": \"2026-09-16T10:41:00+00:00\",\n      \"updated_at\": \"2026-09-16T10:41:02+00:00\",\n      \"product\": {\n        \"id\": 88,\n        \"name\": \"One-size cloth diaper — Sage\",\n        \"sku\": \"CD-OS-SAGE\",\n        \"type\": \"standard\"\n      },\n      \"variant_context\": {\n        \"is_variant_child\": false,\n        \"parent_id\": null,\n        \"parent_sku\": null,\n        \"parent_name\": null,\n        \"sibling_count\": 0\n      },\n      \"sales_channel\": {\n        \"id\": 30,\n        \"name\": \"Walmart US\",\n        \"integration_name\": \"Walmart\"\n      },\n      \"category\": {\n        \"id\": 1184,\n        \"name\": \"Cloth Diapers\",\n        \"path\": \"Baby > Diapering > Cloth Diapers\",\n        \"external_id\": \"5406\"\n      },\n      \"template\": {\n        \"id\": 21,\n        \"name\": \"Cloth diapers — Walmart\"\n      },\n      \"listing_profile\": null,\n      \"variation_theme\": null\n    },\n    {\n      \"id\": 502,\n      \"product_id\": 89,\n      \"sales_channel_id\": 30,\n      \"sales_channel_category_id\": 2231,\n      \"category_provenance\": \"manual\",\n      \"sales_channel_product_template_id\": null,\n      \"sales_channel_listing_profile_id\": null,\n      \"product_listing_id\": null,\n      \"purpose\": \"create\",\n      \"state\": \"ready\",\n      \"scheduled_at\": null,\n      \"image_urls\": [\n        \"https://cdn.example.com/img/wb-lg-navy-1.jpg\"\n      ],\n      \"validation_errors\": null,\n      \"channel_errors\": null,\n      \"published_at\": null,\n      \"created_at\": \"2026-09-16T10:41:00+00:00\",\n      \"updated_at\": \"2026-09-16T10:41:02+00:00\",\n      \"product\": {\n        \"id\": 89,\n        \"name\": \"Large wet/dry bag — Navy\",\n        \"sku\": \"WB-LG-NAVY\",\n        \"type\": \"standard\"\n      },\n      \"variant_context\": {\n        \"is_variant_child\": false,\n        \"parent_id\": null,\n        \"parent_sku\": null,\n        \"parent_name\": null,\n        \"sibling_count\": 0\n      },\n      \"sales_channel\": {\n        \"id\": 30,\n        \"name\": \"Walmart US\",\n        \"integration_name\": \"Walmart\"\n      },\n      \"category\": {\n        \"id\": 2231,\n        \"name\": \"Diaper Bags\",\n        \"path\": \"Baby > Diapering > Diaper Bags\",\n        \"external_id\": \"5412\"\n      },\n      \"template\": null,\n      \"listing_profile\": null,\n      \"variation_theme\": null\n    },\n    {\n      \"id\": 503,\n      \"product_id\": 90,\n      \"sales_channel_id\": 30,\n      \"sales_channel_category_id\": null,\n      \"category_provenance\": null,\n      \"sales_channel_product_template_id\": null,\n      \"sales_channel_listing_profile_id\": null,\n      \"product_listing_id\": null,\n      \"purpose\": \"create\",\n      \"state\": \"draft\",\n      \"scheduled_at\": null,\n      \"image_urls\": [\n        \"https://cdn.example.com/img/bl-100-1.jpg\"\n      ],\n      \"validation_errors\": [\n        {\n          \"field\": \"_global\",\n          \"code\": \"missing_category\",\n          \"message\": \"Pick a category before publishing.\"\n        }\n      ],\n      \"channel_errors\": null,\n      \"published_at\": null,\n      \"created_at\": \"2026-09-16T10:41:00+00:00\",\n      \"updated_at\": \"2026-09-16T10:41:02+00:00\",\n      \"product\": {\n        \"id\": 90,\n        \"name\": \"Bamboo liners (100 pack)\",\n        \"sku\": \"BL-100\",\n        \"type\": \"standard\"\n      },\n      \"variant_context\": {\n        \"is_variant_child\": false,\n        \"parent_id\": null,\n        \"parent_sku\": null,\n        \"parent_name\": null,\n        \"sibling_count\": 0\n      },\n      \"sales_channel\": {\n        \"id\": 30,\n        \"name\": \"Walmart US\",\n        \"integration_name\": \"Walmart\"\n      },\n      \"category\": null,\n      \"template\": null,\n      \"listing_profile\": null,\n      \"variation_theme\": null\n    }\n  ],\n  \"skipped\": [\n    {\n      \"product_id\": 92,\n      \"sku\": \"GC-25\",\n      \"name\": \"Gift card $25\",\n      \"reason\": \"Gift card $25 is not in this channel's catalog.\"\n    }\n  ]\n}"}]},{"name":"Bulk Publish Drafts","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/bulk-publish","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts","bulk-publish"]},"description":"Publish up to 500 drafts (for example the ones returned by Create Drafts in Bulk) as one background job. Returns the job id; each draft's `state`, `published_at` and `channel_errors` are updated as the job progresses — read the drafts, or their publish attempts, to follow it. Drafts that are not publishable are left unchanged.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- `draft_ids` (required, array of integers, 1–500)\n- `channel_label` (nullable, string, max 120) — a name for the job\n\n**Requires permission:** `listings.publish`","body":{"mode":"raw","raw":"{\n  \"draft_ids\": [\n    501,\n    502\n  ],\n  \"channel_label\": \"Walmart US\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/bulk-publish","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts","bulk-publish"]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 8820\n  },\n  \"message\": \"Bulk publish started.\"\n}"}]},{"name":"Prepare Ready-to-List Drafts","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/prepare-ready","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts","prepare-ready"]},"description":"Seed a draft for every \"ready to list\" product on a sales channel — products that are data-complete and routed to a template by their product type, but do not yet have a draft or a live listing. Each product's own template (and its category and listing profile) is resolved and applied automatically, so no template needs to be chosen. Runs as one background job and returns its id; the seeded drafts move to `ready` (or `draft` if a field still needs attention) as the job progresses — read the drafts to follow it, then publish them with Bulk Publish Drafts.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nOmit `product_ids` to prepare the channel's entire ready-to-list set (resolved server-side); pass it to prepare only a chosen subset. It is safe to re-run — a product that already has a draft is skipped.\n\nAuthentication: Requires Bearer token.\n\nFields:\n- `sales_channel_id` (required, integer) — the channel to prepare drafts on\n- `product_ids` (nullable, array of integers, max 5000) — a subset to prepare; omit for the whole ready-to-list set\n- `channel_label` (nullable, string, max 120) — a name for the job\n\n**Requires permission:** `listings.create`","body":{"mode":"raw","raw":"{\n  \"sales_channel_id\": 30,\n  \"product_ids\": [\n    501,\n    502\n  ],\n  \"channel_label\": \"Walmart US\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/prepare-ready","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts","prepare-ready"]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 8821\n  },\n  \"message\": \"Preparing ready-to-list drafts.\"\n}"}]},{"name":"Resolve Draft Categories","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/resolve-categories","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts","resolve-categories"]},"description":"Resolve the channel category for up to 500 products on one sales channel in a single call, before creating drafts in bulk: the product's own override on the channel first, else the category its product type is mapped to — with provenance and the category's default template per product. Nothing is created. A product that resolves to nothing comes back with `sales_channel_category` and `provenance` null; pick a category for it (or map its type) before publishing.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- `sales_channel_id` (required, integer)\n- `product_ids` (required, array of integers, 1–500)\n\nEach resolution carries:\n- `product_id`, `sales_channel_id`\n- `sales_channel_category` — `{id, name, path, external_id, is_leaf}`, or null when nothing resolved\n- `provenance` — `override` (the product's own override on the channel) or `type_map` (its product type's map row on the channel); null when nothing resolved\n- `product_type` — `{id, name, path}` of the type the product resolves under (a variant with no type of its own uses its parent's), or null\n- `template` — `{id, name, is_default}`: the template pinned by the override, else the resolved category's default template; null when there is none\n- `override` — `{id, sales_channel_category_id, sales_channel_product_template_id, created_at}` when an override is set, else null\n- `type_map_category` — set only while an override is in force AND the type map points at a different category, so the difference can be shown; otherwise null\n\nPass each product's `sales_channel_category.id`, `template.id` and `provenance` on to Create Drafts in Bulk as `products[].sales_channel_category_id`, `products[].sales_channel_product_template_id` and `products[].category_provenance`.\n\n**Requires permission:** `listings.create`\n\nRead-only resolution: requires the listings read permission (not create).","body":{"mode":"raw","raw":"{\n  \"sales_channel_id\": 30,\n  \"product_ids\": [\n    88,\n    89,\n    90\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listing-drafts/resolve-categories","host":["{{baseUrl}}"],"path":["api","v2","listing-drafts","resolve-categories"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 88,\n      \"sales_channel_id\": 30,\n      \"sales_channel_category\": {\n        \"id\": 1184,\n        \"name\": \"Cloth Diapers\",\n        \"path\": \"Baby > Diapering > Cloth Diapers\",\n        \"external_id\": \"5406\",\n        \"is_leaf\": true\n      },\n      \"provenance\": \"type_map\",\n      \"product_type\": {\n        \"id\": 7,\n        \"name\": \"Cloth diapers\",\n        \"path\": \"Diapering > Cloth diapers\"\n      },\n      \"template\": {\n        \"id\": 21,\n        \"name\": \"Cloth diapers — Walmart\",\n        \"is_default\": true,\n        \"sales_channel_listing_profile_id\": 3\n      },\n      \"override\": null,\n      \"type_map_category\": null\n    },\n    {\n      \"product_id\": 89,\n      \"sales_channel_id\": 30,\n      \"sales_channel_category\": {\n        \"id\": 2231,\n        \"name\": \"Diaper Bags\",\n        \"path\": \"Baby > Diapering > Diaper Bags\",\n        \"external_id\": \"5412\",\n        \"is_leaf\": true\n      },\n      \"provenance\": \"type_map\",\n      \"product_type\": {\n        \"id\": 12,\n        \"name\": \"Wet bags\",\n        \"path\": \"Wet bags\"\n      },\n      \"template\": null,\n      \"override\": null,\n      \"type_map_category\": null\n    },\n    {\n      \"product_id\": 90,\n      \"sales_channel_id\": 30,\n      \"sales_channel_category\": null,\n      \"provenance\": null,\n      \"product_type\": null,\n      \"template\": null,\n      \"override\": null,\n      \"type_map_category\": null\n    }\n  ]\n}"}]},{"name":"List Channels for Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/channel-lifecycle/channels?product_ids[]=1","host":["{{baseUrl}}"],"path":["api","v2","products","channel-lifecycle","channels"],"query":[{"key":"product_ids[]","value":"1","description":"Product id to include (repeat for each product). Required.","disabled":true}]},"description":"List the sales channels the given products are currently listed on, with how many of the selected products are listed vs not listed on each channel, and whether the channel supports bulk archive / recover / delete.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nUse this to build a channel picker before running a bulk channel action. Pass the product ids you want to act on as a repeated `product_ids[]` query parameter.\n\n`supports_archive` is true when the channel can deactivate a listing; `supports_lifecycle` is true when it can also recover (reactivate/restore) and permanently delete a listing. TikTok Shop is the first channel to support the full lifecycle.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/channel-lifecycle/channels?product_ids[]=1","host":["{{baseUrl}}"],"path":["api","v2","products","channel-lifecycle","channels"],"query":[{"key":"product_ids[]","value":"1","description":"Product id to include (repeat for each product). Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_selected\": 2,\n    \"channels\": [\n      {\n        \"sales_channel_id\": 5,\n        \"integration_instance_id\": 12,\n        \"name\": \"TikTok Shop — US\",\n        \"integration_name\": \"TikTok Shop\",\n        \"integration_image_url\": \"https://cdn.sku.io/integrations/tiktok-shop.png\",\n        \"listed_count\": 2,\n        \"not_listed_count\": 0,\n        \"supports_archive\": true,\n        \"supports_lifecycle\": true\n      },\n      {\n        \"sales_channel_id\": 3,\n        \"integration_instance_id\": 7,\n        \"name\": \"Shopify — US\",\n        \"integration_name\": \"Shopify\",\n        \"integration_image_url\": \"https://cdn.sku.io/integrations/shopify.png\",\n        \"listed_count\": 1,\n        \"not_listed_count\": 1,\n        \"supports_archive\": true,\n        \"supports_lifecycle\": false\n      }\n    ]\n  }\n}"}]},{"name":"Get Channel Action Eligibility","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/channel-lifecycle/eligibility?sales_channel_id=5&product_ids[]=1","host":["{{baseUrl}}"],"path":["api","v2","products","channel-lifecycle","eligibility"],"query":[{"key":"sales_channel_id","value":"5","description":"The sales channel to evaluate against. Required.","disabled":true},{"key":"product_ids[]","value":"1","description":"Product id to include (repeat for each product). Required.","disabled":true}]},"description":"Return the per-state breakdown of the selected products on a single sales channel plus, for each action, how many products are eligible and whether the channel supports it.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`breakdown` counts the selected products by their state on the channel: `live`, `pending`, `archived`, `missing`, `unknown`, and `not_listed`. Each entry in `actions` reports the action (`publish`, `archive`, `recover`, `delete`), its `eligible_count`, whether it is `destructive`, whether it runs as a bulk background action here (`dispatchable`), and whether the channel `supported` it.\n\nOnly archive, recover and delete are dispatchable; publish is handled by the bulk publish flow.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/channel-lifecycle/eligibility?sales_channel_id=5&product_ids[]=1","host":["{{baseUrl}}"],"path":["api","v2","products","channel-lifecycle","eligibility"],"query":[{"key":"sales_channel_id","value":"5","description":"The sales channel to evaluate against. Required.","disabled":true},{"key":"product_ids[]","value":"1","description":"Product id to include (repeat for each product). Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_selected\": 60,\n    \"breakdown\": {\n      \"live\": 42,\n      \"pending\": 0,\n      \"archived\": 6,\n      \"missing\": 0,\n      \"unknown\": 0,\n      \"not_listed\": 12\n    },\n    \"actions\": [\n      {\n        \"action\": \"publish\",\n        \"label\": \"Publish\",\n        \"eligible_count\": 12,\n        \"destructive\": false,\n        \"dispatchable\": false,\n        \"supported\": true\n      },\n      {\n        \"action\": \"archive\",\n        \"label\": \"Archive\",\n        \"eligible_count\": 42,\n        \"destructive\": false,\n        \"dispatchable\": true,\n        \"supported\": true\n      },\n      {\n        \"action\": \"recover\",\n        \"label\": \"Recover\",\n        \"eligible_count\": 6,\n        \"destructive\": false,\n        \"dispatchable\": true,\n        \"supported\": true\n      },\n      {\n        \"action\": \"delete\",\n        \"label\": \"Delete\",\n        \"eligible_count\": 48,\n        \"destructive\": true,\n        \"dispatchable\": true,\n        \"supported\": true\n      }\n    ]\n  }\n}"}]},{"name":"Run Bulk Channel Action","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/channel-lifecycle/dispatch","host":["{{baseUrl}}"],"path":["api","v2","products","channel-lifecycle","dispatch"]},"description":"Run a bulk channel action (archive, recover or delete) against the eligible listings of the selected products on a sales channel. The action runs in the background and the response returns a `tracked_job_log_id` you can poll for progress and a per-product success/failure summary.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nOnly `archive`, `recover` and `delete` are accepted here — publishing new listings uses the bulk publish flow. Products with no eligible listing for the chosen action are skipped; the request returns 422 when none of the selected products are eligible.\n\n- `archive` deactivates the listing on the channel (reversible).\n- `recover` reactivates an archived listing or restores a deleted one.\n- `delete` permanently removes the listing from the channel.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"sales_channel_id\": 5,\n  \"action\": \"archive\",\n  \"product_ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/products/channel-lifecycle/dispatch","host":["{{baseUrl}}"],"path":["api","v2","products","channel-lifecycle","dispatch"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 9174\n  },\n  \"message\": \"Bulk Archive started — track progress with the tracked job log id.\"\n}"}]},{"name":"Get Content Sync Settings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/integration-instances/{integrationInstance}/content-sync-settings","host":["{{baseUrl}}"],"path":["api","integration-instances",":integrationInstance","content-sync-settings"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Get the ongoing content-sync configuration for a sales-channel integration instance: whether sync is enabled, the reconcile cadence, per field-group / per-field ownership and auto-apply defaults, and the governance gate.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nOwnership values: catalog (channel edits are flagged only), channel (channel edits can be pulled into the catalog), two_way (both sides may edit; simultaneous edits become conflicts).\nField groups: identity (barcode, mpn), descriptive (name, brand, attributes), media (images), physical (weight, dimensions).\ngovernance_gate: when enabled, a product is held back from publishing to this channel while any field listed in governance_gate.require is empty on the catalog record. Only outbound publishing is blocked — pulling content in from the channel is never gated, since that is how an incomplete product gets filled in. An enabled gate with an empty require list blocks nothing.\nRequires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/integration-instances/{integrationInstance}/content-sync-settings","host":["{{baseUrl}}"],"path":["api","integration-instances",":integrationInstance","content-sync-settings"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"enabled\": true,\n    \"reconcile_frequency\": \"daily\",\n    \"fill_empty_catalog\": true,\n    \"groups\": {\n      \"identity\": {\n        \"owner\": \"catalog\",\n        \"auto_apply\": false\n      },\n      \"descriptive\": {\n        \"owner\": \"channel\",\n        \"auto_apply\": false\n      },\n      \"media\": {\n        \"owner\": \"channel\",\n        \"auto_apply\": true\n      },\n      \"physical\": {\n        \"owner\": \"two_way\",\n        \"auto_apply\": false\n      }\n    },\n    \"fields\": {\n      \"name\": {\n        \"owner\": \"channel\",\n        \"auto_apply\": false\n      }\n    },\n    \"governance_gate\": {\n      \"enabled\": true,\n      \"require\": [\n        \"barcode\",\n        \"images\",\n        \"weight\"\n      ]\n    }\n  }\n}"}]},{"name":"Update Content Sync Settings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/integration-instances/{integrationInstance}/content-sync-settings","host":["{{baseUrl}}"],"path":["api","integration-instances",":integrationInstance","content-sync-settings"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Update the instance-level content-sync configuration. Partial updates are merged onto the current settings, so only the keys you send change — but enabled is required on every request. Enabling sync for the first time starts a background baseline job over every mapped listing on the instance, so pre-existing differences are not flagged as drift on day one; poll the jobs API to watch it complete.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nreconcile_frequency: six_hours, daily, weekly, or manual. manual switches off the scheduled sweep for this instance — drift is still detected in real time from channel webhooks, and a comparison can be triggered on demand through the reconcile endpoint.\nOwnership values: catalog (channel edits are flagged only), channel (channel edits can be pulled into the catalog), two_way (both sides may edit; simultaneous edits become conflicts).\ngovernance_gate.enabled: hold products back from publishing to this channel until their catalog content is complete. Outbound publishing only — inbound content is never blocked.\ngovernance_gate.require: the fields that must be present before a product may publish. Accepted values: barcode, mpn, name, brand, attributes, images, weight, dimensions. An enabled gate with an empty require list lets every product through.\nThe governance gate is evaluated independently of enabled, so it still applies when ongoing drift detection is switched off.\nUnknown group, field, or required-field keys are rejected with a 422.\nRequires Bearer token.","body":{"mode":"raw","raw":"{\n  \"enabled\": true,\n  \"reconcile_frequency\": \"daily\",\n  \"fill_empty_catalog\": true,\n  \"groups\": {\n    \"descriptive\": {\n      \"owner\": \"channel\",\n      \"auto_apply\": false\n    },\n    \"media\": {\n      \"owner\": \"channel\",\n      \"auto_apply\": true\n    }\n  },\n  \"fields\": {\n    \"name\": {\n      \"owner\": \"channel\",\n      \"auto_apply\": false\n    }\n  },\n  \"governance_gate\": {\n    \"enabled\": true,\n    \"require\": [\n      \"barcode\",\n      \"images\",\n      \"weight\"\n    ]\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/integration-instances/{integrationInstance}/content-sync-settings","host":["{{baseUrl}}"],"path":["api","integration-instances",":integrationInstance","content-sync-settings"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"enabled\": true,\n    \"reconcile_frequency\": \"daily\",\n    \"fill_empty_catalog\": true,\n    \"groups\": {\n      \"identity\": {\n        \"owner\": \"catalog\",\n        \"auto_apply\": false\n      },\n      \"descriptive\": {\n        \"owner\": \"channel\",\n        \"auto_apply\": false\n      },\n      \"media\": {\n        \"owner\": \"channel\",\n        \"auto_apply\": true\n      },\n      \"physical\": {\n        \"owner\": \"two_way\",\n        \"auto_apply\": false\n      }\n    },\n    \"fields\": {\n      \"name\": {\n        \"owner\": \"channel\",\n        \"auto_apply\": false\n      }\n    },\n    \"governance_gate\": {\n      \"enabled\": true,\n      \"require\": [\n        \"barcode\",\n        \"images\",\n        \"weight\"\n      ]\n    }\n  },\n  \"message\": \"Content sync settings updated.\"\n}"}]},{"name":"Get Channel Attribute Mappings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/integration-instances/{integrationInstance}/channel-attribute-mappings","host":["{{baseUrl}}"],"path":["api","integration-instances",":integrationInstance","channel-attribute-mappings"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"List the channel-native attributes this integration instance has actually synced, together with the SKU product fields they can be mapped onto, so channel content can be governed by content sync like any other field.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nDifferent channels speak different attribute vocabularies: some emit flat attribute codes (manufacturer, country_of_manufacture), others emit metafields keyed namespace.key (custom.care_instructions). Both arrive here as a code with a suggested human-readable label. Shopify additionally offers its product-organization fields (product_type, vendor, status) ahead of its metafields, so a classification kept in Shopify's own Product organization block can be mapped and governed the same way.\nsupported: false when the connected channel exposes no attribute vocabulary at all. The candidate list is then empty and there is nothing to map.\ncandidates are ranked by product_count (how many synced products carry that code), most-used first, and each carries a few sample_values so a high-signal code is easy to tell apart from an internal one.\nmapped_attribute_id / mapped_attribute_name are populated when the code is already mapped onto a catalog attribute. mapped_sku_field reports the target in the same vocabulary accepted when saving: attributes.{id} for a catalog attribute, or a product field such as brand or price.Retail.value.\nenabled reports whether that mapping is currently applied on sync.\n\ntargets lists every SKU field a code may be mapped onto, grouped as Basic Info, Physical, Supplier, Pricing Tiers, Supplier Pricing Tiers and Product Attributes. Pricing tiers, supplier pricing tiers and catalog attributes are read live, so tiers and attributes created after this integration was connected appear without a release.\nEach group carries governed, and each target carries governed_field. Content sync compares eight fields only: barcode, mpn, name, brand, attributes, images, weight and dimensions. A target whose governed_field is null — unit cost, default supplier, and every pricing tier target — is still written to the product on every sync, but drift is never reported on it.\n\nMapping is opt-in per integration instance. Until at least one mapping is enabled, the channel's attributes are never applied and no attribute drift is raised.\nRequires Bearer token.\n\nmissing_scopes lists any access scopes the channel refused while syncing attributes (for example read_metaobjects on Shopify). While a scope is missing, attributes that reference objects needing it sync as raw channel ids (gid://...) instead of names; grant the scope to the app in the channel's admin and run a product sync to clear it. Empty for channels that have no such notion.\nunresolved_reference on a candidate is true when its sampled values are still raw channel ids for that reason."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/integration-instances/{integrationInstance}/channel-attribute-mappings","host":["{{baseUrl}}"],"path":["api","integration-instances",":integrationInstance","channel-attribute-mappings"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"supported\": true,\n    \"missing_scopes\": [],\n    \"candidates\": [\n      {\n        \"code\": \"manufacturer\",\n        \"label\": \"Manufacturer\",\n        \"product_count\": 4128,\n        \"sample_values\": [\n          \"Breville\",\n          \"Sage\",\n          \"De'Longhi\"\n        ],\n        \"mapped_attribute_id\": 17,\n        \"mapped_attribute_name\": \"Manufacturer\",\n        \"mapped_sku_field\": \"attributes.17\",\n        \"enabled\": true,\n        \"unresolved_reference\": false\n      },\n      {\n        \"code\": \"supplier_cost\",\n        \"label\": \"Supplier Cost\",\n        \"product_count\": 3902,\n        \"sample_values\": [\n          \"18.40\",\n          \"7.25\",\n          \"112.00\"\n        ],\n        \"mapped_attribute_id\": null,\n        \"mapped_attribute_name\": null,\n        \"mapped_sku_field\": \"unit_cost\",\n        \"enabled\": true,\n        \"unresolved_reference\": false\n      },\n      {\n        \"code\": \"custom.care_instructions\",\n        \"label\": \"Custom: Care Instructions\",\n        \"product_count\": 812,\n        \"sample_values\": [\n          \"Hand wash\",\n          \"Machine wash cold\"\n        ],\n        \"mapped_attribute_id\": null,\n        \"mapped_attribute_name\": null,\n        \"mapped_sku_field\": null,\n        \"enabled\": false,\n        \"unresolved_reference\": false\n      }\n    ],\n    \"targets\": [\n      {\n        \"group\": \"Basic Info\",\n        \"governed\": true,\n        \"targets\": [\n          {\n            \"value\": \"name\",\n            \"label\": \"Title\",\n            \"governed_field\": \"name\",\n            \"attribute_id\": null\n          },\n          {\n            \"value\": \"brand\",\n            \"label\": \"Brand\",\n            \"governed_field\": \"brand\",\n            \"attribute_id\": null\n          },\n          {\n            \"value\": \"barcode\",\n            \"label\": \"Barcode\",\n            \"governed_field\": \"barcode\",\n            \"attribute_id\": null\n          },\n          {\n            \"value\": \"mpn\",\n            \"label\": \"MPN\",\n            \"governed_field\": \"mpn\",\n            \"attribute_id\": null\n          }\n        ]\n      },\n      {\n        \"group\": \"Physical\",\n        \"governed\": true,\n        \"targets\": [\n          {\n            \"value\": \"weight\",\n            \"label\": \"Weight\",\n            \"governed_field\": \"weight\",\n            \"attribute_id\": null\n          },\n          {\n            \"value\": \"length\",\n            \"label\": \"Length\",\n            \"governed_field\": \"dimensions\",\n            \"attribute_id\": null\n          },\n          {\n            \"value\": \"width\",\n            \"label\": \"Width\",\n            \"governed_field\": \"dimensions\",\n            \"attribute_id\": null\n          },\n          {\n            \"value\": \"height\",\n            \"label\": \"Height\",\n            \"governed_field\": \"dimensions\",\n            \"attribute_id\": null\n          }\n        ]\n      },\n      {\n        \"group\": \"Supplier\",\n        \"governed\": false,\n        \"targets\": [\n          {\n            \"value\": \"unit_cost\",\n            \"label\": \"Unit Cost\",\n            \"governed_field\": null,\n            \"attribute_id\": null\n          },\n          {\n            \"value\": \"default_supplier\",\n            \"label\": \"Default Supplier\",\n            \"governed_field\": null,\n            \"attribute_id\": null\n          }\n        ]\n      },\n      {\n        \"group\": \"Pricing Tiers\",\n        \"governed\": false,\n        \"targets\": [\n          {\n            \"value\": \"price.Retail.value\",\n            \"label\": \"Retail\",\n            \"governed_field\": null,\n            \"attribute_id\": null\n          },\n          {\n            \"value\": \"price.Wholesale.value\",\n            \"label\": \"Wholesale\",\n            \"governed_field\": null,\n            \"attribute_id\": null\n          }\n        ]\n      },\n      {\n        \"group\": \"Supplier Pricing Tiers\",\n        \"governed\": false,\n        \"targets\": [\n          {\n            \"value\": \"supplier_pricing.Standard Cost.value\",\n            \"label\": \"Standard Cost\",\n            \"governed_field\": null,\n            \"attribute_id\": null\n          }\n        ]\n      },\n      {\n        \"group\": \"Product Attributes\",\n        \"governed\": true,\n        \"targets\": [\n          {\n            \"value\": \"attributes.17\",\n            \"label\": \"Manufacturer\",\n            \"governed_field\": \"attributes\",\n            \"attribute_id\": 17\n          },\n          {\n            \"value\": \"attributes.58\",\n            \"label\": \"Care Instructions\",\n            \"governed_field\": \"attributes\",\n            \"attribute_id\": 58\n          }\n        ]\n      }\n    ]\n  }\n}"}]},{"name":"Update Channel Attribute Mappings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/integration-instances/{integrationInstance}/channel-attribute-mappings","host":["{{baseUrl}}"],"path":["api","integration-instances",":integrationInstance","channel-attribute-mappings"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Replace the instance's channel attribute mappings. The payload is authoritative: any code not present in mappings is unmapped, so send the full set you want kept, not just the changes.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nmappings: required array (send an empty array to clear every mapping on the instance).\nmappings[].code: required. The channel-native attribute code, exactly as returned by the candidates endpoint (manufacturer, custom.care_instructions).\nmappings[].label: optional human-readable label to remember the code by.\nEach mapping needs exactly one target, given one of three ways — attribute_id, attribute_name or sku_field. A mapping with none of the three is rejected with 422.\nmappings[].attribute_id: the existing catalog attribute this code maps onto.\nmappings[].attribute_name: name of a catalog attribute to map onto, provisioned if it does not exist yet. Matching is by exact name, so re-sending the same name re-uses the same attribute instead of creating a duplicate.\nmappings[].sku_field: any other product field target, taken from the targets list returned by the candidates endpoint — brand, mpn, weight, unit_cost, default_supplier, price.Retail.value, supplier_pricing.Standard Cost.value. The attributes.{id} form is also accepted and is stored as attribute_id, so a catalog attribute may be sent either way.\nA sku_field outside the published target list is ignored rather than errored, and because the payload is authoritative that leaves the code unmapped — read the response back to confirm what was stored.\nmappings[].enabled: optional, defaults to true. A disabled mapping is kept but stops being applied, which is the way to pause a mapping without losing the selection.\n\nOnly enabled mappings are applied on sync. Targets content sync governs (name, brand, barcode, mpn, weight, dimensions and catalog attributes) also feed drift detection, routed by the ownership configured in the content-sync settings. Pricing, supplier pricing, unit cost and default supplier targets are written on every sync but never compared.\nRequires Bearer token.","body":{"mode":"raw","raw":"{\n  \"mappings\": [\n    {\n      \"code\": \"manufacturer\",\n      \"label\": \"Manufacturer\",\n      \"attribute_id\": 17,\n      \"enabled\": true\n    },\n    {\n      \"code\": \"custom.care_instructions\",\n      \"label\": \"Custom: Care Instructions\",\n      \"attribute_name\": \"Care Instructions\",\n      \"enabled\": true\n    },\n    {\n      \"code\": \"supplier_cost\",\n      \"label\": \"Supplier Cost\",\n      \"sku_field\": \"unit_cost\",\n      \"enabled\": true\n    },\n    {\n      \"code\": \"rrp\",\n      \"label\": \"RRP\",\n      \"sku_field\": \"price.Retail.value\",\n      \"enabled\": false\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/integration-instances/{integrationInstance}/channel-attribute-mappings","host":["{{baseUrl}}"],"path":["api","integration-instances",":integrationInstance","channel-attribute-mappings"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"mappings\": [\n      {\n        \"id\": 41,\n        \"channel_attribute_code\": \"manufacturer\",\n        \"channel_attribute_label\": \"Manufacturer\",\n        \"attribute_id\": 17,\n        \"sku_field\": \"attributes.17\",\n        \"enabled\": true\n      },\n      {\n        \"id\": 42,\n        \"channel_attribute_code\": \"custom.care_instructions\",\n        \"channel_attribute_label\": \"Custom: Care Instructions\",\n        \"attribute_id\": 58,\n        \"sku_field\": \"attributes.58\",\n        \"enabled\": true\n      },\n      {\n        \"id\": 43,\n        \"channel_attribute_code\": \"supplier_cost\",\n        \"channel_attribute_label\": \"Supplier Cost\",\n        \"attribute_id\": null,\n        \"sku_field\": \"unit_cost\",\n        \"enabled\": true\n      },\n      {\n        \"id\": 44,\n        \"channel_attribute_code\": \"rrp\",\n        \"channel_attribute_label\": \"RRP\",\n        \"attribute_id\": null,\n        \"sku_field\": \"price.Retail.value\",\n        \"enabled\": false\n      }\n    ]\n  },\n  \"message\": \"Channel attribute mappings updated.\"\n}"}]},{"name":"List Content Changes for Listing","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/content-changes","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","content-changes"],"variable":[{"key":"productListing","value":"","description":"The product listing ID."}]},"description":"Open (pending, conflict, or error) content changes detected on one listing, plus the listing's cached content-sync status. Each change carries the current catalog value and the channel value side by side so the difference can be reviewed field by field.\nRequires Bearer token.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/content-changes","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","content-changes"],"variable":[{"key":"productListing","value":"","description":"The product listing ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product_listing_id\": 315,\n    \"content_sync_status\": \"pending_review\",\n    \"content_sync_checked_at\": \"2026-07-29T14:05:12.000000Z\",\n    \"last_inbound_synced_at\": \"2026-07-28T02:11:40.000000Z\",\n    \"changes\": [\n      {\n        \"id\": 42,\n        \"product_listing_id\": 315,\n        \"product_id\": 88,\n        \"field\": \"name\",\n        \"field_group\": \"descriptive\",\n        \"direction\": \"inbound\",\n        \"owner_at_detection\": \"channel\",\n        \"recommended_action\": \"apply\",\n        \"catalog_value\": \"Stainless Water Bottle 750ml\",\n        \"channel_value\": \"Stainless Steel Water Bottle - 750 ml\",\n        \"status\": \"pending\",\n        \"source\": \"webhook\",\n        \"is_risky\": true,\n        \"conflict_resolution\": null,\n        \"error_message\": null,\n        \"detected_at\": \"2026-07-29T14:05:12.000000Z\",\n        \"resolved_at\": null,\n        \"listing\": {\n          \"id\": 315,\n          \"listing_sku\": \"SWB-750\",\n          \"title\": \"Stainless Steel Water Bottle - 750 ml\",\n          \"content_sync_status\": \"pending_review\",\n          \"integration_instance_id\": 3,\n          \"integration_instance_name\": \"Blue Bottle Retail\",\n          \"integration_name\": \"Shopify\"\n        },\n        \"product\": {\n          \"id\": 88,\n          \"sku\": \"SWB-750\",\n          \"name\": \"Stainless Water Bottle 750ml\"\n        }\n      },\n      {\n        \"id\": 43,\n        \"product_listing_id\": 315,\n        \"product_id\": 88,\n        \"field\": \"weight\",\n        \"field_group\": \"physical\",\n        \"direction\": \"inbound\",\n        \"owner_at_detection\": \"channel\",\n        \"recommended_action\": \"apply\",\n        \"catalog_value\": {\n          \"value\": 11.2,\n          \"unit\": \"oz\"\n        },\n        \"channel_value\": {\n          \"value\": 340,\n          \"unit\": \"g\"\n        },\n        \"status\": \"pending\",\n        \"source\": \"webhook\",\n        \"is_risky\": false,\n        \"conflict_resolution\": null,\n        \"error_message\": null,\n        \"detected_at\": \"2026-07-29T14:05:12.000000Z\",\n        \"resolved_at\": null,\n        \"listing\": {\n          \"id\": 315,\n          \"listing_sku\": \"SWB-750\",\n          \"title\": \"Stainless Steel Water Bottle - 750 ml\",\n          \"content_sync_status\": \"pending_review\",\n          \"integration_instance_id\": 3,\n          \"integration_instance_name\": \"Blue Bottle Retail\",\n          \"integration_name\": \"Shopify\"\n        },\n        \"product\": {\n          \"id\": 88,\n          \"sku\": \"SWB-750\",\n          \"name\": \"Stainless Water Bottle 750ml\"\n        }\n      }\n    ]\n  }\n}"}]},{"name":"Get Content Change History","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/content-changes/history","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","content-changes","history"],"variable":[{"key":"productListing","value":"","description":"The product listing ID."}]},"description":"Full audit trail of content changes for a listing, newest first — including applied, auto-applied, ignored, superseded, rolled-back, and resolved-conflict entries. Every filter is optional; the unfiltered call returns the most recent entries (default 200, max 500).\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAlongside the entries, the response returns the set of users who have ever resolved a change on this listing, so a \"resolved by\" filter can be built without a second call.\nRequires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/content-changes/history","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","content-changes","history"],"variable":[{"key":"productListing","value":"","description":"The product listing ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"changes\": [\n      {\n        \"id\": 61,\n        \"product_listing_id\": 315,\n        \"product_id\": 88,\n        \"field\": \"name\",\n        \"field_group\": \"descriptive\",\n        \"direction\": \"rollback\",\n        \"owner_at_detection\": \"channel\",\n        \"recommended_action\": \"informational\",\n        \"catalog_value\": \"Stainless Steel Water Bottle - 750 ml\",\n        \"channel_value\": \"Stainless Water Bottle 750ml\",\n        \"status\": \"rolled_back\",\n        \"source\": \"manual\",\n        \"is_risky\": true,\n        \"conflict_resolution\": null,\n        \"error_message\": null,\n        \"detected_at\": \"2026-07-29T16:20:44.000000Z\",\n        \"resolved_at\": \"2026-07-29T16:20:44.000000Z\",\n        \"resolved_by\": {\n          \"id\": 1,\n          \"name\": \"Jane Doe\"\n        }\n      },\n      {\n        \"id\": 42,\n        \"product_listing_id\": 315,\n        \"product_id\": 88,\n        \"field\": \"name\",\n        \"field_group\": \"descriptive\",\n        \"direction\": \"inbound\",\n        \"owner_at_detection\": \"channel\",\n        \"recommended_action\": \"apply\",\n        \"catalog_value\": \"Stainless Water Bottle 750ml\",\n        \"channel_value\": \"Stainless Steel Water Bottle - 750 ml\",\n        \"status\": \"rolled_back\",\n        \"source\": \"webhook\",\n        \"is_risky\": true,\n        \"conflict_resolution\": null,\n        \"error_message\": null,\n        \"detected_at\": \"2026-07-29T14:05:12.000000Z\",\n        \"resolved_at\": \"2026-07-29T16:20:44.000000Z\",\n        \"resolved_by\": {\n          \"id\": 1,\n          \"name\": \"Jane Doe\"\n        }\n      }\n    ],\n    \"resolvers\": [\n      {\n        \"id\": 1,\n        \"name\": \"Jane Doe\"\n      }\n    ]\n  },\n  \"message\": null,\n  \"status\": \"success\"\n}"}]},{"name":"Roll Back Content Change","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/content-changes/{contentChange}/rollback","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","content-changes",":contentChange","rollback"],"variable":[{"key":"productListing","value":"","description":"The product listing ID."},{"key":"contentChange","value":"","description":"The content change ID."}]},"description":"Restore the catalog value that an applied change replaced. Nothing is deleted: the original entry is marked rolled_back and a new audit entry records the restore, so the history stays complete.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nOnly a change that actually reached the catalog can be rolled back (applied, auto-applied, or a previous rollback) — anything else returns 409. The change must belong to the listing in the path, otherwise 404. The listing's content baseline moves with the restore, so the next sync does not read the rollback as fresh drift.\nRequires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/content-changes/{contentChange}/rollback","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","content-changes",":contentChange","rollback"],"variable":[{"key":"productListing","value":"","description":"The product listing ID."},{"key":"contentChange","value":"","description":"The content change ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product_listing_id\": 315,\n    \"content_sync_status\": \"in_sync\",\n    \"content_sync_checked_at\": \"2026-07-29T14:05:12.000000Z\",\n    \"last_inbound_synced_at\": \"2026-07-28T02:11:40.000000Z\",\n    \"changes\": [],\n    \"rollback\": {\n      \"id\": 61,\n      \"product_listing_id\": 315,\n      \"product_id\": 88,\n      \"field\": \"name\",\n      \"field_group\": \"descriptive\",\n      \"direction\": \"rollback\",\n      \"owner_at_detection\": \"channel\",\n      \"recommended_action\": \"informational\",\n      \"catalog_value\": \"Stainless Steel Water Bottle - 750 ml\",\n      \"channel_value\": \"Stainless Water Bottle 750ml\",\n      \"status\": \"rolled_back\",\n      \"source\": \"manual\",\n      \"is_risky\": true,\n      \"conflict_resolution\": null,\n      \"error_message\": null,\n      \"detected_at\": \"2026-07-29T16:20:44.000000Z\",\n      \"resolved_at\": \"2026-07-29T16:20:44.000000Z\",\n      \"resolved_by\": {\n        \"id\": 1,\n        \"name\": \"Jane Doe\"\n      }\n    }\n  },\n  \"message\": \"Name rolled back to its previous value.\",\n  \"status\": \"success\"\n}"}]},{"name":"Apply Content Changes","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/content-changes/apply","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","content-changes","apply"],"variable":[{"key":"productListing","value":"","description":"The product listing ID."}]},"description":"Apply open channel changes to the catalog product. Scope by change_ids or by fields; omit both to apply every open change on the listing. Conflict rows are skipped unless conflict_resolution is provided (mine keeps the catalog value, theirs applies the channel value, both applies the channel value while preserving the catalog value for outbound re-push).\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nApplying a change updates the catalog product (name, barcode, mpn, brand, attributes, images, weight, or dimensions — weight is converted into the catalog's unit) and advances the listing's content baseline.\nRequires Bearer token.","body":{"mode":"raw","raw":"{\n  \"change_ids\": [\n    42,\n    43\n  ],\n  \"conflict_resolution\": \"theirs\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/content-changes/apply","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","content-changes","apply"],"variable":[{"key":"productListing","value":"","description":"The product listing ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product_listing_id\": 315,\n    \"content_sync_status\": \"in_sync\",\n    \"content_sync_checked_at\": \"2026-07-29T14:05:12.000000Z\",\n    \"last_inbound_synced_at\": \"2026-07-28T02:11:40.000000Z\",\n    \"changes\": [],\n    \"result\": {\n      \"applied\": 2,\n      \"skipped\": 0,\n      \"conflicts\": 0,\n      \"resolved\": 0\n    }\n  },\n  \"message\": \"2 change(s) applied.\"\n}"}]},{"name":"Ignore Content Changes","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/content-changes/ignore","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","content-changes","ignore"],"variable":[{"key":"productListing","value":"","description":"The product listing ID."}]},"description":"Dismiss open changes without touching the catalog. The suggestion stays dismissed until the channel value changes again — the same channel state is never re-flagged. Scope by change_ids or fields; omit both to ignore everything open on the listing.\nRequires Bearer token.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"fields\": [\n    \"images\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/content-changes/ignore","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","content-changes","ignore"],"variable":[{"key":"productListing","value":"","description":"The product listing ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product_listing_id\": 315,\n    \"content_sync_status\": \"pending_review\",\n    \"content_sync_checked_at\": \"2026-07-29T14:05:12.000000Z\",\n    \"last_inbound_synced_at\": \"2026-07-28T02:11:40.000000Z\",\n    \"changes\": [\n      {\n        \"id\": 42,\n        \"product_listing_id\": 315,\n        \"product_id\": 88,\n        \"field\": \"name\",\n        \"field_group\": \"descriptive\",\n        \"direction\": \"inbound\",\n        \"owner_at_detection\": \"channel\",\n        \"recommended_action\": \"apply\",\n        \"catalog_value\": \"Stainless Water Bottle 750ml\",\n        \"channel_value\": \"Stainless Steel Water Bottle - 750 ml\",\n        \"status\": \"pending\",\n        \"source\": \"webhook\",\n        \"is_risky\": true,\n        \"conflict_resolution\": null,\n        \"error_message\": null,\n        \"detected_at\": \"2026-07-29T14:05:12.000000Z\",\n        \"resolved_at\": null,\n        \"listing\": {\n          \"id\": 315,\n          \"listing_sku\": \"SWB-750\",\n          \"title\": \"Stainless Steel Water Bottle - 750 ml\",\n          \"content_sync_status\": \"pending_review\",\n          \"integration_instance_id\": 3,\n          \"integration_instance_name\": \"Blue Bottle Retail\",\n          \"integration_name\": \"Shopify\"\n        },\n        \"product\": {\n          \"id\": 88,\n          \"sku\": \"SWB-750\",\n          \"name\": \"Stainless Water Bottle 750ml\"\n        }\n      },\n      {\n        \"id\": 43,\n        \"product_listing_id\": 315,\n        \"product_id\": 88,\n        \"field\": \"weight\",\n        \"field_group\": \"physical\",\n        \"direction\": \"inbound\",\n        \"owner_at_detection\": \"channel\",\n        \"recommended_action\": \"apply\",\n        \"catalog_value\": {\n          \"value\": 11.2,\n          \"unit\": \"oz\"\n        },\n        \"channel_value\": {\n          \"value\": 340,\n          \"unit\": \"g\"\n        },\n        \"status\": \"pending\",\n        \"source\": \"webhook\",\n        \"is_risky\": false,\n        \"conflict_resolution\": null,\n        \"error_message\": null,\n        \"detected_at\": \"2026-07-29T14:05:12.000000Z\",\n        \"resolved_at\": null,\n        \"listing\": {\n          \"id\": 315,\n          \"listing_sku\": \"SWB-750\",\n          \"title\": \"Stainless Steel Water Bottle - 750 ml\",\n          \"content_sync_status\": \"pending_review\",\n          \"integration_instance_id\": 3,\n          \"integration_instance_name\": \"Blue Bottle Retail\",\n          \"integration_name\": \"Shopify\"\n        },\n        \"product\": {\n          \"id\": 88,\n          \"sku\": \"SWB-750\",\n          \"name\": \"Stainless Water Bottle 750ml\"\n        }\n      }\n    ]\n  },\n  \"message\": \"1 change(s) ignored.\"\n}"}]},{"name":"Reconcile Listing Content","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/content-changes/reconcile","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","content-changes","reconcile"],"variable":[{"key":"productListing","value":"","description":"The product listing ID."}]},"description":"Re-compare this listing's channel content against the catalog right now and return the fresh set of open changes. Useful after editing the product on the channel when you don't want to wait for the scheduled reconcile.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody parameters:\n- `force` (boolean, optional, default `false`) — run a full comparison instead of an incremental one.\n\nBy default the comparison only reports fields the channel has changed since the last sync, so a difference that already existed when content sync was first enabled for the listing never surfaces. Send `force: true` to diff every governed field against the catalog regardless of whether the channel content moved — use it to backfill after enabling a new field group, or after importing a product whose channel values were never compared.\nRequires Bearer token.","body":{"mode":"raw","raw":"{\n  \"force\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/content-changes/reconcile","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","content-changes","reconcile"],"variable":[{"key":"productListing","value":"","description":"The product listing ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product_listing_id\": 315,\n    \"content_sync_status\": \"pending_review\",\n    \"content_sync_checked_at\": \"2026-07-29T14:05:12.000000Z\",\n    \"last_inbound_synced_at\": \"2026-07-28T02:11:40.000000Z\",\n    \"changes\": [\n      {\n        \"id\": 42,\n        \"product_listing_id\": 315,\n        \"product_id\": 88,\n        \"field\": \"name\",\n        \"field_group\": \"descriptive\",\n        \"direction\": \"inbound\",\n        \"owner_at_detection\": \"channel\",\n        \"recommended_action\": \"apply\",\n        \"catalog_value\": \"Stainless Water Bottle 750ml\",\n        \"channel_value\": \"Stainless Steel Water Bottle - 750 ml\",\n        \"status\": \"pending\",\n        \"source\": \"webhook\",\n        \"is_risky\": true,\n        \"conflict_resolution\": null,\n        \"error_message\": null,\n        \"detected_at\": \"2026-07-29T14:05:12.000000Z\",\n        \"resolved_at\": null,\n        \"listing\": {\n          \"id\": 315,\n          \"listing_sku\": \"SWB-750\",\n          \"title\": \"Stainless Steel Water Bottle - 750 ml\",\n          \"content_sync_status\": \"pending_review\",\n          \"integration_instance_id\": 3,\n          \"integration_instance_name\": \"Blue Bottle Retail\",\n          \"integration_name\": \"Shopify\"\n        },\n        \"product\": {\n          \"id\": 88,\n          \"sku\": \"SWB-750\",\n          \"name\": \"Stainless Water Bottle 750ml\"\n        }\n      },\n      {\n        \"id\": 43,\n        \"product_listing_id\": 315,\n        \"product_id\": 88,\n        \"field\": \"weight\",\n        \"field_group\": \"physical\",\n        \"direction\": \"inbound\",\n        \"owner_at_detection\": \"channel\",\n        \"recommended_action\": \"apply\",\n        \"catalog_value\": {\n          \"value\": 11.2,\n          \"unit\": \"oz\"\n        },\n        \"channel_value\": {\n          \"value\": 340,\n          \"unit\": \"g\"\n        },\n        \"status\": \"pending\",\n        \"source\": \"webhook\",\n        \"is_risky\": false,\n        \"conflict_resolution\": null,\n        \"error_message\": null,\n        \"detected_at\": \"2026-07-29T14:05:12.000000Z\",\n        \"resolved_at\": null,\n        \"listing\": {\n          \"id\": 315,\n          \"listing_sku\": \"SWB-750\",\n          \"title\": \"Stainless Steel Water Bottle - 750 ml\",\n          \"content_sync_status\": \"pending_review\",\n          \"integration_instance_id\": 3,\n          \"integration_instance_name\": \"Blue Bottle Retail\",\n          \"integration_name\": \"Shopify\"\n        },\n        \"product\": {\n          \"id\": 88,\n          \"sku\": \"SWB-750\",\n          \"name\": \"Stainless Water Bottle 750ml\"\n        }\n      }\n    ]\n  },\n  \"message\": \"Listing reconciled against the channel.\"\n}"}]},{"name":"Get Listing Content Rules","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/content-sync-rules","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","content-sync-rules"],"variable":[{"key":"productListing","value":"","description":"The product listing ID."}]},"description":"Get the content ownership rules in force for a single product listing, and where each one comes from.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA listing normally follows its sales channel's content-sync settings. This endpoint returns three things side by side: `instance_defaults` (what the channel says), `overrides` (what this listing has been customised to say), and `effective` (the resolved answer for every governed field).\n\nEach entry in `effective` carries a `source` naming the level that decided it, most specific first: listing_field, listing_group, instance_field, instance_group, default. `inherited` is false once the listing itself decided the field, true while it still follows the channel.\n`risky` marks a field whose change needs a human look before it is applied in bulk.\nGoverned fields: barcode and mpn (identity), name, brand and attributes (descriptive), images (media), weight and dimensions (physical).\nOwnership values: catalog (channel edits are flagged only), channel (channel edits can be pulled into the catalog), two_way (both sides may edit; simultaneous edits become conflicts).\n`enabled` reflects the channel — rules can be saved on a listing whose channel has content sync turned off, they simply are not acted on until it is enabled.\nRequires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/content-sync-rules","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","content-sync-rules"],"variable":[{"key":"productListing","value":"","description":"The product listing ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product_listing_id\": 8412,\n    \"sales_channel_name\": \"Amazon US\",\n    \"enabled\": true,\n    \"instance_defaults\": {\n      \"groups\": {\n        \"identity\": {\n          \"owner\": \"catalog\",\n          \"auto_apply\": false\n        },\n        \"descriptive\": {\n          \"owner\": \"channel\",\n          \"auto_apply\": true\n        },\n        \"media\": {\n          \"owner\": \"catalog\",\n          \"auto_apply\": false\n        },\n        \"physical\": {\n          \"owner\": \"catalog\",\n          \"auto_apply\": false\n        }\n      },\n      \"fields\": {\n        \"name\": {\n          \"owner\": \"two_way\",\n          \"auto_apply\": false\n        }\n      }\n    },\n    \"overrides\": {\n      \"groups\": {\n        \"media\": {\n          \"owner\": \"channel\",\n          \"auto_apply\": false\n        }\n      },\n      \"fields\": {}\n    },\n    \"effective\": [\n      {\n        \"field\": \"barcode\",\n        \"group\": \"identity\",\n        \"risky\": false,\n        \"owner\": \"catalog\",\n        \"auto_apply\": false,\n        \"source\": \"instance_group\",\n        \"inherited\": true\n      },\n      {\n        \"field\": \"mpn\",\n        \"group\": \"identity\",\n        \"risky\": false,\n        \"owner\": \"catalog\",\n        \"auto_apply\": false,\n        \"source\": \"instance_group\",\n        \"inherited\": true\n      },\n      {\n        \"field\": \"name\",\n        \"group\": \"descriptive\",\n        \"risky\": true,\n        \"owner\": \"two_way\",\n        \"auto_apply\": false,\n        \"source\": \"instance_field\",\n        \"inherited\": true\n      },\n      {\n        \"field\": \"brand\",\n        \"group\": \"descriptive\",\n        \"risky\": false,\n        \"owner\": \"channel\",\n        \"auto_apply\": true,\n        \"source\": \"instance_group\",\n        \"inherited\": true\n      },\n      {\n        \"field\": \"attributes\",\n        \"group\": \"descriptive\",\n        \"risky\": false,\n        \"owner\": \"channel\",\n        \"auto_apply\": true,\n        \"source\": \"instance_group\",\n        \"inherited\": true\n      },\n      {\n        \"field\": \"images\",\n        \"group\": \"media\",\n        \"risky\": false,\n        \"owner\": \"channel\",\n        \"auto_apply\": false,\n        \"source\": \"listing_group\",\n        \"inherited\": false\n      },\n      {\n        \"field\": \"weight\",\n        \"group\": \"physical\",\n        \"risky\": false,\n        \"owner\": \"catalog\",\n        \"auto_apply\": false,\n        \"source\": \"instance_group\",\n        \"inherited\": true\n      },\n      {\n        \"field\": \"dimensions\",\n        \"group\": \"physical\",\n        \"risky\": false,\n        \"owner\": \"catalog\",\n        \"auto_apply\": false,\n        \"source\": \"instance_group\",\n        \"inherited\": true\n      }\n    ]\n  }\n}"}]},{"name":"Update Listing Content Rules","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/content-sync-rules","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","content-sync-rules"],"variable":[{"key":"productListing","value":"","description":"The product listing ID."}]},"description":"Customise the content ownership rules for a single product listing, overriding what its sales channel says.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nSend only what should differ from the channel: `groups` keyed by field group and `fields` keyed by field name, each holding an `owner` and an optional `auto_apply`. Anything left out keeps following the channel, so a later change to the channel's defaults still flows through to it.\n\nBoth `groups` and `fields` are required keys — send them as empty objects to clear every customisation and put the listing back on the channel defaults.\nField-level entries beat group-level entries, and anything set here beats the channel.\nGroup keys: identity, descriptive, media, physical. Field keys: barcode, mpn, name, brand, attributes, images, weight, dimensions.\nOwnership values: catalog (channel edits are flagged only), channel (channel edits can be pulled into the catalog), two_way (both sides may edit; simultaneous edits become conflicts).\nUnknown group keys, unknown field keys, and unknown ownership values are rejected with a 422.\nThe full resolved rule set is returned, exactly as the GET returns it.\nRequires Bearer token.","body":{"mode":"raw","raw":"{\n  \"groups\": {\n    \"descriptive\": {\n      \"owner\": \"channel\",\n      \"auto_apply\": false\n    }\n  },\n  \"fields\": {\n    \"name\": {\n      \"owner\": \"catalog\",\n      \"auto_apply\": false\n    }\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/content-sync-rules","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","content-sync-rules"],"variable":[{"key":"productListing","value":"","description":"The product listing ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product_listing_id\": 8412,\n    \"sales_channel_name\": \"Amazon US\",\n    \"enabled\": true,\n    \"instance_defaults\": {\n      \"groups\": {\n        \"identity\": {\n          \"owner\": \"catalog\",\n          \"auto_apply\": false\n        },\n        \"descriptive\": {\n          \"owner\": \"catalog\",\n          \"auto_apply\": false\n        },\n        \"media\": {\n          \"owner\": \"catalog\",\n          \"auto_apply\": false\n        },\n        \"physical\": {\n          \"owner\": \"catalog\",\n          \"auto_apply\": false\n        }\n      },\n      \"fields\": {}\n    },\n    \"overrides\": {\n      \"groups\": {\n        \"descriptive\": {\n          \"owner\": \"channel\",\n          \"auto_apply\": false\n        }\n      },\n      \"fields\": {\n        \"name\": {\n          \"owner\": \"catalog\",\n          \"auto_apply\": false\n        }\n      }\n    },\n    \"effective\": [\n      {\n        \"field\": \"barcode\",\n        \"group\": \"identity\",\n        \"risky\": false,\n        \"owner\": \"catalog\",\n        \"auto_apply\": false,\n        \"source\": \"instance_group\",\n        \"inherited\": true\n      },\n      {\n        \"field\": \"mpn\",\n        \"group\": \"identity\",\n        \"risky\": false,\n        \"owner\": \"catalog\",\n        \"auto_apply\": false,\n        \"source\": \"instance_group\",\n        \"inherited\": true\n      },\n      {\n        \"field\": \"name\",\n        \"group\": \"descriptive\",\n        \"risky\": true,\n        \"owner\": \"catalog\",\n        \"auto_apply\": false,\n        \"source\": \"listing_field\",\n        \"inherited\": false\n      },\n      {\n        \"field\": \"brand\",\n        \"group\": \"descriptive\",\n        \"risky\": false,\n        \"owner\": \"channel\",\n        \"auto_apply\": false,\n        \"source\": \"listing_group\",\n        \"inherited\": false\n      },\n      {\n        \"field\": \"attributes\",\n        \"group\": \"descriptive\",\n        \"risky\": false,\n        \"owner\": \"channel\",\n        \"auto_apply\": false,\n        \"source\": \"listing_group\",\n        \"inherited\": false\n      },\n      {\n        \"field\": \"images\",\n        \"group\": \"media\",\n        \"risky\": false,\n        \"owner\": \"catalog\",\n        \"auto_apply\": false,\n        \"source\": \"instance_group\",\n        \"inherited\": true\n      },\n      {\n        \"field\": \"weight\",\n        \"group\": \"physical\",\n        \"risky\": false,\n        \"owner\": \"catalog\",\n        \"auto_apply\": false,\n        \"source\": \"instance_group\",\n        \"inherited\": true\n      },\n      {\n        \"field\": \"dimensions\",\n        \"group\": \"physical\",\n        \"risky\": false,\n        \"owner\": \"catalog\",\n        \"auto_apply\": false,\n        \"source\": \"instance_group\",\n        \"inherited\": true\n      }\n    ]\n  },\n  \"message\": \"Listing content rules updated.\"\n}"}]},{"name":"List Content Drift","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/content-sync/drift","host":["{{baseUrl}}"],"path":["api","content-sync","drift"]},"description":"Paginated feed of detected content changes across every listing and channel — the review inbox for channel edits awaiting a decision. Defaults to open changes (pending, conflict, error); pass filter[status] to browse history.\nRequires Bearer token.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/content-sync/drift","host":["{{baseUrl}}"],"path":["api","content-sync","drift"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 42,\n      \"product_listing_id\": 315,\n      \"product_id\": 88,\n      \"field\": \"name\",\n      \"field_group\": \"descriptive\",\n      \"direction\": \"inbound\",\n      \"owner_at_detection\": \"channel\",\n      \"recommended_action\": \"apply\",\n      \"catalog_value\": \"Stainless Water Bottle 750ml\",\n      \"channel_value\": \"Stainless Steel Water Bottle - 750 ml\",\n      \"status\": \"pending\",\n      \"source\": \"webhook\",\n      \"is_risky\": true,\n      \"conflict_resolution\": null,\n      \"error_message\": null,\n      \"detected_at\": \"2026-07-29T14:05:12.000000Z\",\n      \"resolved_at\": null,\n      \"listing\": {\n        \"id\": 315,\n        \"listing_sku\": \"SWB-750\",\n        \"title\": \"Stainless Steel Water Bottle - 750 ml\",\n        \"content_sync_status\": \"pending_review\",\n        \"integration_instance_id\": 3,\n        \"integration_instance_name\": \"Blue Bottle Retail\",\n        \"integration_name\": \"Shopify\"\n      },\n      \"product\": {\n        \"id\": 88,\n        \"sku\": \"SWB-750\",\n        \"name\": \"Stainless Water Bottle 750ml\"\n      }\n    },\n    {\n      \"id\": 43,\n      \"product_listing_id\": 315,\n      \"product_id\": 88,\n      \"field\": \"weight\",\n      \"field_group\": \"physical\",\n      \"direction\": \"inbound\",\n      \"owner_at_detection\": \"channel\",\n      \"recommended_action\": \"apply\",\n      \"catalog_value\": {\n        \"value\": 11.2,\n        \"unit\": \"oz\"\n      },\n      \"channel_value\": {\n        \"value\": 340,\n        \"unit\": \"g\"\n      },\n      \"status\": \"pending\",\n      \"source\": \"webhook\",\n      \"is_risky\": false,\n      \"conflict_resolution\": null,\n      \"error_message\": null,\n      \"detected_at\": \"2026-07-29T14:05:12.000000Z\",\n      \"resolved_at\": null,\n      \"listing\": {\n        \"id\": 315,\n        \"listing_sku\": \"SWB-750\",\n        \"title\": \"Stainless Steel Water Bottle - 750 ml\",\n        \"content_sync_status\": \"pending_review\",\n        \"integration_instance_id\": 3,\n        \"integration_instance_name\": \"Blue Bottle Retail\",\n        \"integration_name\": \"Shopify\"\n      },\n      \"product\": {\n        \"id\": 88,\n        \"sku\": \"SWB-750\",\n        \"name\": \"Stainless Water Bottle 750ml\"\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"https://example.test/api/content-sync/drift?page=1\",\n  \"from\": 1,\n  \"last_page\": 4,\n  \"last_page_url\": \"https://example.test/api/content-sync/drift?page=4\",\n  \"links\": [],\n  \"next_page_url\": \"https://example.test/api/content-sync/drift?page=2\",\n  \"path\": \"https://example.test/api/content-sync/drift\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 10,\n  \"total\": 37\n}"}]},{"name":"Get Content Sync Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/content-sync/summary","host":["{{baseUrl}}"],"path":["api","content-sync","summary"]},"description":"Open-change counts for badges and summary cards: totals by status (pending, conflict, error), the number of listings affected, and a breakdown by field group.\nAlso returns `content_sync_instance_ids` — the integration instance ids that currently have content sync turned on. Content sync is opt-in per sales channel, so a reconcile scoped to any other instance has nothing to compare; use this list to scope reconcile requests to comparable channels.\nRequires Bearer token.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/content-sync/summary","host":["{{baseUrl}}"],"path":["api","content-sync","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"pending\": 31,\n    \"conflict\": 4,\n    \"error\": 2,\n    \"listings\": 18,\n    \"by_field_group\": {\n      \"descriptive\": 21,\n      \"media\": 9,\n      \"physical\": 5,\n      \"identity\": 2\n    },\n    \"content_sync_instance_ids\": [\n      20,\n      21\n    ]\n  }\n}"}]},{"name":"List Drift Groups","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/content-sync/groups?group_by=field","host":["{{baseUrl}}"],"path":["api","content-sync","groups"],"query":[{"key":"group_by","value":"field","description":"Bucket axis: field (default) or field_group.","disabled":true}]},"description":"Pre-bucketed view of the same drift feed, so a templated update such as a weight change across 120 listings can be reviewed as one row instead of 120. Buckets honour every filter the feed accepts, so a bucket's counts always match what listing the same filters returns. has_risky marks buckets containing high-impact fields that must be reviewed one listing at a time.\nRequires Bearer token.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/content-sync/groups?group_by=field","host":["{{baseUrl}}"],"path":["api","content-sync","groups"],"query":[{"key":"group_by","value":"field","description":"Bucket axis: field (default) or field_group.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"group_by\": \"field\",\n    \"groups\": [\n      {\n        \"key\": \"weight\",\n        \"field\": \"weight\",\n        \"field_group\": \"physical\",\n        \"has_risky\": false,\n        \"change_count\": 128,\n        \"listing_count\": 120\n      },\n      {\n        \"key\": \"images\",\n        \"field\": \"images\",\n        \"field_group\": \"media\",\n        \"has_risky\": false,\n        \"change_count\": 42,\n        \"listing_count\": 39\n      },\n      {\n        \"key\": \"name\",\n        \"field\": \"name\",\n        \"field_group\": \"descriptive\",\n        \"has_risky\": true,\n        \"change_count\": 11,\n        \"listing_count\": 11\n      }\n    ]\n  }\n}"}]},{"name":"Apply Content Change Group","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/content-sync/group-apply?filter[field]=weight","host":["{{baseUrl}}"],"path":["api","content-sync","group-apply"],"query":[{"key":"filter[field]","value":"weight","description":"Exact field filter: barcode, mpn, brand, attributes, images, weight, or dimensions. Required unless filter[field_group] or filter[integration_instance_ids] is supplied.","disabled":true}]},"description":"Applies every open change in one bucket in the background, without listing ids. The bucket is described by the same filters used to list it, so a bucket of several thousand listings needs no enumeration by the caller. Send the filters in the QUERY STRING — filters in the request body are ignored, which would silently widen the apply. The apply must be scoped by at least one of filter[field], filter[field_group], or filter[integration_instance_ids]; an unscoped call returns 422. High-impact fields such as product name are rejected with 422 and must be applied one listing at a time. Very large buckets are applied in passes: listings_queued below listings_total means the call should be repeated to continue. Follow progress with the returned tracked_job_log_id.\nRequires Bearer token.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/content-sync/group-apply?filter[field]=weight","host":["{{baseUrl}}"],"path":["api","content-sync","group-apply"],"query":[{"key":"filter[field]","value":"weight","description":"Exact field filter: barcode, mpn, brand, attributes, images, weight, or dimensions. Required unless filter[field_group] or filter[integration_instance_ids] is supplied.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"message\": \"Group apply started — follow its progress from the background activity list.\",\n  \"data\": {\n    \"tracked_job_log_id\": 9182,\n    \"listings_queued\": 120,\n    \"listings_total\": 120\n  }\n}"}]},{"name":"Bulk Apply Content Changes","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/content-sync/bulk-apply","host":["{{baseUrl}}"],"path":["api","content-sync","bulk-apply"]},"description":"Apply pending changes across many listings as a background job. High-impact fields (for example product name) are excluded unless include_risky is true. Optionally narrow to specific fields.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the background job id; poll the jobs API to track progress and read the completion summary (changes applied, skipped, conflicts remaining).\nRequires Bearer token.\nLimits: up to 1000 listing ids per call; ids must be unique; field names must be one of barcode, mpn, name, brand, attributes, images, weight, dimensions.","body":{"mode":"raw","raw":"{\n  \"product_listing_ids\": [\n    315,\n    316,\n    340\n  ],\n  \"fields\": [\n    \"images\",\n    \"weight\"\n  ],\n  \"include_risky\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/content-sync/bulk-apply","host":["{{baseUrl}}"],"path":["api","content-sync","bulk-apply"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 912\n  },\n  \"message\": \"Bulk apply started - track progress in the job tray.\"\n}"}]},{"name":"Bulk Ignore Content Changes","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/content-sync/bulk-ignore","host":["{{baseUrl}}"],"path":["api","content-sync","bulk-ignore"]},"description":"Dismiss pending changes across many listings in one call. Runs inline (no background job) since only change statuses are updated — the catalog is untouched.\nRequires Bearer token.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"product_listing_ids\": [\n    315,\n    316\n  ],\n  \"fields\": [\n    \"name\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/content-sync/bulk-ignore","host":["{{baseUrl}}"],"path":["api","content-sync","bulk-ignore"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"ignored\": 5\n  },\n  \"message\": \"5 change(s) ignored.\"\n}"}]},{"name":"Reconcile Content Drift","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/content-sync/reconcile","host":["{{baseUrl}}"],"path":["api","content-sync","reconcile"]},"description":"Start an on-demand reconcile as a background job: re-pull channel content and re-compare against the catalog for every enabled instance, or a scoped set of instances or listings. This is the same sweep that runs on the configured schedule — it catches channels without update webhooks and any missed webhook deliveries.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody parameters:\n- `integration_instance_ids` (array of integer, optional) — limit the sweep to these integration instances.\n- `product_listing_ids` (array of integer, optional) — limit the sweep to these listings.\n- `force` (boolean, optional, default `false`) — run a full comparison instead of an incremental one.\n\nBy default each listing is only diffed when its channel content changed since the last sync, so differences that pre-date content sync being enabled stay invisible no matter how often the sweep runs. Send `force: true` to diff every governed field on every listing in scope against the catalog — slower, and it can raise a large number of changes at once, so scope it with `integration_instance_ids` or `product_listing_ids` when backfilling.\n\nReturns the background job id; poll the jobs API to track progress.\nRequires Bearer token.","body":{"mode":"raw","raw":"{\n  \"integration_instance_ids\": [\n    3\n  ],\n  \"product_listing_ids\": [\n    315,\n    316\n  ],\n  \"force\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/content-sync/reconcile","host":["{{baseUrl}}"],"path":["api","content-sync","reconcile"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 913\n  },\n  \"message\": \"Full comparison started - track progress in the job tray.\"\n}"}]},{"name":"List Content Conflicts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/content-sync/conflicts","host":["{{baseUrl}}"],"path":["api","content-sync","conflicts"]},"description":"Paginated conflicts: fields owned two-way where both the catalog and the channel changed since the last known synced state. Each row carries both values so a side can be chosen. Supports the same filters, sorts, and pagination as List Content Drift.\nRequires Bearer token.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/content-sync/conflicts","host":["{{baseUrl}}"],"path":["api","content-sync","conflicts"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 42,\n      \"product_listing_id\": 315,\n      \"product_id\": 88,\n      \"field\": \"name\",\n      \"field_group\": \"descriptive\",\n      \"direction\": \"inbound\",\n      \"owner_at_detection\": \"two_way\",\n      \"recommended_action\": \"review\",\n      \"catalog_value\": \"Stainless Water Bottle 750ml\",\n      \"channel_value\": \"Stainless Steel Water Bottle - 750 ml\",\n      \"status\": \"conflict\",\n      \"source\": \"webhook\",\n      \"is_risky\": true,\n      \"conflict_resolution\": null,\n      \"error_message\": null,\n      \"detected_at\": \"2026-07-29T14:05:12.000000Z\",\n      \"resolved_at\": null,\n      \"listing\": {\n        \"id\": 315,\n        \"listing_sku\": \"SWB-750\",\n        \"title\": \"Stainless Steel Water Bottle - 750 ml\",\n        \"content_sync_status\": \"pending_review\",\n        \"integration_instance_id\": 3,\n        \"integration_instance_name\": \"Blue Bottle Retail\",\n        \"integration_name\": \"Shopify\"\n      },\n      \"product\": {\n        \"id\": 88,\n        \"sku\": \"SWB-750\",\n        \"name\": \"Stainless Water Bottle 750ml\"\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"https://example.test/api/content-sync/drift?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://example.test/api/content-sync/drift?page=4\",\n  \"links\": [],\n  \"next_page_url\": \"https://example.test/api/content-sync/drift?page=2\",\n  \"path\": \"https://example.test/api/content-sync/drift\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 4,\n  \"total\": 4\n}"}]},{"name":"Resolve Content Conflict","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/content-sync/conflicts/{contentChange}/resolve","host":["{{baseUrl}}"],"path":["api","content-sync","conflicts",":contentChange","resolve"],"variable":[{"key":"contentChange","value":"","description":"The content change ID."}]},"description":"Resolve one two-way conflict.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n- mine: keep the catalog value; the channel change is dismissed.\n- theirs: apply the channel value to the catalog.\n- both: apply the channel value now; the catalog value is preserved on the resolved record for outbound re-push.\nReturns 409 when the record is not an open conflict (already resolved, superseded because the channel moved on, or resolved concurrently) and 422 when applying the channel value to the catalog fails.\nRequires Bearer token.","body":{"mode":"raw","raw":"{\n  \"resolution\": \"theirs\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/content-sync/conflicts/{contentChange}/resolve","host":["{{baseUrl}}"],"path":["api","content-sync","conflicts",":contentChange","resolve"],"variable":[{"key":"contentChange","value":"","description":"The content change ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"product_listing_id\": 315,\n    \"product_id\": 88,\n    \"field\": \"name\",\n    \"field_group\": \"descriptive\",\n    \"direction\": \"inbound\",\n    \"owner_at_detection\": \"channel\",\n    \"recommended_action\": \"apply\",\n    \"catalog_value\": \"Stainless Water Bottle 750ml\",\n    \"channel_value\": \"Stainless Steel Water Bottle - 750 ml\",\n    \"status\": \"applied\",\n    \"source\": \"webhook\",\n    \"is_risky\": true,\n    \"conflict_resolution\": \"theirs\",\n    \"error_message\": null,\n    \"detected_at\": \"2026-07-29T14:05:12.000000Z\",\n    \"resolved_at\": \"2026-07-29T15:20:00.000000Z\",\n    \"listing\": {\n      \"id\": 315,\n      \"listing_sku\": \"SWB-750\",\n      \"title\": \"Stainless Steel Water Bottle - 750 ml\",\n      \"content_sync_status\": \"pending_review\",\n      \"integration_instance_id\": 3,\n      \"integration_instance_name\": \"Blue Bottle Retail\",\n      \"integration_name\": \"Shopify\"\n    },\n    \"product\": {\n      \"id\": 88,\n      \"sku\": \"SWB-750\",\n      \"name\": \"Stainless Water Bottle 750ml\"\n    },\n    \"resolved_by\": {\n      \"id\": 1,\n      \"name\": \"Jane Doe\"\n    }\n  },\n  \"message\": \"Conflict resolved.\"\n}"}]},{"name":"List Channel Catalogs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listings/catalogs","host":["{{baseUrl}}"],"path":["api","v2","listings","catalogs"]},"description":"Paginated list of channel catalogs, each with its rule count and the sales channels using it.\n\nChannel catalogs limit which products a sales channel may carry.\n\nA catalog is a named, reusable set of include/exclude rules over product, category and brand, plus an optional filter rule that is re-evaluated on every read so newly created products qualify automatically. **Exclusions always win**: a product is eligible when it matches at least one include (or the catalog has no include rules at all, which means everything) AND matches no exclude.\n\nA sales channel with no catalog carries every product, so catalogs are entirely opt-in.\n\nAuthentication: Requires Bearer token.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listings/catalogs","host":["{{baseUrl}}"],"path":["api","v2","listings","catalogs"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3,\n      \"name\": \"Fuel Pumps & Regulators\",\n      \"description\": \"Walbro, DeatschWerks and AEM fuel delivery parts\",\n      \"rules_count\": 3,\n      \"sales_channels\": [\n        {\n          \"id\": 42,\n          \"name\": \"Fuelpumps.com\"\n        },\n        {\n          \"id\": 43,\n          \"name\": \"EBAY fuelpumpsusa\"\n        }\n      ],\n      \"created_at\": \"2026-09-01T10:15:00+00:00\",\n      \"updated_at\": \"2026-09-14T08:42:11+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Create Channel Catalog","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listings/catalogs","host":["{{baseUrl}}"],"path":["api","v2","listings","catalogs"]},"description":"Create a catalog, optionally with its rules and channel assignments in the same call.\n\n**Fields**\n- `name` (string, required, max 255)\n- `description` (string, optional, max 500)\n- `rules` (array, optional) — replaces the whole rule set\n  - `rules[].rule_type` (required): `include_product`, `exclude_product`, `include_category`, `exclude_category`, `include_brand`, `exclude_brand`, `include_filter`, `exclude_filter`\n  - `rules[].ref_id` (integer) — required for every rule type EXCEPT the filter types\n  - `rules[].filter_tree` (object) — required for `include_filter` / `exclude_filter`, forbidden otherwise\n- `sales_channel_ids` (array of integers, optional) — the channels this catalog governs. Channels previously pointing at the catalog and omitted here are released back to unrestricted.\n\nChannel catalogs limit which products a sales channel may carry.\n\nA catalog is a named, reusable set of include/exclude rules over product, category and brand, plus an optional filter rule that is re-evaluated on every read so newly created products qualify automatically. **Exclusions always win**: a product is eligible when it matches at least one include (or the catalog has no include rules at all, which means everything) AND matches no exclude.\n\nA sales channel with no catalog carries every product, so catalogs are entirely opt-in.\n\nAuthentication: Requires Bearer token.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"name\": \"Fuel Pumps & Regulators\",\n  \"description\": \"Walbro, DeatschWerks and AEM fuel delivery parts\",\n  \"rules\": [\n    {\n      \"rule_type\": \"include_brand\",\n      \"ref_id\": 12\n    },\n    {\n      \"rule_type\": \"include_filter\",\n      \"filter_tree\": {\n        \"conjunction\": \"and\",\n        \"children\": [\n          {\n            \"type\": \"condition\",\n            \"condition\": {\n              \"column\": \"sku\",\n              \"operator\": \"contains\",\n              \"value\": \"S14\"\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"rule_type\": \"exclude_product\",\n      \"ref_id\": 1940\n    }\n  ],\n  \"sales_channel_ids\": [\n    42,\n    43\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listings/catalogs","host":["{{baseUrl}}"],"path":["api","v2","listings","catalogs"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Fuel Pumps & Regulators\",\n    \"description\": \"Walbro, DeatschWerks and AEM fuel delivery parts\",\n    \"rules_count\": 3,\n    \"rules\": [\n      {\n        \"id\": 11,\n        \"rule_type\": \"include_brand\",\n        \"kind\": \"brand\",\n        \"label\": \"Brand\",\n        \"is_include\": true,\n        \"ref_id\": 12,\n        \"ref_label\": \"Walbro\",\n        \"ref_sku\": null,\n        \"filter_tree\": null\n      },\n      {\n        \"id\": 12,\n        \"rule_type\": \"include_filter\",\n        \"kind\": null,\n        \"label\": \"Filter\",\n        \"is_include\": true,\n        \"ref_id\": null,\n        \"ref_label\": null,\n        \"ref_sku\": null,\n        \"filter_tree\": {\n          \"conjunction\": \"and\",\n          \"children\": [\n            {\n              \"type\": \"condition\",\n              \"condition\": {\n                \"column\": \"sku\",\n                \"operator\": \"contains\",\n                \"value\": \"S14\"\n              }\n            }\n          ]\n        }\n      },\n      {\n        \"id\": 13,\n        \"rule_type\": \"exclude_product\",\n        \"kind\": \"product\",\n        \"label\": \"Product\",\n        \"is_include\": false,\n        \"ref_id\": 1940,\n        \"ref_label\": \"32 Disposable Cotton Inserts\",\n        \"ref_sku\": \"80691164\",\n        \"filter_tree\": null\n      }\n    ],\n    \"sales_channels\": [\n      {\n        \"id\": 42,\n        \"name\": \"Fuelpumps.com\"\n      },\n      {\n        \"id\": 43,\n        \"name\": \"EBAY fuelpumpsusa\"\n      }\n    ],\n    \"created_at\": \"2026-09-01T10:15:00+00:00\",\n    \"updated_at\": \"2026-09-14T08:42:11+00:00\"\n  }\n}"}]},{"name":"Get Channel Catalog","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listings/catalogs/{channelCatalog}","host":["{{baseUrl}}"],"path":["api","v2","listings","catalogs",":channelCatalog"],"variable":[{"key":"channelCatalog","value":"3","description":"Channel catalog id"}]},"description":"One catalog with its full rule set. Each rule's reference is resolved to a human label (`ref_label`, and `ref_sku` for product rules) so it can be rendered without extra lookups.\n\nChannel catalogs limit which products a sales channel may carry.\n\nA catalog is a named, reusable set of include/exclude rules over product, category and brand, plus an optional filter rule that is re-evaluated on every read so newly created products qualify automatically. **Exclusions always win**: a product is eligible when it matches at least one include (or the catalog has no include rules at all, which means everything) AND matches no exclude.\n\nA sales channel with no catalog carries every product, so catalogs are entirely opt-in.\n\nAuthentication: Requires Bearer token.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listings/catalogs/{channelCatalog}","host":["{{baseUrl}}"],"path":["api","v2","listings","catalogs",":channelCatalog"],"variable":[{"key":"channelCatalog","value":"3","description":"Channel catalog id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Fuel Pumps & Regulators\",\n    \"description\": \"Walbro, DeatschWerks and AEM fuel delivery parts\",\n    \"rules_count\": 3,\n    \"rules\": [\n      {\n        \"id\": 11,\n        \"rule_type\": \"include_brand\",\n        \"kind\": \"brand\",\n        \"label\": \"Brand\",\n        \"is_include\": true,\n        \"ref_id\": 12,\n        \"ref_label\": \"Walbro\",\n        \"ref_sku\": null,\n        \"filter_tree\": null\n      },\n      {\n        \"id\": 12,\n        \"rule_type\": \"include_filter\",\n        \"kind\": null,\n        \"label\": \"Filter\",\n        \"is_include\": true,\n        \"ref_id\": null,\n        \"ref_label\": null,\n        \"ref_sku\": null,\n        \"filter_tree\": {\n          \"conjunction\": \"and\",\n          \"children\": [\n            {\n              \"type\": \"condition\",\n              \"condition\": {\n                \"column\": \"sku\",\n                \"operator\": \"contains\",\n                \"value\": \"S14\"\n              }\n            }\n          ]\n        }\n      },\n      {\n        \"id\": 13,\n        \"rule_type\": \"exclude_product\",\n        \"kind\": \"product\",\n        \"label\": \"Product\",\n        \"is_include\": false,\n        \"ref_id\": 1940,\n        \"ref_label\": \"32 Disposable Cotton Inserts\",\n        \"ref_sku\": \"80691164\",\n        \"filter_tree\": null\n      }\n    ],\n    \"sales_channels\": [\n      {\n        \"id\": 42,\n        \"name\": \"Fuelpumps.com\"\n      },\n      {\n        \"id\": 43,\n        \"name\": \"EBAY fuelpumpsusa\"\n      }\n    ],\n    \"created_at\": \"2026-09-01T10:15:00+00:00\",\n    \"updated_at\": \"2026-09-14T08:42:11+00:00\"\n  }\n}"}]},{"name":"Update Channel Catalog","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listings/catalogs/{channelCatalog}","host":["{{baseUrl}}"],"path":["api","v2","listings","catalogs",":channelCatalog"],"variable":[{"key":"channelCatalog","value":"3","description":"Channel catalog id"}]},"description":"Update a catalog. Sending `rules` REPLACES the entire rule set rather than appending; omitting the key leaves the existing rules untouched. The same applies to `sales_channel_ids`.\n\nField rules are identical to Create, except `name` is optional here.\n\nChannel catalogs limit which products a sales channel may carry.\n\nA catalog is a named, reusable set of include/exclude rules over product, category and brand, plus an optional filter rule that is re-evaluated on every read so newly created products qualify automatically. **Exclusions always win**: a product is eligible when it matches at least one include (or the catalog has no include rules at all, which means everything) AND matches no exclude.\n\nA sales channel with no catalog carries every product, so catalogs are entirely opt-in.\n\nAuthentication: Requires Bearer token.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"name\": \"Fuel Pumps & Regulators\",\n  \"description\": \"Walbro, DeatschWerks and AEM fuel delivery parts\",\n  \"rules\": [\n    {\n      \"rule_type\": \"include_brand\",\n      \"ref_id\": 12\n    },\n    {\n      \"rule_type\": \"include_filter\",\n      \"filter_tree\": {\n        \"conjunction\": \"and\",\n        \"children\": [\n          {\n            \"type\": \"condition\",\n            \"condition\": {\n              \"column\": \"sku\",\n              \"operator\": \"contains\",\n              \"value\": \"S14\"\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"rule_type\": \"exclude_product\",\n      \"ref_id\": 1940\n    }\n  ],\n  \"sales_channel_ids\": [\n    42,\n    43\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/listings/catalogs/{channelCatalog}","host":["{{baseUrl}}"],"path":["api","v2","listings","catalogs",":channelCatalog"],"variable":[{"key":"channelCatalog","value":"3","description":"Channel catalog id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Fuel Pumps & Regulators\",\n    \"description\": \"Walbro, DeatschWerks and AEM fuel delivery parts\",\n    \"rules_count\": 3,\n    \"rules\": [\n      {\n        \"id\": 11,\n        \"rule_type\": \"include_brand\",\n        \"kind\": \"brand\",\n        \"label\": \"Brand\",\n        \"is_include\": true,\n        \"ref_id\": 12,\n        \"ref_label\": \"Walbro\",\n        \"ref_sku\": null,\n        \"filter_tree\": null\n      },\n      {\n        \"id\": 12,\n        \"rule_type\": \"include_filter\",\n        \"kind\": null,\n        \"label\": \"Filter\",\n        \"is_include\": true,\n        \"ref_id\": null,\n        \"ref_label\": null,\n        \"ref_sku\": null,\n        \"filter_tree\": {\n          \"conjunction\": \"and\",\n          \"children\": [\n            {\n              \"type\": \"condition\",\n              \"condition\": {\n                \"column\": \"sku\",\n                \"operator\": \"contains\",\n                \"value\": \"S14\"\n              }\n            }\n          ]\n        }\n      },\n      {\n        \"id\": 13,\n        \"rule_type\": \"exclude_product\",\n        \"kind\": \"product\",\n        \"label\": \"Product\",\n        \"is_include\": false,\n        \"ref_id\": 1940,\n        \"ref_label\": \"32 Disposable Cotton Inserts\",\n        \"ref_sku\": \"80691164\",\n        \"filter_tree\": null\n      }\n    ],\n    \"sales_channels\": [\n      {\n        \"id\": 42,\n        \"name\": \"Fuelpumps.com\"\n      },\n      {\n        \"id\": 43,\n        \"name\": \"EBAY fuelpumpsusa\"\n      }\n    ],\n    \"created_at\": \"2026-09-01T10:15:00+00:00\",\n    \"updated_at\": \"2026-09-14T08:42:11+00:00\"\n  }\n}"}]},{"name":"Delete Channel Catalog","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listings/catalogs/{channelCatalog}","host":["{{baseUrl}}"],"path":["api","v2","listings","catalogs",":channelCatalog"],"variable":[{"key":"channelCatalog","value":"3","description":"Channel catalog id"}]},"description":"Delete a catalog. Every channel using it is released back to carrying the whole product range — a channel is never left pointing at a catalog that no longer exists, and no listings are removed.\n\nChannel catalogs limit which products a sales channel may carry.\n\nA catalog is a named, reusable set of include/exclude rules over product, category and brand, plus an optional filter rule that is re-evaluated on every read so newly created products qualify automatically. **Exclusions always win**: a product is eligible when it matches at least one include (or the catalog has no include rules at all, which means everything) AND matches no exclude.\n\nA sales channel with no catalog carries every product, so catalogs are entirely opt-in.\n\nAuthentication: Requires Bearer token.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/listings/catalogs/{channelCatalog}","host":["{{baseUrl}}"],"path":["api","v2","listings","catalogs",":channelCatalog"],"variable":[{"key":"channelCatalog","value":"3","description":"Channel catalog id"}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"null"}]},{"name":"List Eligible Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listings/catalogs/{channelCatalog}/preview","host":["{{baseUrl}}"],"path":["api","v2","listings","catalogs",":channelCatalog","preview"],"variable":[{"key":"channelCatalog","value":"3","description":"Channel catalog id"}]},"description":"The products this catalog currently admits, resolved from its rules. Filter rules are re-evaluated on every call, so this reflects products created since the catalog was saved.\n\nChannel catalogs limit which products a sales channel may carry.\n\nA catalog is a named, reusable set of include/exclude rules over product, category and brand, plus an optional filter rule that is re-evaluated on every read so newly created products qualify automatically. **Exclusions always win**: a product is eligible when it matches at least one include (or the catalog has no include rules at all, which means everything) AND matches no exclude.\n\nA sales channel with no catalog carries every product, so catalogs are entirely opt-in.\n\nAuthentication: Requires Bearer token.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listings/catalogs/{channelCatalog}/preview","host":["{{baseUrl}}"],"path":["api","v2","listings","catalogs",":channelCatalog","preview"],"variable":[{"key":"channelCatalog","value":"3","description":"Channel catalog id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 514,\n      \"sku\": \"GSS340\",\n      \"name\": \"Walbro GSS340 Fuel Pump\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 19,\n  \"per_page\": 10,\n  \"total\": 188,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"Get Catalog Impact","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listings/catalogs/{channelCatalog}/impact","host":["{{baseUrl}}"],"path":["api","v2","listings","catalogs",":channelCatalog","impact"],"variable":[{"key":"channelCatalog","value":"3","description":"Channel catalog id"}]},"description":"What this catalog costs each channel it governs.\n\n`live_but_ineligible` lists products that are LIVE on the channel but fall outside the catalog. They are reported, never acted on — a listing is never removed from a channel automatically — so they stay live until someone archives them deliberately.\n\nChannel catalogs limit which products a sales channel may carry.\n\nA catalog is a named, reusable set of include/exclude rules over product, category and brand, plus an optional filter rule that is re-evaluated on every read so newly created products qualify automatically. **Exclusions always win**: a product is eligible when it matches at least one include (or the catalog has no include rules at all, which means everything) AND matches no exclude.\n\nA sales channel with no catalog carries every product, so catalogs are entirely opt-in.\n\nAuthentication: Requires Bearer token.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listings/catalogs/{channelCatalog}/impact","host":["{{baseUrl}}"],"path":["api","v2","listings","catalogs",":channelCatalog","impact"],"variable":[{"key":"channelCatalog","value":"3","description":"Channel catalog id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"eligible_count\": 188,\n    \"total_products\": 1961,\n    \"channels\": [\n      {\n        \"sales_channel_id\": 43,\n        \"name\": \"EBAY fuelpumpsusa\",\n        \"live_count\": 76,\n        \"live_but_ineligible\": [\n          {\n            \"id\": 1932,\n            \"sku\": \"80694066\",\n            \"name\": \"5 Pack - Reusable Cloth Diaper\"\n          }\n        ]\n      }\n    ]\n  }\n}"}]},{"name":"List Channel Field Names","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{salesChannel}/attribute-mappings/field-names","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":salesChannel","attribute-mappings","field-names"],"variable":[{"key":"salesChannel","value":"15","description":"Sales channel id"}]},"description":"Every distinct field name across the channel's synced categories, with how many categories carry it and in how many it is required. This is the vocabulary a global attribute mapping targets: a mapping is keyed on a NAME and matched case-insensitively against each category's fields when a listing's values are resolved, so a name that recurs across hundreds of categories is worth mapping once, while a one-off belongs on that category's template.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nOrdered by category count, descending. Capped at 500 names.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{salesChannel}/attribute-mappings/field-names","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":salesChannel","attribute-mappings","field-names"],"variable":[{"key":"salesChannel","value":"15","description":"Sales channel id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"name\": \"Brand\",\n      \"label\": \"Brand\",\n      \"category_count\": 412,\n      \"required_count\": 388\n    },\n    {\n      \"name\": \"Country of Origin\",\n      \"label\": \"Country/Region of Manufacture\",\n      \"category_count\": 406,\n      \"required_count\": 12\n    },\n    {\n      \"name\": \"Cable Length\",\n      \"label\": \"Cable Length\",\n      \"category_count\": 1,\n      \"required_count\": 0\n    }\n  ]\n}"}]},{"name":"Generate Listing Content","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ai/listing-content","host":["{{baseUrl}}"],"path":["api","ai","listing-content"]},"description":"Generate listing content for one product on one sales channel using the tenant's configured AI provider. The prompt carries the product's facts (name, identifiers, brand, weight, dimensions, category, and its attributes) plus any `source_material` you pass, and applies the TARGET channel's own description rules — TikTok Shop: HTML, up to 10,000 characters, 3–5 selling points; Amazon: plain text, up to 2,000; eBay and Shopify: HTML; Walmart, Temu and Faire: plain text — so you do not restate them.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`source_material` is up to 10 labelled `{label, text}` entries (each text up to 8,000 characters): a description live on another channel, a manufacturer page, a supplier sheet. The model reconciles them into one account rather than pasting, prefers the brand's own wording where sources disagree, and explains itself in `content.rationale` — returned whenever `description` is requested and non-empty.\n\n`description_format` (`html` | `plain`) and `description_max_length` (100–20000) override the channel's rule for one call. `include_product_attributes` (default true) feeds the product's own attributes: short values as facts, long ones (a description already written for another channel) as source material.\n\n`fields` defaults to all four of `title`, `description`, `bullet_points`, `seo_keywords`. Only requested fields come back, and empty results are omitted. Nothing is written — apply the content to a draft or attribute yourself.\n\nReturns 422 when AI listing content is disabled for the tenant, when the provider is not configured, or when the model returned no usable content — the `message` says which. Returns 404 for an unknown product or channel.\n\nAuthentication: Requires Bearer token.\n\nBody fields: product_id (required), sales_channel_id (required), sales_channel_category_id, fields[], source_material[] of {label,text}, description_format, description_max_length, include_product_attributes, tone, target_keywords[] (max 25), additional_instructions (max 500), optimize_for_rufus.","body":{"mode":"raw","raw":"{\n  \"product_id\": 7206,\n  \"sales_channel_id\": 30,\n  \"fields\": [\n    \"description\"\n  ],\n  \"source_material\": [\n    {\n      \"label\": \"Amazon listing\",\n      \"text\": \"At Charlie Banana, we're all about caring for your baby and our planet at the same time. Our waterproof changing pads...\"\n    },\n    {\n      \"label\": \"Amazon bullets\",\n      \"text\": \"Made with quality materials: tested to OEKO-TEX Standard 100.\\nWaterproof and machine washable.\"\n    }\n  ],\n  \"tone\": \"professional\",\n  \"target_keywords\": [\n    \"reusable changing pad\",\n    \"organic cotton\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ai/listing-content","host":["{{baseUrl}}"],"path":["api","ai","listing-content"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"content\": {\n      \"description\": \"&lt;p>Charlie Banana's reusable changing pad keeps your baby `comfortable...</p>`&lt;ul>&lt;li>&lt;strong>OEKO-TEX Standard 100 `tested</strong>` — ...&lt;/li>&lt;li>&lt;strong>Waterproof and machine `washable</strong>` — ...&lt;/li>&lt;li>&lt;strong>100% organic cotton `lining</strong>` — ...&lt;/li>&lt;/ul>\",\n      \"rationale\": \"Built from the Amazon listing and its bullets, which agree on materials and care. Kept the OEKO-TEX claim because the listing states it; left out the pack-count detail, which varies by variant. Structured as an opening paragraph plus three selling points because TikTok Shop rates listings on that shape.\"\n    },\n    \"provider\": \"anthropic\",\n    \"tokens\": 1834,\n    \"rufus_optimized\": false\n  }\n}"}]},{"name":"Bulk Generate & Apply Listing Content","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/ai/listing-content/bulk","host":["{{baseUrl}}"],"path":["api","ai","listing-content","bulk"]},"description":"Queue a bulk AI content enrichment for many products on one sales channel. For each product the configured AI provider generates listing content, which is written to the product attributes that channel's templates read from — so the products become ready to list without generating each one individually. Returns 202 with a tracked job id; the job records a per-product summary (enriched / skipped / failed) as it runs.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nOnly content fields the channel binds to a product attribute are filled; a channel whose content comes from product columns or fixed values has nothing to enrich and returns 422. Enrich at most 500 products per call.\n\nBody fields:\n- `sales_channel_id` (integer, required) — the target sales channel.\n- `product_ids` (integer[], required, 1–500) — the products to enrich.","body":{"mode":"raw","raw":"{\n  \"sales_channel_id\": 24,\n  \"product_ids\": [\n    461,\n    1183,\n    1190\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/ai/listing-content/bulk","host":["{{baseUrl}}"],"path":["api","ai","listing-content","bulk"]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 532610\n  },\n  \"message\": \"Content enrichment started.\"\n}"}]},{"name":"Get Channel Category Map","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{salesChannel}/category-map","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":salesChannel","category-map"],"variable":[{"key":"salesChannel","value":"30","description":"Sales channel id"}]},"description":"The channel's product type → category map: one row per active leaf product type, path-ordered. Each row carries the type, how many active products carry it, the channel category it maps to (or null), `category_missing` (true when a taxonomy re-sync removed the mapped category — the row counts as unmapped until it is re-mapped), the mapped category's default template and how many templates that category has.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`include=suggestions` adds up to 3 suggested categories to each unmapped row, matched from the type's name and a sample of its products' names (`confidence` 0–1, `source`). Suggestions are cached for a day.\n\n`version` is a concurrency token for the whole map — send it back with Replace Channel Category Map. `taxonomy_synced` is false when the channel's categories have not been synced yet, in which case no row can be mapped.\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `listings.index`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{salesChannel}/category-map","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":salesChannel","category-map"],"variable":[{"key":"salesChannel","value":"30","description":"Sales channel id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"rows\": [\n    {\n      \"product_type\": {\n        \"id\": 7,\n        \"name\": \"Cloth diapers\",\n        \"path\": \"Diapering > Cloth diapers\"\n      },\n      \"products_count\": 48,\n      \"sales_channel_category\": {\n        \"id\": 1184,\n        \"name\": \"Cloth Diapers\",\n        \"path\": \"Baby > Diapering > Cloth Diapers\",\n        \"external_id\": \"5406\",\n        \"is_leaf\": true\n      },\n      \"category_missing\": false,\n      \"default_template\": {\n        \"id\": 21,\n        \"name\": \"Cloth diapers — Walmart\",\n        \"is_default\": true\n      },\n      \"templates_count\": 2,\n      \"suggestions\": [],\n      \"updated_at\": \"2026-09-10T14:02:11.000000Z\"\n    },\n    {\n      \"product_type\": {\n        \"id\": 8,\n        \"name\": \"Swim diapers\",\n        \"path\": \"Diapering > Swim diapers\"\n      },\n      \"products_count\": 12,\n      \"sales_channel_category\": null,\n      \"category_missing\": true,\n      \"default_template\": null,\n      \"templates_count\": 0,\n      \"suggestions\": [],\n      \"updated_at\": \"2026-08-02T16:45:09.000000Z\"\n    },\n    {\n      \"product_type\": {\n        \"id\": 12,\n        \"name\": \"Wet bags\",\n        \"path\": \"Wet bags\"\n      },\n      \"products_count\": 9,\n      \"sales_channel_category\": null,\n      \"category_missing\": false,\n      \"default_template\": null,\n      \"templates_count\": 0,\n      \"suggestions\": [\n        {\n          \"sales_channel_category_id\": 2231,\n          \"category\": {\n            \"id\": 2231,\n            \"name\": \"Diaper Bags\",\n            \"path\": \"Baby > Diapering > Diaper Bags\",\n            \"external_id\": \"5412\"\n          },\n          \"confidence\": 0.62,\n          \"source\": \"local_search\"\n        }\n      ],\n      \"updated_at\": null\n    }\n  ],\n  \"version\": \"9c1f4b7e2d8a3c5e6f0a1b2c3d4e5f60\",\n  \"taxonomy_synced\": true\n}"}]},{"name":"Replace Channel Category Map","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{salesChannel}/category-map","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":salesChannel","category-map"],"variable":[{"key":"salesChannel","value":"30","description":"Sales channel id"}]},"description":"Replace the channel's map in full. Every row in the payload with a category is saved; a row with a null category — or a leaf type absent from the payload — loses its mapping. Every category must belong to this channel and be a leaf (products cannot list into a parent category); violations answer 422 keyed on `rows.N.sales_channel_category_id`.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nSend the `version` from your last read. If the map changed in between, nothing is written and the response is 409 with the current rows and a fresh `version` — re-apply your changes to those rows and retry. Omit `version` to skip the check.\n\nResponds with the saved map in the same shape as Get Channel Category Map.\n\nAuthentication: Requires Bearer token.\n\nFields:\n- `rows` (required, array, max 2000)\n- `rows[].product_type_id` (required, integer) — a leaf product type\n- `rows[].sales_channel_category_id` (nullable, integer) — a leaf category of this channel; null clears the mapping\n- `version` (nullable, string, max 64) — the token from the last read\n\n**Requires permission:** `listings.update`","body":{"mode":"raw","raw":"{\n  \"version\": \"9c1f4b7e2d8a3c5e6f0a1b2c3d4e5f60\",\n  \"rows\": [\n    {\n      \"product_type_id\": 7,\n      \"sales_channel_category_id\": 1184\n    },\n    {\n      \"product_type_id\": 8,\n      \"sales_channel_category_id\": null\n    },\n    {\n      \"product_type_id\": 12,\n      \"sales_channel_category_id\": 2231\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{salesChannel}/category-map","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":salesChannel","category-map"],"variable":[{"key":"salesChannel","value":"30","description":"Sales channel id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"rows\": [\n    {\n      \"product_type\": {\n        \"id\": 7,\n        \"name\": \"Cloth diapers\",\n        \"path\": \"Diapering > Cloth diapers\"\n      },\n      \"products_count\": 48,\n      \"sales_channel_category\": {\n        \"id\": 1184,\n        \"name\": \"Cloth Diapers\",\n        \"path\": \"Baby > Diapering > Cloth Diapers\",\n        \"external_id\": \"5406\",\n        \"is_leaf\": true\n      },\n      \"category_missing\": false,\n      \"default_template\": {\n        \"id\": 21,\n        \"name\": \"Cloth diapers — Walmart\",\n        \"is_default\": true\n      },\n      \"templates_count\": 2,\n      \"suggestions\": [],\n      \"updated_at\": \"2026-09-10T14:02:11.000000Z\"\n    },\n    {\n      \"product_type\": {\n        \"id\": 8,\n        \"name\": \"Swim diapers\",\n        \"path\": \"Diapering > Swim diapers\"\n      },\n      \"products_count\": 12,\n      \"sales_channel_category\": null,\n      \"category_missing\": false,\n      \"default_template\": null,\n      \"templates_count\": 0,\n      \"suggestions\": [],\n      \"updated_at\": null\n    },\n    {\n      \"product_type\": {\n        \"id\": 12,\n        \"name\": \"Wet bags\",\n        \"path\": \"Wet bags\"\n      },\n      \"products_count\": 9,\n      \"sales_channel_category\": {\n        \"id\": 2231,\n        \"name\": \"Diaper Bags\",\n        \"path\": \"Baby > Diapering > Diaper Bags\",\n        \"external_id\": \"5412\",\n        \"is_leaf\": true\n      },\n      \"category_missing\": false,\n      \"default_template\": null,\n      \"templates_count\": 0,\n      \"suggestions\": [],\n      \"updated_at\": \"2026-09-16T10:41:00.000000Z\"\n    }\n  ],\n  \"version\": \"3e7a9d1c5b2f4e8a0c6d2b1f9e7a5c43\",\n  \"taxonomy_synced\": true\n}"}]},{"name":"Seed Channel Category Map","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{salesChannel}/category-map/seed","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":salesChannel","category-map","seed"],"variable":[{"key":"salesChannel","value":"30","description":"Sales channel id"}]},"description":"Propose a category for each unmapped leaf product type (or for every leaf type when `only_unmapped` is `false`) from the channel's synced taxonomy: the best match for the type's name plus a sample of its products' names. Nothing is saved — review the proposals and send the accepted ones with Replace Channel Category Map. Types with no confident match are left out.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- `only_unmapped` (optional, boolean, default true) — may also be passed as a query parameter\n\nEach proposal: `product_type_id`, `sales_channel_category` (`{id, name, path, external_id}`), `confidence` (0–1), `source`.\n\n**Requires permission:** `listings.index`","body":{"mode":"raw","raw":"{\n  \"only_unmapped\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{salesChannel}/category-map/seed","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":salesChannel","category-map","seed"],"variable":[{"key":"salesChannel","value":"30","description":"Sales channel id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_type_id\": 8,\n      \"sales_channel_category\": {\n        \"id\": 1187,\n        \"name\": \"Swim Diapers\",\n        \"path\": \"Baby > Diapering > Swim Diapers\",\n        \"external_id\": \"5409\"\n      },\n      \"confidence\": 0.88,\n      \"source\": \"local_search\"\n    },\n    {\n      \"product_type_id\": 12,\n      \"sales_channel_category\": {\n        \"id\": 2231,\n        \"name\": \"Diaper Bags\",\n        \"path\": \"Baby > Diapering > Diaper Bags\",\n        \"external_id\": \"5412\"\n      },\n      \"confidence\": 0.62,\n      \"source\": \"local_search\"\n    }\n  ]\n}"}]},{"name":"Get Inventory for Product Listing","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/get-inventory","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","get-inventory"],"variable":[{"key":"productListing","value":"1","description":"Product listing ID"}]},"description":"Returns the available inventory for a product listing across all warehouses / sales channel inventory locations.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/get-inventory","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","get-inventory"],"variable":[{"key":"productListing","value":"1","description":"Product listing ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"available\": 150,\n    \"on_hand\": 200,\n    \"allocated\": 50,\n    \"warehouses\": [\n      {\n        \"warehouse_id\": 1,\n        \"warehouse_name\": \"Main Warehouse\",\n        \"available\": 100\n      },\n      {\n        \"warehouse_id\": 2,\n        \"warehouse_name\": \"East Warehouse\",\n        \"available\": 50\n      }\n    ]\n  }\n}"}]},{"name":"Link to Subscription Offering","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/link-offering","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","link-offering"],"variable":[{"key":"productListing","value":"1","description":"Product listing ID"}]},"description":"Links a product listing to a subscription offering. Used for subscription-based sales channels.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"subscription_offering_id\": 3\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/link-offering","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","link-offering"],"variable":[{"key":"productListing","value":"1","description":"Product listing ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Successfully updated Product Listing 1\",\n  \"data\": {\n    \"id\": 1,\n    \"product_id\": 42,\n    \"sales_channel_id\": 5,\n    \"subscription_offering\": {\n      \"id\": 3,\n      \"name\": \"Monthly Plan\"\n    }\n  }\n}"}]},{"name":"Unlink from Subscription Offering","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/unlink-offering","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","unlink-offering"],"variable":[{"key":"productListing","value":"1","description":"Product listing ID"}]},"description":"Unlinks a product listing from a subscription offering.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"product_id\": 42\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-listings/{productListing}/unlink-offering","host":["{{baseUrl}}"],"path":["api","product-listings",":productListing","unlink-offering"],"variable":[{"key":"productListing","value":"1","description":"Product listing ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Successfully updated Product Listing 1\",\n  \"data\": {\n    \"id\": 1,\n    \"product_id\": 42,\n    \"sales_channel_id\": 5,\n    \"subscription_offering\": null\n  }\n}"}]},{"name":"Refresh Amazon Buy Box","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listings/buy-box/refresh","host":["{{baseUrl}}"],"path":["api","v2","listings","buy-box","refresh"]},"description":"Samples Amazon's Featured Offer (Buy Box) for the ASINs behind the selected listings and stores the result against each ASIN.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThis is a read-only observation. It records who held the Featured Offer, at what price, and when the sample was taken. It never changes a price on Amazon.\n\nThe result is a point-in-time SAMPLE, not a continuous status — Amazon rotates the Featured Offer through the day. Every stored field is accompanied by the timestamp it was observed at.\n\nTargeting modes:\n- product_listing_ids (array of integers) — refresh the ASINs behind these listings.\n- apply_to_all: true — refresh every ASIN matching the optional filters object (the same filters accepted by the listings list endpoint).\n\nOne of the two is required; a request with neither returns 422.\n\nBehaviour:\n- Work is asynchronous. The response returns immediately with one entry per Amazon integration.\n- A selection can span several Amazon integrations. Because a sampling batch cannot cross seller credentials, the ASINs are grouped by integration and one run is started per integration. ASINs are de-duplicated per integration.\n- Only one sample run may be in flight per integration. An integration already running returns status \"busy\" with an explanatory message; tracked_job_log_id is null in that case, and is also null when the in-flight run was started by the recurring schedule rather than by a request.\n- Listings that are not Amazon listings, or that have no ASIN, are counted in skipped_non_amazon rather than silently dropped.\n\nStatus codes:\n- 202 Accepted — at least one run was started (a partial conflict is still a success).\n- 409 Conflict — every targeted integration already had a run in flight.\n- 422 Unprocessable Entity — nothing in the selection was an Amazon listing with an ASIN, or neither targeting mode was supplied.\n\nRequires a Personal Access Token with products read/write scope.","body":{"mode":"raw","raw":"{\n  \"product_listing_ids\": [\n    4521,\n    4522\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {\n    \"search\": \"WIDGET\",\n    \"integration_instance_id\": 2\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listings/buy-box/refresh","host":["{{baseUrl}}"],"path":["api","v2","listings","buy-box","refresh"]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"runs\": [\n      {\n        \"integration_instance_id\": 2,\n        \"name\": \"Amazon US\",\n        \"asins\": 17,\n        \"tracked_job_log_id\": 8812,\n        \"status\": \"queued\",\n        \"message\": null\n      },\n      {\n        \"integration_instance_id\": 9,\n        \"name\": \"Amazon EU\",\n        \"asins\": 0,\n        \"tracked_job_log_id\": null,\n        \"status\": \"busy\",\n        \"message\": \"A Buy Box sweep is already running for this integration.\"\n      }\n    ],\n    \"queued\": 1,\n    \"busy\": 1,\n    \"skipped_non_amazon\": 3\n  },\n  \"message\": \"Refreshing Buy Box for 17 ASINs on 1 integration. 1 integration was already running.\",\n  \"status\": \"OK\"\n}"}]},{"name":"Show Product Listing","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/product-listings/{product_listing}","host":["{{baseUrl}}"],"path":["api","product-listings",":product_listing"],"variable":[{"key":"product_listing","value":"1","description":"Product listing ID"}]},"description":"Returns a single product listing with its sales channel, integration, pricing tier, inventory cache, and document relations loaded. The integration-specific resource class is used based on the sales channel.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/product-listings/{product_listing}","host":["{{baseUrl}}"],"path":["api","product-listings",":product_listing"],"variable":[{"key":"product_listing","value":"1","description":"Product listing ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 1,\n  \"product_id\": 42,\n  \"sales_channel_id\": 5,\n  \"sales_channel_listing_id\": \"7823456789012\",\n  \"listing_sku\": \"PROD-001\",\n  \"title\": \"Premium Widget\",\n  \"master_of_price\": true,\n  \"master_of_stock\": true,\n  \"product_pricing_tier_id\": null,\n  \"sales_channel\": {\n    \"id\": 5,\n    \"name\": \"Shopify Store\",\n    \"integration_instance\": {\n      \"id\": 2,\n      \"name\": \"My Shopify\",\n      \"integration\": {\n        \"name\": \"shopify\"\n      }\n    }\n  },\n  \"inventory_cache\": {\n    \"available_quantity\": 150\n  }\n}"}]},{"name":"Update Product Listing","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-listings/{product_listing}","host":["{{baseUrl}}"],"path":["api","product-listings",":product_listing"],"variable":[{"key":"product_listing","value":"1","description":"Product listing ID"}]},"description":"Updates a product listing. If master_of_price or product_pricing_tier_id change, the price is pushed to the sales channel. If inventoryLocations is provided, inventory is synced to the channel.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"master_of_price\": true,\n  \"master_of_stock\": false,\n  \"product_pricing_tier_id\": 2,\n  \"inventoryLocations\": [\n    1,\n    2\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/product-listings/{product_listing}","host":["{{baseUrl}}"],"path":["api","product-listings",":product_listing"],"variable":[{"key":"product_listing","value":"1","description":"Product listing ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Successfully updated Product Listing 7823456789012\",\n  \"data\": {\n    \"id\": 1,\n    \"product_id\": 42,\n    \"sales_channel_id\": 5,\n    \"sales_channel_listing_id\": \"7823456789012\",\n    \"listing_sku\": \"PROD-001\",\n    \"master_of_price\": true,\n    \"master_of_stock\": false,\n    \"product_pricing_tier_id\": 2\n  }\n}"}]},{"name":"Get Publish-Readiness Snapshot","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/publish-readiness","host":["{{baseUrl}}"],"path":["api","v2","products",":product","publish-readiness"],"variable":[{"key":"product","value":"1","description":"Product ID"}]},"description":"Per-product, multi-channel publish readiness snapshot. Returns one entry per publish-capable, connected sales channel, grouped by bucket (live / ready / ready_to_list / warn / idle / publishing / scheduled / draft) so the publish dialog can render confidence groups without orchestrating N parallel calls.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRead-only: does NOT speculatively create drafts. Channels with no draft and no live listing land in `idle` and surface recommended next actions (pick a template, create one, etc.).\n\nBucket resolution:\n- `live` — a non-archived `ProductListing` exists for this product on the channel\n- `ready` — open draft in state `ready`\n- `scheduled` — open draft in state `scheduled`\n- `publishing` — open draft in state `publishing` or `validating`\n- `warn` — open draft in state `error`, OR `draft` with validation errors\n- `draft` — open draft in state `draft` with no validation errors\n- `removed` — the listing was live and the channel no longer hosts it\n- `idle` — no open draft and no live listing\n\nAuthentication: Requires Bearer token.\n\n**`quantity_to_push`** (int|null, per channel entry) — the stock a publish to this channel would seed right now, computed through the same SalesChannelInventoryCalculator rules the standing inventory sync uses (max/min/buffer, locations, bundles). `null` when quantity seeding is gated off (listing-publishing `push_quantity_on_publish` setting disabled, SKU.io not master of stock for the instance, no inventory rules configured) or the bucket cannot publish (`live` / `publishing`).\n\n**`quantity_variant_count`** (int|null, per channel entry) — how many variants the `quantity_to_push` figure is spread across. Non-null only for a matrix parent, whose own stock is always zero because every unit sits on a child: the figure is the sum across its variants, each of which is seeded with its own quantity when the family publishes. Null for a standalone product, whose figure is its own.\n\n**`existing_listing`** (object|null, per channel entry) — the channel-side listing when one exists, else `null`. Fields:\n- `remote_status` — the last reconciled channel-side state: `live`, `pending` (accepted, still being ingested by the channel), `rejected` (the channel accepted the submission then refused to publish it, so it never went live), `missing` (was live, no longer hosted), `archived` (hosted but hidden from shoppers), or `unknown`.\n- `remote_status_checked_at` — when that state was last confirmed with the channel.\n- `remote_status_reason` (string|null) — the channel's own explanation when it refused the listing, shown verbatim so it names what to fix. For example, a Walmart item withheld for a missing primary image reports \"This item is missing a primary image. Add a 'Main Image URL' and resubmit the item.\" `null` when the channel gave no reason. A `rejected` listing stays rejected until the underlying problem is corrected and it is published again — resubmitting unchanged fails identically.\n\n**`draft.channel_errors`** — what the SALES CHANNEL said when it refused the item, as distinct from `validation_errors`, which is what this system's own validation found before anything was sent. Each entry carries the channel's `code`, its `message`, and the `field` it names where one is given; `channel_errors_count` is the same list's length.\n\nThe two are different problems and want different handling: outstanding `validation_errors` mean the item is not ready to send, while `channel_errors` mean it was sent and came back rejected — republishing it unchanged will fail the same way."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/publish-readiness","host":["{{baseUrl}}"],"path":["api","v2","products",":product","publish-readiness"],"variable":[{"key":"product","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"channel\": {\n        \"id\": 12,\n        \"name\": \"My Shopify Store\",\n        \"integration_name\": \"Shopify\",\n        \"integration_instance_id\": 3\n      },\n      \"bucket\": \"ready\",\n      \"existing_listing\": null,\n      \"draft\": {\n        \"id\": 88,\n        \"state\": \"ready\",\n        \"sales_channel_category_id\": 41,\n        \"sales_channel_product_template_id\": 5,\n        \"sales_channel_listing_profile_id\": 2,\n        \"scheduled_at\": null,\n        \"validation_errors_count\": 0,\n        \"missing_required\": [],\n        \"template\": {\n          \"id\": 5,\n          \"name\": \"Hoodies — Standard\"\n        },\n        \"category\": {\n          \"id\": 41,\n          \"name\": \"Hoodies\",\n          \"path\": \"Apparel / Hoodies\"\n        },\n        \"updated_at\": \"2026-06-22T14:31:22+00:00\",\n        \"channel_errors_count\": 0,\n        \"channel_errors\": []\n      },\n      \"available_templates_count\": 3,\n      \"issues_count\": 0,\n      \"quantity_to_push\": 42,\n      \"quantity_variant_count\": null\n    },\n    {\n      \"channel\": {\n        \"id\": 7,\n        \"name\": \"My eBay Store\",\n        \"integration_name\": \"Ebay\",\n        \"integration_instance_id\": 1\n      },\n      \"bucket\": \"warn\",\n      \"existing_listing\": null,\n      \"draft\": {\n        \"id\": 92,\n        \"state\": \"draft\",\n        \"sales_channel_category_id\": 318,\n        \"sales_channel_product_template_id\": 11,\n        \"sales_channel_listing_profile_id\": null,\n        \"scheduled_at\": null,\n        \"validation_errors_count\": 3,\n        \"missing_required\": [\n          {\n            \"field\": \"Brand\",\n            \"label\": \"Item Specifics: Brand\"\n          },\n          {\n            \"field\": \"Condition\",\n            \"label\": \"Item Condition\"\n          },\n          {\n            \"field\": \"Color\",\n            \"label\": \"Color\"\n          }\n        ],\n        \"template\": {\n          \"id\": 11,\n          \"name\": \"eBay Hoodie — Default\"\n        },\n        \"category\": {\n          \"id\": 318,\n          \"name\": \"Hoodies\",\n          \"path\": \"Clothing / Men / Hoodies\"\n        },\n        \"updated_at\": \"2026-06-22T14:18:04+00:00\",\n        \"channel_errors_count\": 1,\n        \"channel_errors\": [\n          {\n            \"code\": \"EXT_DATA_ERROR_49505365506868\",\n            \"message\": \"'/storage/images/tee-red.jpg' is not a valid value for the `Main Image URL` field. Enter a valid value.\",\n            \"field\": \"mainImageUrl\"\n          }\n        ]\n      },\n      \"available_templates_count\": 2,\n      \"issues_count\": 3,\n      \"quantity_to_push\": 42,\n      \"quantity_variant_count\": null\n    },\n    {\n      \"channel\": {\n        \"id\": 22,\n        \"name\": \"Walmart US\",\n        \"integration_name\": \"Walmart\",\n        \"integration_instance_id\": 5\n      },\n      \"bucket\": \"idle\",\n      \"existing_listing\": null,\n      \"draft\": null,\n      \"available_templates_count\": 0,\n      \"issues_count\": 0,\n      \"quantity_to_push\": 42,\n      \"quantity_variant_count\": null\n    },\n    {\n      \"channel\": {\n        \"id\": 4,\n        \"name\": \"Etsy\",\n        \"integration_name\": \"Etsy\",\n        \"integration_instance_id\": 2\n      },\n      \"bucket\": \"live\",\n      \"existing_listing\": {\n        \"id\": 9412,\n        \"sales_channel_listing_id\": \"1394820392\",\n        \"listing_sku\": \"AC-HOODIE-BLK-M\",\n        \"updated_at\": \"2026-06-22T14:27:11+00:00\",\n        \"remote_status\": \"live\",\n        \"remote_status_checked_at\": \"2026-06-22T03:00:14+00:00\",\n        \"remote_status_reason\": null\n      },\n      \"draft\": null,\n      \"available_templates_count\": 0,\n      \"issues_count\": 0,\n      \"quantity_to_push\": null,\n      \"quantity_variant_count\": null\n    },\n    {\n      \"channel\": {\n        \"id\": 31,\n        \"name\": \"Walmart Marketplace\",\n        \"integration_name\": \"Walmart\",\n        \"integration_instance_id\": 8\n      },\n      \"bucket\": \"warn\",\n      \"existing_listing\": {\n        \"id\": 9613,\n        \"sales_channel_listing_id\": \"AC-HOODIE-BLK-M\",\n        \"listing_sku\": \"AC-HOODIE-BLK-M\",\n        \"updated_at\": \"2026-06-22T09:12:40+00:00\",\n        \"remote_status\": \"rejected\",\n        \"remote_status_checked_at\": \"2026-06-22T10:00:22+00:00\",\n        \"remote_status_reason\": \"This item is missing a primary image. Add a 'Main Image URL' and resubmit the item.\"\n      },\n      \"draft\": null,\n      \"available_templates_count\": 1,\n      \"issues_count\": 0,\n      \"quantity_to_push\": null,\n      \"quantity_variant_count\": null\n    }\n  ]\n}"}]},{"name":"Get Publish Matrix","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listings/publish","host":["{{baseUrl}}"],"path":["api","v2","listings","publish"]},"description":"Catalog-wide publish matrix. Returns a paginated list of products with per-channel publish state (bucket) for each publish-capable connected channel. Returns catalog-wide publish state per product across channels.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**One row per product**, with `channels[]` listing all publish-capable channels and their current bucket (`live` / `ready` / `warn` / `idle` / `scheduled` / `publishing` / `draft`). The `channel_buckets` map is a convenience for matrix cell rendering (channel_id → bucket string).\n\n`totals` aggregates bucket counts across the ENTIRE catalog (every product × publish-capable channel) — this is what the summary cards above the matrix display. `page_totals` is the same aggregation scoped to only the products on the current page (retained for page-level tallies).\n\nAuthentication: Requires Bearer token. `channel_totals` breaks the same catalog-wide counts down per channel — each entry lists the channel (channel_id, name, integration_name), its total product count, and a count in each status bucket.\n\n**`product.variant_parent`** — set when the product is a CHILD of a variation family, giving that family's parent as `{id, sku, name}`; null for standalone products and for the parent itself.\n\nA variation family is listed as one grouped listing sent from the parent, which creates one item per variant on the channel. A variant therefore has no listing draft and no template of its own, and cannot be published on its own. Use this field to label such rows and to point actions at the parent — publishing or republishing a variant directly is not a supported operation."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listings/publish","host":["{{baseUrl}}"],"path":["api","v2","listings","publish"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product\": {\n        \"id\": 4,\n        \"sku\": \"BB004\",\n        \"name\": \"Baby Bumble Hoodie\",\n        \"brand\": \"Baby Bumble\",\n        \"primary_image_url\": \"https://cdn.example.com/img/beanie-1.jpg\",\n        \"product_type\": \"bundle\",\n        \"is_bundle\": true,\n        \"bundle_component_count\": 3,\n        \"variant_parent\": null\n      },\n      \"channels\": [\n        {\n          \"channel\": {\n            \"id\": 3,\n            \"name\": \"My Shopify Store\",\n            \"integration_name\": \"Shopify\",\n            \"integration_instance_id\": 1\n          },\n          \"bucket\": \"live\",\n          \"existing_listing\": {\n            \"id\": 9412,\n            \"sales_channel_listing_id\": \"abc123\",\n            \"listing_sku\": \"BB004\",\n            \"updated_at\": \"2026-06-22T14:27:11+00:00\",\n            \"last_pushed_at\": \"2026-06-20T09:14:00+00:00\",\n            \"created_at\": \"2026-06-19T08:00:00+00:00\"\n          },\n          \"draft\": null,\n          \"available_templates_count\": 2,\n          \"issues_count\": 0\n        },\n        {\n          \"channel\": {\n            \"id\": 25,\n            \"name\": \"Amazon\",\n            \"integration_name\": \"Amazon\",\n            \"integration_instance_id\": 3\n          },\n          \"bucket\": \"ready\",\n          \"existing_listing\": null,\n          \"draft\": {\n            \"id\": 88,\n            \"state\": \"ready\",\n            \"sales_channel_category_id\": 41,\n            \"sales_channel_product_template_id\": 5,\n            \"sales_channel_listing_profile_id\": null,\n            \"scheduled_at\": null,\n            \"validation_errors_count\": 0,\n            \"missing_required\": [],\n            \"template\": {\n              \"id\": 5,\n              \"name\": \"Amazon Hoodie — FBA\"\n            },\n            \"category\": {\n              \"id\": 41,\n              \"name\": \"Hoodies\",\n              \"path\": \"Apparel / Hoodies\"\n            },\n            \"updated_at\": \"2026-06-22T14:31:22+00:00\"\n          },\n          \"available_templates_count\": 4,\n          \"issues_count\": 0\n        },\n        {\n          \"channel\": {\n            \"id\": 30,\n            \"name\": \"eBay Store\",\n            \"integration_name\": \"Ebay\",\n            \"integration_instance_id\": 1\n          },\n          \"bucket\": \"warn\",\n          \"existing_listing\": null,\n          \"draft\": {\n            \"id\": 92,\n            \"state\": \"draft\",\n            \"sales_channel_category_id\": 318,\n            \"sales_channel_product_template_id\": 11,\n            \"sales_channel_listing_profile_id\": null,\n            \"scheduled_at\": null,\n            \"validation_errors_count\": 3,\n            \"missing_required\": [\n              {\n                \"field\": \"Brand\",\n                \"label\": \"Brand\"\n              },\n              {\n                \"field\": \"Color\",\n                \"label\": \"Color\"\n              },\n              {\n                \"field\": \"Item_Condition\",\n                \"label\": \"Item Condition\"\n              }\n            ],\n            \"template\": {\n              \"id\": 11,\n              \"name\": \"eBay Hoodie — Default\"\n            },\n            \"category\": {\n              \"id\": 318,\n              \"name\": \"Hoodies\",\n              \"path\": \"Clothing / Men / Hoodies\"\n            },\n            \"updated_at\": \"2026-06-22T14:18:04+00:00\"\n          },\n          \"available_templates_count\": 2,\n          \"issues_count\": 3\n        },\n        {\n          \"channel\": {\n            \"id\": 26,\n            \"name\": \"Walmart US\",\n            \"integration_name\": \"Walmart\",\n            \"integration_instance_id\": 5\n          },\n          \"bucket\": \"idle\",\n          \"existing_listing\": null,\n          \"draft\": null,\n          \"available_templates_count\": 0,\n          \"issues_count\": 0\n        }\n      ],\n      \"channel_buckets\": {\n        \"3\": \"live\",\n        \"25\": \"ready\",\n        \"26\": \"idle\",\n        \"30\": \"warn\"\n      }\n    },\n    {\n      \"product\": {\n        \"id\": 91,\n        \"sku\": \"BB010-RED-M\",\n        \"name\": \"Baby Bumble Tee - Red / M\",\n        \"brand\": \"Baby Bumble\",\n        \"primary_image_url\": \"https://cdn.example.com/img/tee-red.jpg\",\n        \"product_type\": \"standard\",\n        \"is_bundle\": false,\n        \"bundle_component_count\": 0,\n        \"variant_parent\": {\n          \"id\": 90,\n          \"sku\": \"BB010\",\n          \"name\": \"Baby Bumble Tee\"\n        }\n      },\n      \"channels\": [\n        {\n          \"channel\": {\n            \"id\": 3,\n            \"name\": \"My Shopify Store\",\n            \"integration_name\": \"Shopify\",\n            \"integration_instance_id\": 1\n          },\n          \"bucket\": \"live\",\n          \"existing_listing\": {\n            \"id\": 9412,\n            \"sales_channel_listing_id\": \"abc123\",\n            \"listing_sku\": \"BB004\",\n            \"updated_at\": \"2026-06-22T14:27:11+00:00\",\n            \"last_pushed_at\": \"2026-06-20T09:14:00+00:00\",\n            \"created_at\": \"2026-06-19T08:00:00+00:00\"\n          },\n          \"draft\": null,\n          \"available_templates_count\": 2,\n          \"issues_count\": 0\n        },\n        {\n          \"channel\": {\n            \"id\": 25,\n            \"name\": \"Amazon\",\n            \"integration_name\": \"Amazon\",\n            \"integration_instance_id\": 3\n          },\n          \"bucket\": \"ready\",\n          \"existing_listing\": null,\n          \"draft\": {\n            \"id\": 88,\n            \"state\": \"ready\",\n            \"sales_channel_category_id\": 41,\n            \"sales_channel_product_template_id\": 5,\n            \"sales_channel_listing_profile_id\": null,\n            \"scheduled_at\": null,\n            \"validation_errors_count\": 0,\n            \"missing_required\": [],\n            \"template\": {\n              \"id\": 5,\n              \"name\": \"Amazon Hoodie — FBA\"\n            },\n            \"category\": {\n              \"id\": 41,\n              \"name\": \"Hoodies\",\n              \"path\": \"Apparel / Hoodies\"\n            },\n            \"updated_at\": \"2026-06-22T14:31:22+00:00\"\n          },\n          \"available_templates_count\": 4,\n          \"issues_count\": 0\n        },\n        {\n          \"channel\": {\n            \"id\": 30,\n            \"name\": \"eBay Store\",\n            \"integration_name\": \"Ebay\",\n            \"integration_instance_id\": 1\n          },\n          \"bucket\": \"warn\",\n          \"existing_listing\": null,\n          \"draft\": {\n            \"id\": 92,\n            \"state\": \"draft\",\n            \"sales_channel_category_id\": 318,\n            \"sales_channel_product_template_id\": 11,\n            \"sales_channel_listing_profile_id\": null,\n            \"scheduled_at\": null,\n            \"validation_errors_count\": 3,\n            \"missing_required\": [\n              {\n                \"field\": \"Brand\",\n                \"label\": \"Brand\"\n              },\n              {\n                \"field\": \"Color\",\n                \"label\": \"Color\"\n              },\n              {\n                \"field\": \"Item_Condition\",\n                \"label\": \"Item Condition\"\n              }\n            ],\n            \"template\": {\n              \"id\": 11,\n              \"name\": \"eBay Hoodie — Default\"\n            },\n            \"category\": {\n              \"id\": 318,\n              \"name\": \"Hoodies\",\n              \"path\": \"Clothing / Men / Hoodies\"\n            },\n            \"updated_at\": \"2026-06-22T14:18:04+00:00\"\n          },\n          \"available_templates_count\": 2,\n          \"issues_count\": 3\n        },\n        {\n          \"channel\": {\n            \"id\": 26,\n            \"name\": \"Walmart US\",\n            \"integration_name\": \"Walmart\",\n            \"integration_instance_id\": 5\n          },\n          \"bucket\": \"idle\",\n          \"existing_listing\": null,\n          \"draft\": null,\n          \"available_templates_count\": 0,\n          \"issues_count\": 0\n        }\n      ],\n      \"channel_buckets\": {\n        \"3\": \"live\",\n        \"25\": \"ready\",\n        \"26\": \"idle\",\n        \"30\": \"warn\"\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 11,\n  \"per_page\": 20,\n  \"total\": 218,\n  \"from\": 1,\n  \"to\": 20,\n  \"first_page_url\": \"https://example.test/api/v2/listings/publish?page=1\",\n  \"last_page_url\": \"https://example.test/api/v2/listings/publish?page=11\",\n  \"next_page_url\": \"https://example.test/api/v2/listings/publish?page=2\",\n  \"prev_page_url\": null,\n  \"path\": \"https://example.test/api/v2/listings/publish\",\n  \"totals\": {\n    \"live\": 130,\n    \"ready\": 18,\n    \"ready_to_list\": 60,\n    \"warn\": 22,\n    \"idle\": 46,\n    \"scheduled\": 2,\n    \"publishing\": 0,\n    \"draft\": 0\n  },\n  \"page_totals\": {\n    \"live\": 41,\n    \"ready\": 6,\n    \"warn\": 8,\n    \"idle\": 25,\n    \"scheduled\": 0,\n    \"publishing\": 0,\n    \"draft\": 0\n  },\n  \"channel_totals\": [\n    {\n      \"channel_id\": 3,\n      \"name\": \"My Shopify Store\",\n      \"integration_name\": \"Shopify\",\n      \"total\": 109,\n      \"live\": 78,\n      \"ready\": 9,\n      \"removed\": 0,\n      \"warn\": 11,\n      \"idle\": 10,\n      \"scheduled\": 1,\n      \"publishing\": 0,\n      \"draft\": 0\n    },\n    {\n      \"channel_id\": 5,\n      \"name\": \"My eBay Store\",\n      \"integration_name\": \"eBay\",\n      \"total\": 109,\n      \"live\": 41,\n      \"ready\": 6,\n      \"removed\": 0,\n      \"warn\": 8,\n      \"idle\": 53,\n      \"scheduled\": 1,\n      \"publishing\": 0,\n      \"draft\": 0\n    }\n  ]\n}"}]},{"name":"Get Listing Health Rollup","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/listings/health","host":["{{baseUrl}}"],"path":["api","v2","listings","health"]},"description":"Per-channel listing-health rollup: how many listings on each sales channel are live, archived, removed (no longer on the channel), drifted (channel quantity/price differs from SKU.io), or stale (not verified against the channel in the last 24 hours). Also returns catalog-wide totals. Counts are driven by the last reconciliation of each listing.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/listings/health","host":["{{baseUrl}}"],"path":["api","v2","listings","health"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"channels\": [\n    {\n      \"sales_channel_id\": 1,\n      \"integration_name\": \"Shopify\",\n      \"live\": 812,\n      \"archived\": 44,\n      \"missing\": 9,\n      \"drifted\": 17,\n      \"stale\": 3,\n      \"total\": 885\n    },\n    {\n      \"sales_channel_id\": 2,\n      \"integration_name\": \"Amazon\",\n      \"live\": 410,\n      \"archived\": 12,\n      \"missing\": 4,\n      \"drifted\": 6,\n      \"stale\": 1,\n      \"total\": 432\n    }\n  ],\n  \"totals\": {\n    \"live\": 1222,\n    \"archived\": 56,\n    \"missing\": 13,\n    \"drifted\": 23,\n    \"stale\": 4,\n    \"total\": 1317\n  }\n}"}]},{"name":"Reconcile Listing Status","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listings/reconcile","host":["{{baseUrl}}"],"path":["api","v2","listings","reconcile"]},"description":"Start a reconciliation that fetches the true state of each listing directly from its sales channel and updates the stored status, quantity and price. With no body it reconciles every channel that supports status reporting; pass sales_channel_ids to limit it to specific channels, or product_listing_ids to recheck a specific set of listings. Runs asynchronously and returns the id of the background job doing the work.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"sales_channel_ids\": [\n    1,\n    2\n  ],\n  \"product_listing_ids\": [\n    1001,\n    1002\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listings/reconcile","host":["{{baseUrl}}"],"path":["api","v2","listings","reconcile"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 48213\n  },\n  \"message\": \"Listing reconciliation started.\"\n}"}]},{"name":"Recover Listing on Channel","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/product-listings/{listing}/recover","host":["{{baseUrl}}"],"path":["api","v2","product-listings",":listing","recover"],"variable":[{"key":"listing","value":"1001","description":"ProductListing id"}]},"description":"Recover an archived or removed listing back to live on its sales channel — the inverse of ending a listing. Depending on the channel this re-publishes, un-hides, restores from trash or restores stock for the same listing. Only applies to listings whose remote_status is archived or missing. On success the listing's remote_status returns to live.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/product-listings/{listing}/recover","host":["{{baseUrl}}"],"path":["api","v2","product-listings",":listing","recover"],"variable":[{"key":"listing","value":"1001","description":"ProductListing id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1001,\n    \"remote_status\": \"live\",\n    \"remote_status_checked_at\": \"2026-07-31T14:10:00.000000Z\"\n  },\n  \"message\": \"Listing recovered on the channel.\"\n}"}]},{"name":"Push to Fix Listing Drift","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/product-listings/{listing}/resync-inventory","host":["{{baseUrl}}"],"path":["api","v2","product-listings",":listing","resync-inventory"],"variable":[{"key":"listing","value":"42","description":"Product listing ID"}]},"description":"Fix a drifted listing: re-push SKU.io's stock to the channel (flags the listing for the next outbound inventory sync) and recheck its channel-side state so the drift flag refreshes. Runs asynchronously; returns the background job id.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/product-listings/{listing}/resync-inventory","host":["{{baseUrl}}"],"path":["api","v2","product-listings",":listing","resync-inventory"],"variable":[{"key":"listing","value":"42","description":"Product listing ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 48310\n  },\n  \"message\": \"Re-syncing inventory to the channel and rechecking — track progress in the job tray.\"\n}"}]},{"name":"Recalculate Listing Price","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/product-listings/{listing}/recalculate-price","host":["{{baseUrl}}"],"path":["api","v2","product-listings",":listing","recalculate-price"],"variable":[{"key":"listing","value":"42","description":"Product listing ID"}]},"description":"Re-resolve one product listing's cached SKU.io price from its product's pricing levels and profiles, and return the new figure — the pricing counterpart of the listing's inventory recalculation.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nSynchronous. Nothing is sent to the channel; to transmit the price, start a price push for the listing.\n\n`price` is null when the listing resolves to no price under its current policy (no level configured, or the product has no price at the levels that apply).\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.sync` permission.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/product-listings/{listing}/recalculate-price","host":["{{baseUrl}}"],"path":["api","v2","product-listings",":listing","recalculate-price"],"variable":[{"key":"listing","value":"42","description":"Product listing ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"price\": 22.99,\n    \"priced_from_tier_id\": 1,\n    \"price_updated_at\": \"2026-09-16T19:30:48+00:00\"\n  },\n  \"message\": \"Price recalculated.\"\n}"}]},{"name":"Validate Requirements","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listings/validate-requirements","host":["{{baseUrl}}"],"path":["api","v2","listings","validate-requirements"]},"description":"Dry-run a set of products against a template on a sales-channel instance and report, per product, required and recommended field coverage (present vs missing) plus any validation issues. Nothing is created or published.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Body**\n- product_ids (integer[], required) — the products to validate.\n- sales_channel_id (integer, required) — the sales-channel instance to validate against.\n- sales_channel_product_template_id (integer, required) — the template to apply; must belong to the given sales channel.\n\nEach product's status is one of ready, needs_attention (a required field is missing a value), or error (a field has an invalid value).","body":{"mode":"raw","raw":"{\n  \"product_ids\": [\n    63,\n    64\n  ],\n  \"sales_channel_id\": 40,\n  \"sales_channel_product_template_id\": 12\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listings/validate-requirements","host":["{{baseUrl}}"],"path":["api","v2","listings","validate-requirements"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"summary\": {\n      \"total\": 2,\n      \"ready\": 1,\n      \"needs_attention\": 1,\n      \"error\": 0\n    },\n    \"template\": {\n      \"id\": 12,\n      \"name\": \"Apparel — Standard\",\n      \"category\": {\n        \"id\": 88,\n        \"name\": \"T-Shirts\",\n        \"path\": \"Apparel > Tops > T-Shirts\"\n      }\n    },\n    \"channel\": {\n      \"id\": 40,\n      \"name\": \"Lookwise\"\n    },\n    \"products\": [\n      {\n        \"product\": {\n          \"id\": 63,\n          \"sku\": \"APP-CCT-016\",\n          \"name\": \"Classic Cotton T-Shirt\",\n          \"image_url\": \"https://cdn.example.com/p/63.jpg\"\n        },\n        \"template\": {\n          \"id\": 12,\n          \"name\": \"Apparel — Standard\"\n        },\n        \"status\": \"needs_attention\",\n        \"required\": {\n          \"total\": 10,\n          \"present\": 8,\n          \"missing\": [\n            {\n              \"name\": \"Color\",\n              \"label\": \"Color\"\n            },\n            {\n              \"name\": \"Material\",\n              \"label\": \"Material\"\n            }\n          ]\n        },\n        \"recommended\": {\n          \"total\": 5,\n          \"present\": 3,\n          \"missing\": [\n            {\n              \"name\": \"FabricWeight\",\n              \"label\": \"Fabric Weight\"\n            }\n          ]\n        },\n        \"issues\": [],\n        \"fields\": [\n          {\n            \"name\": \"Title\",\n            \"label\": \"Title\",\n            \"usage\": \"required\",\n            \"present\": true,\n            \"value\": \"Classic Cotton T-Shirt\",\n            \"error\": null\n          },\n          {\n            \"name\": \"Color\",\n            \"label\": \"Color\",\n            \"usage\": \"required\",\n            \"present\": false,\n            \"value\": \"\",\n            \"error\": null\n          }\n        ]\n      },\n      {\n        \"product\": {\n          \"id\": 64,\n          \"sku\": \"APP-HDY-002\",\n          \"name\": \"Zip Hoodie\",\n          \"image_url\": null\n        },\n        \"template\": {\n          \"id\": 12,\n          \"name\": \"Apparel — Standard\"\n        },\n        \"status\": \"ready\",\n        \"required\": {\n          \"total\": 10,\n          \"present\": 10,\n          \"missing\": []\n        },\n        \"recommended\": {\n          \"total\": 5,\n          \"present\": 5,\n          \"missing\": []\n        },\n        \"issues\": [],\n        \"fields\": []\n      }\n    ]\n  }\n}"}]},{"name":"Preview Bulk Listing Pricing","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listings/bulk-edit-pricing-preview","host":["{{baseUrl}}"],"path":["api","v2","listings","bulk-edit-pricing-preview"]},"description":"Dry run for Bulk Update Listing Pricing. For every targeted listing it resolves the Master of Price and pricing level the listing is priced from today, what it would be priced from after the change, the price that results, and what the next price push would do with that figure under the channel's current guardrails. Nothing is written.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nTargeting (one of):\n- ids (array of integers, required unless apply_to_all is true) — explicit listing IDs\n- apply_to_all (boolean) — target every listing matching the optional filters object\n\nfilters (object, used with apply_to_all) — the same filters the listings list accepts, as bare keys:\n- search (string) — listing SKU, title, channel listing ID, product SKU or product name\n- integration_id (integer), integration_instance_id (integer)\n- is_fba (true/false)\n- has_product (true/false) — listings linked (or not) to a product\n- archived — omit for active listings only; true for archived listings only; all for both\n- filter_groups (object) — an advanced filter tree: {\"conjunction\": \"and\", \"children\": [{\"type\": \"condition\", \"condition\": {\"column\": \"price\", \"operator\": \"greater_than\", \"value\": 20}}]}. Columns: id, listing_sku, title, sales_channel_listing_id, price, integration_id, is_fba, created_at, updated_at, plus channel document fields (doc_amazon_asin, doc_amazon_brand, doc_amazon_status, doc_shopify_status, doc_shopify_vendor, doc_walmart_published_status, doc_woocommerce_status, doc_ebay_listing_status, doc_bigcommerce_type and their siblings). Operators: is, is_not, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, is_one_of, greater_than, less_than, between.\n\npricing (required, object):\n- inherit (boolean) — when true, clears the listing's own Master of Price and pricing level so it inherits both from its channel; master_of_price and product_pricing_tier_id are then ignored\n- master_of_price (string, required unless inherit is true) — one of: sku.io, sales_channel, neither (case-insensitive)\n- product_pricing_tier_id (integer, required when master_of_price is sku.io) — the pricing level to pin the listing to; ignored for any other master\n\nResponse fields:\n- total_listings, will_change, unchanged — how many listings the change touches\n- fba_listings — how many of them are FBA listings\n- push — counts of what the next price push would do with the new prices: would_push, blocked (a guardrail refuses the price), unchanged (the channel already has that price), not_pushed (SKU.io would not own the price, no level prices the product, or the channel has no price push)\n- truncated, row_limit — counts are always complete; rows are capped at row_limit (500) and truncated says whether any were left out\n- rows[] — one per listing: id, listing_sku, title, channel_name, is_fba, will_change, current and new (each with master_of_price, is_override, tier_id, tier_name, price and scope — listing for the listing's own pinned level, fulfillment for a fulfillment-channel lane such as Amazon MFN, channel for the channel sequence, null when no level prices it), push (state, last_pushed_price, detail) and warning (a per-row caution, e.g. an FBA listing being pinned to a level even though a fulfillment lane never prices it)\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    4521,\n    4522,\n    4523\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {\n    \"search\": \"WIDGET\",\n    \"integration_instance_id\": 3,\n    \"is_fba\": false\n  },\n  \"pricing\": {\n    \"inherit\": false,\n    \"master_of_price\": \"sku.io\",\n    \"product_pricing_tier_id\": 6\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listings/bulk-edit-pricing-preview","host":["{{baseUrl}}"],"path":["api","v2","listings","bulk-edit-pricing-preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_listings\": 3,\n    \"computed_listings\": 3,\n    \"sampled\": false,\n    \"will_change\": 2,\n    \"unchanged\": 1,\n    \"fba_listings\": 1,\n    \"push\": {\n      \"would_push\": 1,\n      \"blocked\": 1,\n      \"unchanged\": 1,\n      \"not_pushed\": 0\n    },\n    \"truncated\": false,\n    \"row_limit\": 500,\n    \"rows\": [\n      {\n        \"id\": 4521,\n        \"listing_sku\": \"WIDGET-BLUE-01\",\n        \"title\": \"Blue Widget - 2 Pack\",\n        \"channel_name\": \"Amazon US\",\n        \"is_fba\": false,\n        \"will_change\": true,\n        \"current\": {\n          \"master_of_price\": \"sku.io\",\n          \"is_override\": false,\n          \"tier_id\": 2,\n          \"tier_name\": \"Retail\",\n          \"price\": 24,\n          \"scope\": \"channel\"\n        },\n        \"new\": {\n          \"master_of_price\": \"sku.io\",\n          \"is_override\": true,\n          \"tier_id\": 6,\n          \"tier_name\": \"Amazon US MFN\",\n          \"price\": 27.5,\n          \"scope\": \"listing\"\n        },\n        \"push\": {\n          \"state\": \"would_push\",\n          \"last_pushed_price\": 24,\n          \"detail\": null\n        },\n        \"warning\": null\n      },\n      {\n        \"id\": 4522,\n        \"listing_sku\": \"WIDGET-RED-01\",\n        \"title\": \"Red Widget - 2 Pack\",\n        \"channel_name\": \"Amazon US\",\n        \"is_fba\": true,\n        \"will_change\": true,\n        \"current\": {\n          \"master_of_price\": \"sku.io\",\n          \"is_override\": false,\n          \"tier_id\": 2,\n          \"tier_name\": \"Retail\",\n          \"price\": 24,\n          \"scope\": \"channel\"\n        },\n        \"new\": {\n          \"master_of_price\": \"sku.io\",\n          \"is_override\": true,\n          \"tier_id\": 6,\n          \"tier_name\": \"Amazon US MFN\",\n          \"price\": 12,\n          \"scope\": \"listing\"\n        },\n        \"push\": {\n          \"state\": \"blocked\",\n          \"last_pushed_price\": 24,\n          \"detail\": \"Below the 15% margin floor (landed cost $14.20 requires at least $16.71).\"\n        },\n        \"warning\": \"FBA listing — it will be pinned too; the MFN lane never applies to it.\"\n      },\n      {\n        \"id\": 4523,\n        \"listing_sku\": \"WIDGET-GREEN-01\",\n        \"title\": \"Green Widget - 2 Pack\",\n        \"channel_name\": \"Amazon US\",\n        \"is_fba\": false,\n        \"will_change\": false,\n        \"current\": {\n          \"master_of_price\": \"sku.io\",\n          \"is_override\": true,\n          \"tier_id\": 6,\n          \"tier_name\": \"Amazon US MFN\",\n          \"price\": 27.5,\n          \"scope\": \"listing\"\n        },\n        \"new\": {\n          \"master_of_price\": \"sku.io\",\n          \"is_override\": true,\n          \"tier_id\": 6,\n          \"tier_name\": \"Amazon US MFN\",\n          \"price\": 27.5,\n          \"scope\": \"listing\"\n        },\n        \"push\": {\n          \"state\": \"unchanged\",\n          \"last_pushed_price\": 27.5,\n          \"detail\": null\n        },\n        \"warning\": null\n      }\n    ]\n  }\n}"}]},{"name":"Bulk Update Listing Pricing","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/listings/bulk-update-pricing","host":["{{baseUrl}}"],"path":["api","v2","listings","bulk-update-pricing"]},"description":"Applies a pricing change to many listings at once: pin each listing to a Master of Price and, when SKU.io owns the price, a pricing level — or reset them to inherit from their channel. Takes the same body as Preview Bulk Listing Pricing; run the preview first for a dry run of the same payload.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nSelections of up to 25 listings are applied immediately and the response reports applied, unchanged and failed counts. Larger selections are queued as a background job: the response returns the job's tracked_job_log_id and the number of listings queued, and the change is applied in the background. In both cases every updated listing's price is re-resolved through its price-level sequence and the change is recorded in the listing's activity log.\n\nTargeting (one of):\n- ids (array of integers, required unless apply_to_all is true) — explicit listing IDs\n- apply_to_all (boolean) — target every listing matching the optional filters object\n\nfilters (object, used with apply_to_all) — the same filters the listings list accepts, as bare keys:\n- search (string) — listing SKU, title, channel listing ID, product SKU or product name\n- integration_id (integer), integration_instance_id (integer)\n- is_fba (true/false)\n- has_product (true/false) — listings linked (or not) to a product\n- archived — omit for active listings only; true for archived listings only; all for both\n- filter_groups (object) — an advanced filter tree: {\"conjunction\": \"and\", \"children\": [{\"type\": \"condition\", \"condition\": {\"column\": \"price\", \"operator\": \"greater_than\", \"value\": 20}}]}. Columns: id, listing_sku, title, sales_channel_listing_id, price, integration_id, is_fba, created_at, updated_at, plus channel document fields (doc_amazon_asin, doc_amazon_brand, doc_amazon_status, doc_shopify_status, doc_shopify_vendor, doc_walmart_published_status, doc_woocommerce_status, doc_ebay_listing_status, doc_bigcommerce_type and their siblings). Operators: is, is_not, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, is_one_of, greater_than, less_than, between.\n\npricing (required, object):\n- inherit (boolean) — when true, clears the listing's own Master of Price and pricing level so it inherits both from its channel; master_of_price and product_pricing_tier_id are then ignored\n- master_of_price (string, required unless inherit is true) — one of: sku.io, sales_channel, neither (case-insensitive)\n- product_pricing_tier_id (integer, required when master_of_price is sku.io) — the pricing level to pin the listing to; ignored for any other master\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    4521,\n    4522,\n    4523\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {\n    \"search\": \"WIDGET\",\n    \"integration_instance_id\": 3,\n    \"is_fba\": false\n  },\n  \"pricing\": {\n    \"inherit\": false,\n    \"master_of_price\": \"sku.io\",\n    \"product_pricing_tier_id\": 6\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/listings/bulk-update-pricing","host":["{{baseUrl}}"],"path":["api","v2","listings","bulk-update-pricing"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Updated pricing on 2 listing(s), 1 already matched.\",\n  \"data\": {\n    \"applied\": 2,\n    \"unchanged\": 1,\n    \"failed\": 0\n  }\n}"}]},{"name":"Delete Listing from Channel","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/product-listings/{listing}/delete","host":["{{baseUrl}}"],"path":["api","v2","product-listings",":listing","delete"],"variable":[{"key":"listing","value":"1001","description":"ProductListing id"}]},"description":"Delete a listing from its sales channel and remove the local listing record in one operation. When the listing reached the channel (its remote_status is live, archived or missing) and the channel supports deletion, the channel-side delete runs first; if the channel rejects it, the local record is preserved and a 422 is returned so you can retry. A listing that never went live (rejected or pending) is simply removed locally.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/product-listings/{listing}/delete","host":["{{baseUrl}}"],"path":["api","v2","product-listings",":listing","delete"],"variable":[{"key":"listing","value":"1001","description":"ProductListing id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1001\n  },\n  \"message\": \"Listing deleted from the channel.\"\n}"}]}]},{"name":"Sales Credits","item":[{"name":"List Sales Credits","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-credits?page=1&limit=10&sort=&search=&archived=0&included[]=&excluded[]=","host":["{{baseUrl}}"],"path":["api","v2","sales-credits"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"10","description":"Items per page (default: 10)","disabled":true},{"key":"sort","value":"","description":"Sort field and direction (prefix with - for descending). Common fields: id, credit_date, sales_credit_number, created_at, updated_at, archived_at, attachments_count","disabled":true},{"key":"search","value":"","description":"Full-text search across sales credit number, customer name, order number, SKU","disabled":true},{"key":"archived","value":"0","description":"0 = active only (default), 1 = archived only","disabled":true},{"key":"included[]","value":"","description":"IDs to force include in results","disabled":true},{"key":"excluded[]","value":"","description":"IDs to force exclude from results","disabled":true}]},"description":"Returns a paginated list of sales credits using the standard list interface. Supports search, archiving filter, and column selection.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nThis endpoint uses the standard list interface. Common filters are passed as query parameters directly.\n\nResponse is wrapped with table_specifications metadata when table_specifications parameter is provided.\n\nTotals fields:\n- total: total credit amount\n- refund_payments_total: sum of cash/card refunds against the credit (always positive)\n- applied_to_other_total: sum of credit consumed by Loop Returns EXC-* exchange orders (always positive)\n- fees_retained_total: sum of fees Loop withheld out of the customer's credit — return shipping, restocking, etc. (always positive)\n- outstanding_amount: total minus (refund_payments_total + applied_to_other_total + fees_retained_total), clamped to zero (i.e., the unrefunded, unapplied balance still owed back to the customer)\n\n**Documents.** Each row carries `attachments_count` (how many documents are attached to the credit) and `attachment_filenames` (their file names). Both `filter[attachments_count]` and `filter[attachment_filename]` are available, so you can isolate credits that are missing a supporting document or find the credit a given file belongs to. `attachments_count` is also sortable."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-credits?page=1&limit=10&sort=&search=&archived=0&included[]=&excluded[]=","host":["{{baseUrl}}"],"path":["api","v2","sales-credits"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"10","description":"Items per page (default: 10)","disabled":true},{"key":"sort","value":"","description":"Sort field and direction (prefix with - for descending). Common fields: id, credit_date, sales_credit_number, created_at, updated_at, archived_at, attachments_count","disabled":true},{"key":"search","value":"","description":"Full-text search across sales credit number, customer name, order number, SKU","disabled":true},{"key":"archived","value":"0","description":"0 = active only (default), 1 = archived only","disabled":true},{"key":"included[]","value":"","description":"IDs to force include in results","disabled":true},{"key":"excluded[]","value":"","description":"IDs to force exclude from results","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 55,\n      \"sales_credit_number\": \"SC-00055\",\n      \"credit_date\": \"2025-04-10\",\n      \"is_for_overpayment\": false,\n      \"created_at\": \"2025-04-10T09:00:00.000000Z\",\n      \"updated_at\": \"2025-04-10T09:00:00.000000Z\",\n      \"archived_at\": null,\n      \"statuses\": {\n        \"credit_status\": \"open\",\n        \"return_status\": \"pending\"\n      },\n      \"additional_credit\": 0,\n      \"tax_credit\": 0,\n      \"product_total\": 150,\n      \"total\": 150,\n      \"refund_payments_total\": 0,\n      \"applied_to_other_total\": 0,\n      \"fees_retained_total\": 0,\n      \"outstanding_amount\": 150,\n      \"currency_code\": \"USD\",\n      \"sales_order_id\": 100,\n      \"has_products\": true,\n      \"has_rma\": false,\n      \"payments\": [],\n      \"exchange_orders\": []\n    }\n  ],\n  \"per_page\": 10,\n  \"total\": 5,\n  \"last_page\": 1\n}"}]},{"name":"Sales Credit Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-credits/{salesCredit}/activity-log","host":["{{baseUrl}}"],"path":["api","v2","sales-credits",":salesCredit","activity-log"],"variable":[{"key":"salesCredit","value":"1","description":"Sales Credit ID"}]},"description":"Returns the activity log for a sales credit.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-credits/{salesCredit}/activity-log","host":["{{baseUrl}}"],"path":["api","v2","sales-credits",":salesCredit","activity-log"],"variable":[{"key":"salesCredit","value":"1","description":"Sales Credit ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"action\": \"created\",\n      \"user\": {\n        \"id\": 1,\n        \"name\": \"John Smith\"\n      },\n      \"created_at\": \"2024-03-01T10:00:00.000000Z\"\n    }\n  ]\n}"}]},{"name":"Sales Credits Filter Options","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-credits/filter-options","host":["{{baseUrl}}"],"path":["api","v2","sales-credits","filter-options"]},"description":"Returns available filter options (distinct values) for sales credits filtering.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-credits/filter-options","host":["{{baseUrl}}"],"path":["api","v2","sales-credits","filter-options"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"statuses\": [\n      \"draft\",\n      \"issued\",\n      \"applied\",\n      \"cancelled\"\n    ],\n    \"customers\": [\n      {\n        \"id\": 7,\n        \"name\": \"Acme Corp\"\n      }\n    ]\n  }\n}"}]},{"name":"List Sales Credits","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits?page=1&limit=10&sort=&search=&archived=0&included[]=&excluded[]=","host":["{{baseUrl}}"],"path":["api","sales-credits"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"10","description":"Items per page (default: 10)","disabled":true},{"key":"sort","value":"","description":"Sort field and direction (prefix with - for descending). Common fields: id, credit_date, sales_credit_number, created_at, updated_at, archived_at","disabled":true},{"key":"search","value":"","description":"Full-text search across sales credit number, customer name, order number, SKU","disabled":true},{"key":"archived","value":"0","description":"0 = active only (default), 1 = archived only","disabled":true},{"key":"included[]","value":"","description":"IDs to force include in results","disabled":true},{"key":"excluded[]","value":"","description":"IDs to force exclude from results","disabled":true}]},"description":"Returns a paginated list of sales credits with standard list query parameters. Supports search, archiving filter, and column selection.\n\nAuthentication: Requires Bearer token.\n\nThis endpoint uses standard list query parameters. Common filters are passed as query parameters directly.\n\nResponse is wrapped with table_specifications metadata when table_specifications parameter is provided.\n\nTotals fields:\n- total: total credit amount\n- refund_payments_total: sum of cash/card refunds against the credit (always positive)\n- applied_to_other_total: sum of credit consumed by Loop Returns EXC-* exchange orders (always positive)\n- fees_retained_total: sum of fees Loop withheld out of the customer's credit — return shipping, restocking, etc. (always positive)\n- outstanding_amount: total minus (refund_payments_total + applied_to_other_total + fees_retained_total), clamped to zero (i.e., the unrefunded, unapplied balance still owed back to the customer)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/sales-credits?page=1&limit=10&sort=&search=&archived=0&included[]=&excluded[]=","host":["{{baseUrl}}"],"path":["api","sales-credits"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"10","description":"Items per page (default: 10)","disabled":true},{"key":"sort","value":"","description":"Sort field and direction (prefix with - for descending). Common fields: id, credit_date, sales_credit_number, created_at, updated_at, archived_at","disabled":true},{"key":"search","value":"","description":"Full-text search across sales credit number, customer name, order number, SKU","disabled":true},{"key":"archived","value":"0","description":"0 = active only (default), 1 = archived only","disabled":true},{"key":"included[]","value":"","description":"IDs to force include in results","disabled":true},{"key":"excluded[]","value":"","description":"IDs to force exclude from results","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 55,\n      \"sales_credit_number\": \"SC-00055\",\n      \"credit_date\": \"2025-04-10\",\n      \"is_for_overpayment\": false,\n      \"created_at\": \"2025-04-10T09:00:00.000000Z\",\n      \"updated_at\": \"2025-04-10T09:00:00.000000Z\",\n      \"archived_at\": null,\n      \"statuses\": {\n        \"credit_status\": \"open\",\n        \"return_status\": \"pending\"\n      },\n      \"additional_credit\": 0,\n      \"tax_credit\": 0,\n      \"product_total\": 150,\n      \"total\": 150,\n      \"refund_payments_total\": 0,\n      \"applied_to_other_total\": 0,\n      \"fees_retained_total\": 0,\n      \"outstanding_amount\": 150,\n      \"currency_code\": \"USD\",\n      \"sales_order_id\": 100,\n      \"has_products\": true,\n      \"has_rma\": false,\n      \"payments\": [],\n      \"exchange_orders\": []\n    }\n  ],\n  \"per_page\": 10,\n  \"total\": 5,\n  \"last_page\": 1\n}"}]},{"name":"Create Sales Credit","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/sales-credits","host":["{{baseUrl}}"],"path":["api","sales-credits"]},"description":"Creates a new sales credit. Must be linked to either a sales_order_id or a customer_id.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- sales_order_id (required if no customer_id): Sales order ID to credit against\n- customer_id (required if no sales_order_id): Customer ID for non-order credits\n- sales_credit_number (optional, max 255): Custom credit number (auto-generated if omitted)\n- credit_date (optional, date): Credit date\n- store_id (optional): Store ID\n- to_warehouse_id (optional): Return destination warehouse ID\n- sales_credit_note (optional): Free-form note\n- currency_id (optional): Currency ID (defaults to account default)\n- currency_code (optional): Currency code (alternative to currency_id)\n- tax_rate_id (optional): Tax rate ID\n- is_tax_included (optional, boolean): Whether prices include tax. Default: false\n- is_product (optional, boolean): Whether this is a product return. Default: false\n- tax_lines (optional): Array of tax line objects\n- sales_credit_lines (optional): Array of credit lines — each with sales_order_line_id, quantity, and price","body":{"mode":"raw","raw":"{\n  \"sales_order_id\": 100,\n  \"sales_credit_number\": \"SC-MANUAL-001\",\n  \"credit_date\": \"2025-04-10\",\n  \"store_id\": 3,\n  \"to_warehouse_id\": 1,\n  \"sales_credit_note\": \"Customer returned damaged item\",\n  \"currency_id\": 1,\n  \"tax_rate_id\": null,\n  \"is_tax_included\": false,\n  \"is_product\": true,\n  \"tax_lines\": [],\n  \"sales_credit_lines\": [\n    {\n      \"sales_order_line_id\": 201,\n      \"quantity\": 2,\n      \"price\": 75,\n      \"financial_line_id\": null\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/sales-credits","host":["{{baseUrl}}"],"path":["api","sales-credits"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 56,\n    \"sales_credit_number\": \"SC-MANUAL-001\",\n    \"total\": 150,\n    \"currency_code\": \"USD\"\n  },\n  \"message\": \"sales credit created successfully\"\n}"}]},{"name":"Bulk Delete Sales Credits","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits","host":["{{baseUrl}}"],"path":["api","sales-credits"]},"description":"Bulk deletes sales credits by IDs or by applying current filters.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- ids (required if apply_to_all is false): Array of sales credit IDs to delete\n- apply_to_all (optional, boolean): If true, deletes all records matching the current filters instead of specific IDs\n- filters (optional): Filter set to apply when apply_to_all is true"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/sales-credits","host":["{{baseUrl}}"],"path":["api","sales-credits"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"sales credits bulk deleted successfully\"\n}"}]},{"name":"Get Sales Credit","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits/{sales_credit}","host":["{{baseUrl}}"],"path":["api","sales-credits",":sales_credit"],"variable":[{"key":"sales_credit","value":"55","description":"The sales credit ID."}]},"description":"Returns a single sales credit with all relations: lines, store with address, sales order, RMA with receipts, payments, allocations, and accounting transaction.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nTotals fields:\n- total: total credit amount\n- refund_payments_total: sum of cash/card refunds against the credit (always positive)\n- applied_to_other_total: sum of credit consumed by Loop Returns EXC-* exchange orders (always positive)\n- fees_retained_total: sum of fees Loop withheld out of the customer's credit — return shipping, restocking, etc. (always positive)\n- outstanding_amount: total minus (refund_payments_total + applied_to_other_total + fees_retained_total), clamped to zero (i.e., the unrefunded, unapplied balance still owed back to the customer)\n\nResponse field `metadata` (object|null): minimal credit-source metadata for channel-synced credits - `source` (e.g. `ebay`, `shopify`, `woocommerce`) and `sales_channel_refund_id` (the channel refund ID). `null` for manually created credits. Used by the UI for the source chip on the sales order Credits tab."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/sales-credits/{sales_credit}","host":["{{baseUrl}}"],"path":["api","sales-credits",":sales_credit"],"variable":[{"key":"sales_credit","value":"55","description":"The sales credit ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 55,\n    \"sales_credit_number\": \"SC-00055\",\n    \"store\": {\n      \"id\": 3,\n      \"name\": \"Main Store\",\n      \"email\": \"store@example.com\",\n      \"address\": {\n        \"address1\": \"123 Main St\",\n        \"city\": \"New York\",\n        \"province\": \"NY\",\n        \"zip\": \"10001\",\n        \"country\": \"United States\",\n        \"country_code\": \"US\"\n      }\n    },\n    \"credit_date\": \"2025-04-10\",\n    \"is_for_overpayment\": false,\n    \"integration\": \"Shopify\",\n    \"created_at\": \"2025-04-10T09:00:00.000000Z\",\n    \"updated_at\": \"2025-04-10T09:00:00.000000Z\",\n    \"archived_at\": null,\n    \"to_warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"returns_receipt_date\": null,\n    \"returns_receipt_quantity\": 0,\n    \"statuses\": {\n      \"credit_status\": \"open\",\n      \"return_status\": \"pending\"\n    },\n    \"additional_credit\": 0,\n    \"tax_credit\": 5,\n    \"product_total\": 150,\n    \"total\": 155,\n    \"refund_payments_total\": 143.49,\n    \"applied_to_other_total\": 0,\n    \"fees_retained_total\": 0,\n    \"outstanding_amount\": 11.51,\n    \"currency_code\": \"USD\",\n    \"tags\": [\n      \"returns\"\n    ],\n    \"sales_order_id\": 100,\n    \"sales_order\": {\n      \"id\": 100,\n      \"sales_order_number\": \"SO-00100\"\n    },\n    \"sales_credit_lines\": [\n      {\n        \"id\": 201,\n        \"product_id\": 12,\n        \"quantity\": 2,\n        \"price\": 75\n      }\n    ],\n    \"line_item_count\": 1,\n    \"total_quantity\": 2,\n    \"first_line_sku\": \"WIDGET-A\",\n    \"exchange_orders\": [],\n    \"payments\": [],\n    \"has_products\": true,\n    \"has_rma\": false,\n    \"rma\": null,\n    \"tax_lines\": [],\n    \"tax_rate\": null,\n    \"calculated_tax_total\": 5,\n    \"sales_credit_note\": \"Customer damaged item\",\n    \"metadata\": {\n      \"source\": \"ebay\",\n      \"sales_channel_refund_id\": \"5012345678\"\n    },\n    \"is_tax_included\": false,\n    \"tax_rate_id\": null\n  }\n}"}]},{"name":"Update Sales Credit","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/sales-credits/{sales_credit}","host":["{{baseUrl}}"],"path":["api","sales-credits",":sales_credit"],"variable":[{"key":"sales_credit","value":"55","description":"The sales credit ID."}]},"description":"Updates a sales credit including its lines. Lines array replaces all existing lines when provided; omit lines to leave them unchanged.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- sales_credit_number (optional, max 255): Custom credit number\n- credit_date (optional, date): Credit date\n- store_id (optional): Store ID\n- to_warehouse_id (optional): Return destination warehouse ID\n- customer_id (optional): Customer ID\n- sales_credit_note (optional): Free-form note\n- currency_id (optional): Currency ID\n- currency_code (optional): Currency code\n- tax_rate_id (optional): Tax rate ID\n- is_tax_included (optional, boolean): Whether prices include tax\n- is_product (optional, boolean): Whether this is a product return\n- tax_lines (optional): Array of tax line objects\n- sales_credit_lines (optional): Array of credit lines to sync","body":{"mode":"raw","raw":"{\n  \"credit_date\": \"2025-04-12\",\n  \"sales_credit_note\": \"Updated note: partial return\",\n  \"to_warehouse_id\": 2,\n  \"tax_rate_id\": 3,\n  \"is_tax_included\": false,\n  \"sales_credit_lines\": [\n    {\n      \"sales_order_line_id\": 201,\n      \"quantity\": 1,\n      \"price\": 75\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/sales-credits/{sales_credit}","host":["{{baseUrl}}"],"path":["api","sales-credits",":sales_credit"],"variable":[{"key":"sales_credit","value":"55","description":"The sales credit ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 55,\n    \"sales_credit_number\": \"SC-00055\",\n    \"credit_date\": \"2025-04-12\",\n    \"total\": 75\n  },\n  \"message\": \"sales credit SC-00055 updated successfully\"\n}"}]},{"name":"Delete Sales Credit","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits/{sales_credit}","host":["{{baseUrl}}"],"path":["api","sales-credits",":sales_credit"],"variable":[{"key":"sales_credit","value":"55","description":"The sales credit ID."}]},"description":"Deletes a single sales credit.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/sales-credits/{sales_credit}","host":["{{baseUrl}}"],"path":["api","sales-credits",":sales_credit"],"variable":[{"key":"sales_credit","value":"55","description":"The sales credit ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"sales credit SC-00055 deleted successfully\"\n}"}]},{"name":"Bulk Archive Sales Credits","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/sales-credits/archive","host":["{{baseUrl}}"],"path":["api","sales-credits","archive"]},"description":"Bulk archives sales credits by IDs or by applying current filters.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- ids (required if apply_to_all is false): Array of sales credit IDs to archive\n- apply_to_all (optional, boolean): If true, archives all records matching current filters\n- filters (optional): Filter set to apply when apply_to_all is true","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    55,\n    56\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/sales-credits/archive","host":["{{baseUrl}}"],"path":["api","sales-credits","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"sales credits bulk archived successfully\"\n}"}]},{"name":"Bulk Unarchive Sales Credits","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/sales-credits/unarchive","host":["{{baseUrl}}"],"path":["api","sales-credits","unarchive"]},"description":"Bulk unarchives sales credits by IDs or by applying current filters.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- ids (required if apply_to_all is false): Array of sales credit IDs to unarchive\n- apply_to_all (optional, boolean): If true, unarchives all records matching current filters\n- filters (optional): Filter set to apply when apply_to_all is true","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    55,\n    56\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/sales-credits/unarchive","host":["{{baseUrl}}"],"path":["api","sales-credits","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"sales credits bulk unarchived successfully\"\n}"}]},{"name":"Check Sales Credits Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/sales-credits/deletable","host":["{{baseUrl}}"],"path":["api","sales-credits","deletable"]},"description":"Checks whether each specified sales credit can be deleted (i.e., is not used/linked to payments or other records). Returns deletability status and reason for each.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- ids (required): Array of sales credit IDs to check (min 1)","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    55,\n    56,\n    57\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/sales-credits/deletable","host":["{{baseUrl}}"],"path":["api","sales-credits","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 55,\n      \"sales_credit_number\": \"SC-00055\",\n      \"deletable\": true,\n      \"reason\": null\n    },\n    {\n      \"id\": 56,\n      \"sales_credit_number\": \"SC-00056\",\n      \"deletable\": false,\n      \"reason\": \"Has associated payments\"\n    },\n    {\n      \"id\": 57,\n      \"sales_credit_number\": \"SC-00057\",\n      \"deletable\": true,\n      \"reason\": null\n    }\n  ]\n}"}]},{"name":"Archive Sales Credit","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits/{sales_credit}/archive","host":["{{baseUrl}}"],"path":["api","sales-credits",":sales_credit","archive"],"variable":[{"key":"sales_credit","value":"55","description":"The sales credit ID."}]},"description":"Archives a single sales credit. Returns a warning if already archived.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/sales-credits/{sales_credit}/archive","host":["{{baseUrl}}"],"path":["api","sales-credits",":sales_credit","archive"],"variable":[{"key":"sales_credit","value":"55","description":"The sales credit ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"sales credit SC-00055 archived successfully\"\n}"}]},{"name":"Unarchive Sales Credit","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits/{sales_credit}/unarchived","host":["{{baseUrl}}"],"path":["api","sales-credits",":sales_credit","unarchived"],"variable":[{"key":"sales_credit","value":"55","description":"The sales credit ID."}]},"description":"Unarchives a single sales credit. Returns a warning if not archived.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/sales-credits/{sales_credit}/unarchived","host":["{{baseUrl}}"],"path":["api","sales-credits",":sales_credit","unarchived"],"variable":[{"key":"sales_credit","value":"55","description":"The sales credit ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 55,\n    \"sales_credit_number\": \"SC-00055\",\n    \"archived_at\": null\n  },\n  \"message\": \"sales credit SC-00055 unarchived successfully\"\n}"}]},{"name":"Record Sales Credit Payment","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/sales-credits/paid","host":["{{baseUrl}}"],"path":["api","sales-credits","paid"]},"description":"Records a payment against a sales credit, marking it as paid.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- sales_credit_id (required): Sales credit ID to record payment for\n- payment_type_id (required): Payment type ID\n- payment_date (optional, date): Date of payment\n- amount (required, numeric, > 0): Payment amount — must not exceed remaining balance\n- external_reference (optional, max 255): External reference number\n- currency_id (optional): Currency ID","body":{"mode":"raw","raw":"{\n  \"sales_credit_id\": 55,\n  \"payment_type_id\": 2,\n  \"payment_date\": \"2025-04-15\",\n  \"amount\": 150,\n  \"external_reference\": \"REF-123456\",\n  \"currency_id\": 1\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/sales-credits/paid","host":["{{baseUrl}}"],"path":["api","sales-credits","paid"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 55,\n    \"sales_credit_number\": \"SC-00055\",\n    \"statuses\": {\n      \"credit_status\": \"paid\"\n    },\n    \"payments\": [\n      {\n        \"id\": 88,\n        \"amount\": 150,\n        \"payment_date\": \"2025-04-15\",\n        \"external_reference\": \"REF-123456\"\n      }\n    ]\n  },\n  \"message\": \"payment created successfully\"\n}"}]},{"name":"Update Payment","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/payments/{payment}","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","payments",":payment"],"variable":[{"key":"salesCredit","value":"55","description":"The sales credit ID."},{"key":"payment","value":"88","description":"The payment ID."}]},"description":"Updates an existing payment on a sales credit. All fields are optional (uses sometimes validation).\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- payment_date (optional, date): Updated payment date\n- amount (optional, numeric, > 0): Updated payment amount\n- external_reference (optional, max 255): External reference number\n- currency_id (optional): Currency ID","body":{"mode":"raw","raw":"{\n  \"payment_date\": \"2025-04-16\",\n  \"amount\": 120,\n  \"external_reference\": \"REF-UPDATED\",\n  \"currency_id\": 1\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/payments/{payment}","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","payments",":payment"],"variable":[{"key":"salesCredit","value":"55","description":"The sales credit ID."},{"key":"payment","value":"88","description":"The payment ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 55,\n    \"payments\": [\n      {\n        \"id\": 88,\n        \"amount\": 120,\n        \"payment_date\": \"2025-04-16\"\n      }\n    ]\n  },\n  \"message\": \"payment created successfully\"\n}"}]},{"name":"Delete Payment","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/payments/{payment}","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","payments",":payment"],"variable":[{"key":"salesCredit","value":"55","description":"The sales credit ID."},{"key":"payment","value":"88","description":"The payment ID."}]},"description":"Deletes a payment from a sales credit and recalculates the payment status.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/payments/{payment}","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","payments",":payment"],"variable":[{"key":"salesCredit","value":"55","description":"The sales credit ID."},{"key":"payment","value":"88","description":"The payment ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 55,\n    \"payments\": []\n  },\n  \"message\": \"payment deleted successfully\"\n}"}]},{"name":"List Notes","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/notes","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","notes"],"variable":[{"key":"salesCredit","value":"55","description":"The sales credit ID."}]},"description":"Returns a paginated list of notes for a sales credit, ordered by created_at descending. Includes the note author.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/notes","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","notes"],"variable":[{"key":"salesCredit","value":"55","description":"The sales credit ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 33,\n      \"note\": \"Customer confirmed return shipment sent\",\n      \"created_at\": \"2025-04-11T10:00:00.000000Z\",\n      \"user\": {\n        \"id\": 1,\n        \"name\": \"Admin User\"\n      }\n    }\n  ],\n  \"per_page\": 15,\n  \"total\": 1\n}"}]},{"name":"Add Note","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/notes","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","notes"],"variable":[{"key":"salesCredit","value":"55","description":"The sales credit ID."}]},"description":"Adds a note to a sales credit.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- note (required): Note text","body":{"mode":"raw","raw":"{\n  \"note\": \"Customer confirmed return shipment sent\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/notes","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","notes"],"variable":[{"key":"salesCredit","value":"55","description":"The sales credit ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 34,\n    \"note\": \"Customer confirmed return shipment sent\",\n    \"created_at\": \"2025-04-23T10:00:00.000000Z\",\n    \"user\": {\n      \"id\": 1,\n      \"name\": \"Admin User\"\n    }\n  },\n  \"message\": \"note created successfully\"\n}"}]},{"name":"Delete Note","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/notes/{noteId}","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","notes",":noteId"],"variable":[{"key":"salesCredit","value":"55","description":"The sales credit ID."},{"key":"noteId","value":"33","description":"The note ID."}]},"description":"Deletes a note from a sales credit.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/notes/{noteId}","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","notes",":noteId"],"variable":[{"key":"salesCredit","value":"55","description":"The sales credit ID."},{"key":"noteId","value":"33","description":"The note ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"note 33 deleted successfully\"\n}"}]},{"name":"Generate Accounting Transaction","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/generate-accounting-transaction","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","generate-accounting-transaction"],"variable":[{"key":"salesCredit","value":"55","description":"The sales credit ID."}]},"description":"Generates an accounting transaction for the sales credit (e.g., for syncing to accounting software like QuickBooks Online).\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/generate-accounting-transaction","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","generate-accounting-transaction"],"variable":[{"key":"salesCredit","value":"55","description":"The sales credit ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 78,\n    \"reference\": \"SC-00055\",\n    \"amount\": 150\n  },\n  \"message\": \"Accounting transaction generated successfully.\"\n}"}]},{"name":"Preview Invoice","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits/{sales_credit}/invoice/preview","host":["{{baseUrl}}"],"path":["api","sales-credits",":sales_credit","invoice","preview"],"variable":[{"key":"sales_credit","value":"55","description":"The sales credit ID."}]},"description":"Generates a PDF invoice preview for the sales credit and returns the URL to the generated file.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/sales-credits/{sales_credit}/invoice/preview","host":["{{baseUrl}}"],"path":["api","sales-credits",":sales_credit","invoice","preview"],"variable":[{"key":"sales_credit","value":"55","description":"The sales credit ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"path\": \"https://sku2.test/storage/reports/sales-credit-55-invoice.pdf\"\n  }\n}"}]},{"name":"Export Sales Credits","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits/list/export","host":["{{baseUrl}}"],"path":["api","sales-credits","list","export"]},"description":"Download an Excel/CSV export of sales credits.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope.rw:orders).\n\nResponse: a binary file download (xlsx or csv), NOT JSON. Columns: ID, Credit #, Sales Order #, Store, Customer, Customer Email, Credit Date, Status, Payment Status, Currency, Tax Total, Total Credit, Refunded, Note, Created At.\n\nQuery params:\n- format: xlsx (default) or csv\n- scope: 'filtered' (default) or 'selected'\n- ids: comma-separated IDs (only when scope=selected)\n\n**Documents.** The export carries a `Documents` column holding the number of supporting documents attached to each credit."}},{"name":"Download Import Template","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits/import/template","host":["{{baseUrl}}"],"path":["api","sales-credits","import","template"]},"description":"Download a multi-sheet XLSX import template pre-filled with sample rows, a field guide, and reference data (stores, currencies, warehouses, tax rates, nominal codes) from your account.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope.rw:orders).\n\nResponse: a binary XLSX file download, NOT JSON. Fill the 'Import Here' sheet, then upload it in the import wizard."}},{"name":"Validate Import","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/sales-credits/import/validate","host":["{{baseUrl}}"],"path":["api","sales-credits","import","validate"]},"description":"Validate and preview mapped import rows BEFORE importing. No database writes occur — safe to call repeatedly.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope.rw:orders).\n\nResolves customer / sales order / store / currency / warehouse / SKU / nominal code / tax rate names to IDs, groups flat rows into credits (by id, then sales_credit_number, then sales_order_number), and computes an upsert action per credit (create / update / blocked). Customers that don't exist are auto-created on import.\n\nEach row accepts all template columns: id, sales_credit_number, credit_date, sales_order_number, customer_email, customer_id, customer_name, customer_company, customer_phone, store_name, currency_code, to_warehouse_name, sales_credit_note, sku, sales_order_line_id, description, quantity, amount, unit_cost, nominal_code, tax_rate, is_product.\n\nMax 50000 rows per call.","body":{"mode":"raw","raw":"{\n  \"rows\": [\n    {\n      \"id\": \"\",\n      \"sales_credit_number\": \"SC-001\",\n      \"credit_date\": \"2026-04-01\",\n      \"sales_order_number\": \"\",\n      \"customer_email\": \"jane@example.com\",\n      \"customer_id\": \"\",\n      \"customer_name\": \"Jane Doe\",\n      \"customer_company\": \"\",\n      \"customer_phone\": \"\",\n      \"store_name\": \"\",\n      \"currency_code\": \"USD\",\n      \"to_warehouse_name\": \"Main Warehouse\",\n      \"sales_credit_note\": \"Returned item\",\n      \"sku\": \"WIDGET-001\",\n      \"sales_order_line_id\": \"\",\n      \"description\": \"Blue Widget - Large\",\n      \"quantity\": \"1\",\n      \"amount\": \"19.99\",\n      \"unit_cost\": \"\",\n      \"nominal_code\": \"\",\n      \"tax_rate\": \"\",\n      \"is_product\": \"true\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/sales-credits/import/validate","host":["{{baseUrl}}"],"path":["api","sales-credits","import","validate"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"sales_credits\": [\n    {\n      \"group_key\": \"scn:sc-001\",\n      \"id\": null,\n      \"sales_credit_number\": \"SC-001\",\n      \"credit_date\": \"2026-04-01\",\n      \"sales_credit_note\": \"Returned item\",\n      \"action\": \"create\",\n      \"match_reason\": null,\n      \"block_reason\": null,\n      \"existing_sales_credit\": null,\n      \"sales_order\": null,\n      \"customer\": {\n        \"id\": null,\n        \"email\": \"jane@example.com\",\n        \"name\": \"Jane Doe\",\n        \"will_create\": true\n      },\n      \"store\": null,\n      \"currency\": {\n        \"id\": 1,\n        \"code\": \"USD\"\n      },\n      \"warehouse\": {\n        \"id\": 3,\n        \"name\": \"Main Warehouse\"\n      },\n      \"errors\": [],\n      \"lines\": [\n        {\n          \"sku\": \"WIDGET-001\",\n          \"product_id\": 845,\n          \"product_name\": \"Blue Widget - Large\",\n          \"sales_order_line_id\": null,\n          \"description\": \"Blue Widget - Large\",\n          \"quantity\": 1,\n          \"amount\": 19.99,\n          \"unit_cost\": null,\n          \"nominal_code_id\": null,\n          \"tax_rate_id\": null,\n          \"is_product\": true,\n          \"line_total\": 19.99,\n          \"errors\": []\n        }\n      ],\n      \"total_quantity\": 1,\n      \"total_amount\": 19.99\n    }\n  ],\n  \"summary\": {\n    \"total_sales_credits\": 1,\n    \"total_line_items\": 1,\n    \"total_errors\": 0,\n    \"total_will_create\": 1,\n    \"total_will_update\": 0,\n    \"total_blocked\": 0,\n    \"customers_created\": 1\n  }\n}"}]},{"name":"Import Sales Credits","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/sales-credits/import","host":["{{baseUrl}}"],"path":["api","sales-credits","import"]},"description":"Execute the import — upsert sales credits from validated data (typically the output of the Validate Import endpoint).\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope.rw:orders).\n\nUpsert behavior:\n- Rows with no matched existing credit are CREATED (customers are auto-created when needed).\n- Rows matching an existing editable credit are UPDATED.\n- Rows with action=blocked are rejected (paid / allocated / closed / Shopify-origin credits cannot be updated via import).\n\ncontinue_on_error (default false): when true, each credit is imported in its own transaction and failures are collected into 'skipped'. When false, the entire import runs in one transaction.\n\nReturns 201 when any credit was created, otherwise 200.","body":{"mode":"raw","raw":"{\n  \"continue_on_error\": false,\n  \"sales_credits\": [\n    {\n      \"group_key\": \"scn:sc-001\",\n      \"id\": null,\n      \"action\": \"create\",\n      \"match_reason\": null,\n      \"block_reason\": null,\n      \"existing_sales_credit\": null,\n      \"sales_credit_number\": \"SC-001\",\n      \"credit_date\": \"2026-04-01\",\n      \"sales_credit_note\": \"Returned item\",\n      \"sales_order\": null,\n      \"customer\": {\n        \"id\": null,\n        \"email\": \"jane@example.com\",\n        \"name\": \"Jane Doe\"\n      },\n      \"store\": null,\n      \"currency\": {\n        \"id\": 1,\n        \"code\": \"USD\"\n      },\n      \"warehouse\": {\n        \"id\": 3,\n        \"name\": \"Main Warehouse\"\n      },\n      \"lines\": [\n        {\n          \"sku\": \"WIDGET-001\",\n          \"product_id\": 845,\n          \"product_name\": \"Blue Widget - Large\",\n          \"sales_order_line_id\": null,\n          \"description\": \"Blue Widget - Large\",\n          \"quantity\": 1,\n          \"amount\": 19.99,\n          \"unit_cost\": null,\n          \"nominal_code_id\": null,\n          \"tax_rate_id\": null,\n          \"is_product\": true\n        }\n      ]\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/sales-credits/import","host":["{{baseUrl}}"],"path":["api","sales-credits","import"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"created\": [\n    {\n      \"id\": 301,\n      \"sales_credit_number\": \"SC-001\"\n    }\n  ],\n  \"updated\": [],\n  \"skipped\": [],\n  \"customers_created\": [\n    {\n      \"id\": 99,\n      \"name\": \"Jane Doe\",\n      \"email\": \"jane@example.com\"\n    }\n  ],\n  \"summary\": {\n    \"total\": 1,\n    \"created\": 1,\n    \"updated\": 0,\n    \"skipped\": 0,\n    \"customers_created\": 1\n  }\n}"}]},{"name":"List Sales Credit Documents","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/attachments","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","attachments"],"variable":[{"key":"salesCredit","value":"","description":"The sales credit ID."}]},"description":"Lists every supporting document attached to the sales credit, oldest first.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nEach document reports its original file name, byte size, MIME type, who uploaded it, and a ready-to-use `download_url`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/attachments","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","attachments"],"variable":[{"key":"salesCredit","value":"","description":"The sales credit ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 812,\n      \"file_name\": \"credit-note-SC-1042.pdf\",\n      \"file_size\": 184320,\n      \"mime_type\": \"application/pdf\",\n      \"created_at\": \"2026-07-22T14:05:31.000000Z\",\n      \"uploaded_by\": {\n        \"id\": 7,\n        \"name\": \"Dana Whitfield\"\n      },\n      \"download_url\": \"https://acme.sku.io/api/sales-credits/1042/attachments/812/download\"\n    }\n  ]\n}"}]},{"name":"Upload Sales Credit Document","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/attachments","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","attachments"],"variable":[{"key":"salesCredit","value":"","description":"The sales credit ID."}]},"description":"Uploads a supporting document to the sales credit.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nSend the file as `multipart/form-data` under the `file` key. Accepted types: PDF, PNG, JPG, JPEG, WEBP. Maximum size: 20 MB. Empty files are rejected.\n\nReturns 422 when the credit is archived — archived credits are read-only history.","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/attachments","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","attachments"],"variable":[{"key":"salesCredit","value":"","description":"The sales credit ID."}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 812,\n    \"file_name\": \"credit-note-SC-1042.pdf\",\n    \"file_size\": 184320,\n    \"mime_type\": \"application/pdf\",\n    \"created_at\": \"2026-07-22T14:05:31.000000Z\",\n    \"uploaded_by\": {\n      \"id\": 7,\n      \"name\": \"Dana Whitfield\"\n    },\n    \"download_url\": \"https://acme.sku.io/api/sales-credits/1042/attachments/812/download\"\n  }\n}"}]},{"name":"Delete Sales Credit Document","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/attachments/{attachment}","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","attachments",":attachment"],"variable":[{"key":"salesCredit","value":"","description":"The sales credit ID."},{"key":"attachment","value":"","description":"The attachment ID."}]},"description":"Permanently deletes a document from the sales credit, removing both the record and the stored file.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 404 if the document does not belong to the given credit, and 422 if the credit is archived."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/attachments/{attachment}","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","attachments",":attachment"],"variable":[{"key":"salesCredit","value":"","description":"The sales credit ID."},{"key":"attachment","value":"","description":"The attachment ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Attachment deleted.\"\n}"}]},{"name":"Download Sales Credit Document","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/attachments/{attachment}/download","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","attachments",":attachment","download"],"variable":[{"key":"salesCredit","value":"","description":"The sales credit ID."},{"key":"attachment","value":"","description":"The attachment ID."}]},"description":"Streams the document back as a file download (Content-Disposition: attachment).\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nThe response is the raw file, not JSON. Returns 404 if the document does not belong to the given credit."}},{"name":"View Sales Credit Document Inline","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sales-credits/{salesCredit}/attachments/{attachment}/view","host":["{{baseUrl}}"],"path":["api","sales-credits",":salesCredit","attachments",":attachment","view"],"variable":[{"key":"salesCredit","value":"","description":"The sales credit ID."},{"key":"attachment","value":"","description":"The attachment ID."}]},"description":"Streams the document for inline display (Content-Disposition: inline) — used to preview a PDF or image without downloading it.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nThe response is the raw file, not JSON. Returns 404 if the document does not belong to the given credit."}}]},{"name":"Reporting","item":[{"name":"Ship By Schedule","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/reporting/ship-by-schedule","host":["{{baseUrl}}"],"path":["api","v2","reporting","ship-by-schedule"]},"description":"Ship-by schedule: open demand grouped by the day each order must ship, and per product within that day, with the quantity still short of stock.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nDays are calendar days in your account timezone. A ship-by value is stored as an instant, so the report converts it to your timezone before grouping — an order due 23 September appears under `2026-09-23` regardless of the underlying UTC offset, and `filter[date_from]` / `filter[date_to]` select exactly the days you name.\n\nDefault window: with neither date filter supplied, the report covers the last 30 days plus all future dates. Supplying either date filter replaces that window entirely.\n\nFilters:\n- `filter[search]` — partial match on product SKU or product name.\n- `filter[date_from]` — earliest ship-by day to include (`YYYY-MM-DD`, inclusive).\n- `filter[date_to]` — latest ship-by day to include (`YYYY-MM-DD`, inclusive).\n- `filter[status]` — `all` (default), `past_due`, `due_today`, or `upcoming`, relative to today in your account timezone.\n\nSorting: `sort` accepts `ship_by_date`, `total_orders`, `total_quantity`, `product_sku`. Prefix with `-` for descending. Default: `ship_by_date` ascending.\n\nPagination: `page` and `per_page` (default 10). Each row is one product on one ship-by day; a day with several products returns several rows sharing the same `ship_by_date`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/reporting/ship-by-schedule","host":["{{baseUrl}}"],"path":["api","v2","reporting","ship-by-schedule"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"ship_by_date\": \"2026-09-23\",\n      \"product_id\": 8821,\n      \"product_sku\": \"LB-CL-FZ-NA-2PACK\",\n      \"product_name\": \"Lobster Claws, Frozen — 2 Pack\",\n      \"total_quantity\": 48,\n      \"shortage_quantity\": 6,\n      \"covered_quantity\": 4,\n      \"uncovered_quantity\": 2,\n      \"total_orders\": 21\n    },\n    {\n      \"ship_by_date\": \"2026-09-23\",\n      \"product_id\": 8834,\n      \"product_sku\": \"LB-TL-FZ-6-7-2PACK\",\n      \"product_name\": \"Lobster Tails 6-7 oz — 2 Pack\",\n      \"total_quantity\": 40,\n      \"shortage_quantity\": 0,\n      \"covered_quantity\": 0,\n      \"uncovered_quantity\": 0,\n      \"total_orders\": 15\n    },\n    {\n      \"ship_by_date\": \"2026-09-24\",\n      \"product_id\": 8821,\n      \"product_sku\": \"LB-CL-FZ-NA-2PACK\",\n      \"product_name\": \"Lobster Claws, Frozen — 2 Pack\",\n      \"total_quantity\": 45,\n      \"shortage_quantity\": 12,\n      \"covered_quantity\": 12,\n      \"uncovered_quantity\": 0,\n      \"total_orders\": 13\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 10,\n  \"total\": 27,\n  \"from\": 1,\n  \"to\": 10,\n  \"first_page_url\": \"https://example.sku.io/api/v2/reporting/ship-by-schedule?page=1\",\n  \"last_page_url\": \"https://example.sku.io/api/v2/reporting/ship-by-schedule?page=3\",\n  \"next_page_url\": \"https://example.sku.io/api/v2/reporting/ship-by-schedule?page=2\",\n  \"prev_page_url\": null,\n  \"path\": \"https://example.sku.io/api/v2/reporting/ship-by-schedule\"\n}"}]},{"name":"Get Inventory Planning Report","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning?page=1&per_page=25","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"per_page","value":"25","description":"Items per page","disabled":true}]},"description":"Returns a paginated inventory planning report with replenishment data per product. Supports warehouse filtering via filter[warehouse_id], filter[warehouse_id][is], or base64-encoded filter_groups tree.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nPagination: Default per page from request or manager default\n\nThe response also includes `unfiltered_total`, the count of rows in the base scope before any user filters are applied — used by the frontend to show export totals."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning?page=1&per_page=25","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"per_page","value":"25","description":"Items per page","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 42,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Blue Widget\",\n      \"barcode\": \"1234567890\",\n      \"type\": \"standard\",\n      \"brand\": \"Acme\",\n      \"brand_id\": 5,\n      \"available_qty\": 150,\n      \"available_to_sell\": 150,\n      \"total_stock_qty\": 180,\n      \"reserved_qty\": 20,\n      \"in_transit_qty\": 10,\n      \"inbound_qty\": 50,\n      \"projected_total\": 200,\n      \"active_backorder_qty\": 0,\n      \"net_available\": 150,\n      \"supplier_name\": \"Widget Co\",\n      \"supplier_id\": 10,\n      \"supplier_sku\": \"WC-WIDGET-001\",\n      \"supplier_price\": 8.5,\n      \"supplier_currency_code\": \"USD\",\n      \"average_cost\": 9.25,\n      \"stock_value\": 1665,\n      \"estimated_landed_cost\": 10.2,\n      \"gross_margin_pct\": 0.42,\n      \"length\": 10,\n      \"width\": 5,\n      \"height\": 3,\n      \"dimension_unit\": \"cm\",\n      \"weight\": 0.5,\n      \"weight_unit\": \"kg\",\n      \"cbm\": 0.00015,\n      \"volume\": 0.00015,\n      \"volume_unit\": \"CBM\",\n      \"qty_sold_30d\": 45,\n      \"qty_sold_60d\": 90,\n      \"qty_sold_90d\": 135,\n      \"qty_sold_180d\": 270,\n      \"monthly_avg_sales\": 45,\n      \"daily_avg_sales\": 1.5,\n      \"revenue_180d\": 4050,\n      \"demand_trend_pct\": 12.5,\n      \"demand_trend_direction\": \"stable\",\n      \"demand_trend_confidence\": \"high\",\n      \"days_of_stock\": 100,\n      \"days_until_stockout\": 133.3,\n      \"stockout_risk\": \"low\",\n      \"stockout_risk_color\": \"success\",\n      \"abc_classification\": \"A\",\n      \"xyz_classification\": \"X\",\n      \"abc_xyz_cell\": \"AX\",\n      \"sell_through_rate\": 0.643,\n      \"inventory_turnover\": 3,\n      \"revenue_at_risk\": 0,\n      \"overstock_qty\": 0,\n      \"overstock_value\": 0,\n      \"is_dead_stock\": false,\n      \"dead_stock_value\": null,\n      \"lead_time\": 30,\n      \"avg_actual_lead_time\": 28.5,\n      \"lead_time_reliability_pct\": 0.92,\n      \"lead_time_variance\": 3.2,\n      \"min_order_qty\": 50,\n      \"target_stock_days\": 60,\n      \"min_stock_level\": 20,\n      \"reorder_point\": 65,\n      \"suggested_reorder_qty\": 50,\n      \"case_quantity\": 12,\n      \"suggested_cases\": 5,\n      \"adjusted_reorder_qty\": 60,\n      \"reorder_cbm\": 0.009,\n      \"reorder_cost\": 510,\n      \"reorder_landed_cost\": 612,\n      \"next_po_date\": \"2024-07-15\",\n      \"next_po_qty\": 100,\n      \"days_until_relief\": 21,\n      \"velocity_computed_at\": \"2024-06-14T08:00:00.000000Z\"\n    }\n  ],\n  \"per_page\": 25,\n  \"total\": 342,\n  \"unfiltered_total\": 2103,\n  \"last_page\": 14\n}"}]},{"name":"Get Inventory Planning Column Totals","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/aggregates?aggregates[available_qty]=sum","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","aggregates"],"query":[{"key":"aggregates[available_qty]","value":"sum","description":"aggregates[&lt;column>]=&lt;calc>. Repeat for each column to total.","disabled":true}]},"description":"Column-summary totals for the report's \"Calculate\" footer — computes Sum, Average, Min, Max, Range, or a Count variant per column over the FULL filtered set (not just the current page), in one query.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAggregatable columns: available_qty, available_to_sell, total_stock_qty, reserved_qty, in_transit_qty, inbound_qty, projected_total, active_backorder_qty, net_available, stock_value, min_stock_level, qty_sold_30d, qty_sold_60d, qty_sold_90d, qty_sold_180d, revenue_180d. Supported calcs: sum, avg, min, max, range, count_all, count_values, count_unique, count_empty, count_not_empty. Pass them as aggregates[&lt;column>]=&lt;calc>. Totals cover the stock-quantity, stock-value and sales columns.\n\nApply the same filters and search as the list endpoint to scope the totals. An unknown calc returns 422. Authentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/aggregates?aggregates[available_qty]=sum","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","aggregates"],"query":[{"key":"aggregates[available_qty]","value":"sum","description":"aggregates[&lt;column>]=&lt;calc>. Repeat for each column to total.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"stock_value\": {\n      \"calc\": \"sum\",\n      \"value\": 486320\n    },\n    \"available_qty\": {\n      \"calc\": \"sum\",\n      \"value\": 58210\n    },\n    \"qty_sold_180d\": {\n      \"calc\": \"sum\",\n      \"value\": 91044\n    }\n  }\n}"}]},{"name":"Get Inventory Planning Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/summary","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","summary"]},"description":"Returns summary metrics for the inventory planning report header cards (counts of items at risk, overstocked, out of stock, etc.).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nneeds_reorder_count: products whose Suggested Qty is above 0 — the same rule the report's \"Suggested Qty > 0\" filter applies, so the Need Reorder count always equals the rows that filter returns (products with no default supplier included). supplier_consolidation_totals.item_count is the per-supplier subset only.\n\nDays of stock / days until stockout are floored at 0: a product oversold into negative availability reports 0 days, not a negative number."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/summary","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_products\": 342,\n    \"needs_reorder_count\": 10,\n    \"high_risk\": 12,\n    \"medium_risk\": 28,\n    \"low_risk\": 302,\n    \"out_of_stock\": 5,\n    \"dead_stock\": 8,\n    \"total_stock_value\": 125400,\n    \"total_reorder_cost\": 38200\n  }\n}"}]},{"name":"Export Inventory Planning Report","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/export?format=csv","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","export"],"query":[{"key":"format","value":"csv","description":"csv (default) | xlsx","disabled":true}]},"description":"Exports the inventory planning report as CSV or XLSX. Supports column selection and warehouse filtering.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nResponse: text/csv or application/vnd.openxmlformats-officedocument.spreadsheetml.sheet file download"}},{"name":"Refresh Velocity Data","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/refresh-velocity","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","refresh-velocity"]},"description":"Dispatches a tracked background job to recompute product velocity cache and supplier lead time reliability. Returns a tracked_job_log_id for monitoring via the tracked job log endpoints.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/refresh-velocity","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","refresh-velocity"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 87\n  },\n  \"message\": \"Velocity cache refresh started. Track progress in the job tray.\"\n}"}]},{"name":"Refresh Sales Financials","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/refresh-financials","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","refresh-financials"]},"description":"Dispatches a tracked background job to refresh sales financials data used by the inventory planning report. Returns a tracked_job_log_id for monitoring via the tracked job log endpoints.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/refresh-financials","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","refresh-financials"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 88\n  },\n  \"message\": \"Sales financials refresh started. Track progress in the job tray.\"\n}"}]},{"name":"Create Draft PO from Planning","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/create-po","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","create-po"]},"description":"Creates a draft purchase order from reorder suggestions in the inventory planning report.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nRequired fields:\n- supplier_id: integer (must exist)\n- product_ids: array of integers (min 1, must exist)\n\nOptional fields:\n- quantities: object keyed by product_id with integer values (min 1)\n- use_adjusted_qty: boolean (default: true) — use the adjusted reorder qty\n- order_status: 'draft' | 'open' (default: 'draft')\n- destination_warehouse_id: integer (must exist)","body":{"mode":"raw","raw":"{\n  \"supplier_id\": 10,\n  \"product_ids\": [\n    42,\n    43,\n    44\n  ],\n  \"use_adjusted_qty\": true,\n  \"order_status\": \"draft\",\n  \"destination_warehouse_id\": 1,\n  \"quantities\": {\n    \"42\": 100,\n    \"43\": 50\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/create-po","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","create-po"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"purchase_order_id\": 350,\n    \"purchase_order_number\": \"PO-350\",\n    \"line_count\": 3,\n    \"total_cost\": 2550,\n    \"order_status\": \"draft\",\n    \"destination_warehouse\": \"Main Warehouse\"\n  },\n  \"message\": \"Purchase order created\"\n}"}]},{"name":"List Scheduled Reports","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/schedules","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","schedules"]},"description":"Returns all scheduled reports for the authenticated user for the inventory_planning report type.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/schedules","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","schedules"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Weekly Inventory Report\",\n      \"report_type\": \"inventory_planning\",\n      \"frequency\": \"weekly\",\n      \"day_of_week\": 1,\n      \"day_of_month\": null,\n      \"time\": \"08:00\",\n      \"timezone\": \"America/New_York\",\n      \"format\": \"xlsx\",\n      \"recipients\": [\n        \"manager@example.com\"\n      ],\n      \"filters\": {\n        \"warehouse_id\": 1\n      },\n      \"columns\": [\n        \"sku\",\n        \"name\",\n        \"available_qty\",\n        \"suggested_reorder_qty\"\n      ],\n      \"is_active\": true,\n      \"last_sent_at\": \"2024-06-10T08:00:00+00:00\",\n      \"next_run_at\": \"2024-06-17T08:00:00+00:00\",\n      \"created_at\": \"2024-01-15T10:00:00+00:00\",\n      \"updated_at\": \"2024-06-10T08:00:00+00:00\"\n    }\n  ]\n}"}]},{"name":"Create Scheduled Report","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/schedules","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","schedules"]},"description":"Creates a new scheduled inventory planning report for the authenticated user.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nRequired fields:\n- name: string (max 255)\n- frequency: daily | weekly | monthly\n- time: HH:MM (24h format)\n- timezone: valid timezone string\n- format: csv | xlsx\n- recipients: array of emails (min 1)\n\nConditional required:\n- day_of_week: integer 0-6 (required when frequency=weekly)\n- day_of_month: integer 1-28 (required when frequency=monthly)\n\nOptional:\n- filters: object of filter values\n- columns: array of column names","body":{"mode":"raw","raw":"{\n  \"name\": \"Weekly Inventory Report\",\n  \"frequency\": \"weekly\",\n  \"day_of_week\": 1,\n  \"time\": \"08:00\",\n  \"timezone\": \"America/New_York\",\n  \"format\": \"xlsx\",\n  \"recipients\": [\n    \"manager@example.com\",\n    \"buyer@example.com\"\n  ],\n  \"filters\": {\n    \"warehouse_id\": 1\n  },\n  \"columns\": [\n    \"sku\",\n    \"name\",\n    \"available_qty\",\n    \"suggested_reorder_qty\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/schedules","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","schedules"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 2,\n    \"name\": \"Weekly Inventory Report\",\n    \"report_type\": \"inventory_planning\",\n    \"frequency\": \"weekly\",\n    \"day_of_week\": 1,\n    \"time\": \"08:00\",\n    \"timezone\": \"America/New_York\",\n    \"format\": \"xlsx\",\n    \"recipients\": [\n      \"manager@example.com\"\n    ],\n    \"is_active\": true,\n    \"created_at\": \"2024-06-15T10:00:00+00:00\"\n  },\n  \"message\": \"Scheduled report created\"\n}"}]},{"name":"Delete Scheduled Report","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/schedules/{scheduledReport}","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","schedules",":scheduledReport"],"variable":[{"key":"scheduledReport","value":"1","description":"Scheduled Report ID"}]},"description":"Deletes a scheduled report. Only the owner can delete their own scheduled reports.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/reporting/inventory-planning/schedules/{scheduledReport}","host":["{{baseUrl}}"],"path":["api","reporting","inventory-planning","schedules",":scheduledReport"],"variable":[{"key":"scheduledReport","value":"1","description":"Scheduled Report ID"}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":""}]},{"name":"Get Profitability Trend","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/profitability/trend?group_by=brand&interval=month&limit=12&sort=desc","host":["{{baseUrl}}"],"path":["api","reporting","profitability","trend"],"query":[{"key":"group_by","value":"brand","description":"Required. Dimension: brand | sku | supplier | sales_channel | product_type | sub_sales_channel | customer_tags. customer_tags fans out — an order counts toward each of its customer's tags — and the grand total is deduplicated. customer_tags does not require attribute_id.","disabled":true},{"key":"interval","value":"month","description":"Required. Time interval: day | week | month | quarter | year","disabled":true},{"key":"limit","value":"12","description":"Optional. Number of periods to return","disabled":true},{"key":"sort","value":"desc","description":"Optional. Sort order: asc | desc","disabled":true}]},"description":"Returns trend data (per-interval metrics) grouped by a dimension. When indirect costs are included (default), per-interval total cost and profit reflect cost-entry allocations; period allocations are day-prorated across intervals. Margin-ladder metrics are available: gross_profit / gross_margin (revenue minus COGS), contribution_margin / contribution_margin_pct (after channel and indirect costs), plus ad_spend and tacos for sku, brand, and supplier groupings.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRows may include {interval}_indirect_cost keys and a 'Non-Product / Unallocated' row (is_unallocated: true) for amounts that cannot be attributed to the chosen grouping.\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/profitability/trend?group_by=brand&interval=month&limit=12&sort=desc","host":["{{baseUrl}}"],"path":["api","reporting","profitability","trend"],"query":[{"key":"group_by","value":"brand","description":"Required. Dimension: brand | sku | supplier | sales_channel | product_type | sub_sales_channel | customer_tags. customer_tags fans out — an order counts toward each of its customer's tags — and the grand total is deduplicated. customer_tags does not require attribute_id.","disabled":true},{"key":"interval","value":"month","description":"Required. Time interval: day | week | month | quarter | year","disabled":true},{"key":"limit","value":"12","description":"Optional. Number of periods to return","disabled":true},{"key":"sort","value":"desc","description":"Optional. Sort order: asc | desc","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"period\": \"2024-06\",\n      \"dimension_value\": \"Acme Brand\",\n      \"revenue\": 15200,\n      \"cogs\": 8500,\n      \"total_cost\": 9200,\n      \"profit\": 6000,\n      \"quantity\": 320,\n      \"num_orders\": 45,\n      \"margin\": 39.47\n    }\n  ]\n}"}]},{"name":"Get Profitability Cost Breakdown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/profitability/cost-breakdown?group_by=brand","host":["{{baseUrl}}"],"path":["api","reporting","profitability","cost-breakdown"],"query":[{"key":"group_by","value":"brand","description":"Required. Dimension: brand | sku | supplier | sales_channel | product_type | sub_sales_channel | customer_tags. customer_tags fans out — an order counts toward each of its customer's tags — and the grand total is deduplicated. customer_tags does not require attribute_id.","disabled":true}]},"description":"Returns cost breakdown rows per dimension with financial line type columns, indirect costs (cost-entry allocations), and the margin ladder: gross_profit / gross_margin, contribution_margin / contribution_margin_pct, and company-level operating_profit (contribution margin minus unallocated indirect costs).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nIndirect costs attribute direct dimension-target allocations plus product-target allocations rolled up to the grouping; unattributable amounts appear on the 'Non-Product / Unallocated' row so grand totals cover all allocations in range. Rows and grand_totals carry cost_allocated (direct cost allocated to the order lines) and cogs_returned, so total_cost = cogs + cost_allocated − cogs_returned + indirect_cost reconciles; cost_allocated minus the cost line-type columns is allocated cost no line type explains. For sku, brand, and supplier groupings, rows also carry ad_spend, acos, and tacos (a breakout of indirect costs — already included in the totals, never added twice).\n\nAuthentication: Requires Bearer token\n\n**Fee-source coverage** — `fee_source_coverage` lists every sales channel that produced orders in the window, with `orders_in_range`, `orders_with_fee_line`, and a `state` of `actual`, `estimated`, `pending`, `none`, or `unsupported`. A channel in `none` produced revenue with no marketplace-fee cost at all, so its margin — and the blended contribution margin in `grand_totals` — is overstated by whatever that channel charged. `marketplace_fee_financial_line_type_ids` names the cost columns that caveat applies to. Use it to qualify the totals rather than reading a missing fee as a zero fee."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/profitability/cost-breakdown?group_by=brand","host":["{{baseUrl}}"],"path":["api","reporting","profitability","cost-breakdown"],"query":[{"key":"group_by","value":"brand","description":"Required. Dimension: brand | sku | supplier | sales_channel | product_type | sub_sales_channel | customer_tags. customer_tags fans out — an order counts toward each of its customer's tags — and the grand total is deduplicated. customer_tags does not require attribute_id.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"dimensions\": [\n      {\n        \"rowLabel\": \"Acme Brand\",\n        \"revenue\": 45000,\n        \"cogs\": 25000,\n        \"cost_allocated\": 3200,\n        \"cogs_returned\": 0,\n        \"indirect_cost\": 1800,\n        \"indirect_cost_by_type\": {\n          \"2\": 1200,\n          \"5\": 600\n        },\n        \"total_cost\": 30000,\n        \"profit\": 15000,\n        \"margin\": 33.33,\n        \"gross_profit\": 20000,\n        \"gross_margin\": 44.44,\n        \"contribution_margin\": 15000,\n        \"contribution_margin_pct\": 33.33,\n        \"flt_values\": {\n          \"1\": 3200\n        },\n        \"is_unallocated\": false\n      },\n      {\n        \"rowLabel\": \"Non-Product / Unallocated\",\n        \"revenue\": 0,\n        \"cogs\": 0,\n        \"cost_allocated\": 0,\n        \"cogs_returned\": 0,\n        \"indirect_cost\": 500,\n        \"indirect_cost_by_type\": {\n          \"2\": 500\n        },\n        \"total_cost\": 500,\n        \"profit\": -500,\n        \"margin\": null,\n        \"gross_profit\": 0,\n        \"gross_margin\": null,\n        \"contribution_margin\": -500,\n        \"contribution_margin_pct\": null,\n        \"flt_values\": {},\n        \"is_unallocated\": true\n      }\n    ],\n    \"financial_line_types\": [\n      {\n        \"id\": 1,\n        \"name\": \"Shipping\",\n        \"classification\": \"COST\",\n        \"total\": 3200\n      }\n    ],\n    \"indirect_cost_types\": [\n      {\n        \"id\": 2,\n        \"name\": \"Marketing\",\n        \"total\": 1700\n      },\n      {\n        \"id\": 5,\n        \"name\": \"Software\",\n        \"total\": 600\n      }\n    ],\n    \"grand_totals\": {\n      \"revenue\": 45000,\n      \"cogs\": 25000,\n      \"cost_allocated\": 3200,\n      \"cogs_returned\": 0,\n      \"total_cost\": 30500,\n      \"profit\": 14500,\n      \"margin\": 32.22,\n      \"gross_profit\": 20000,\n      \"gross_margin\": 44.44,\n      \"indirect_cost\": 2300,\n      \"contribution_margin\": 14500,\n      \"contribution_margin_pct\": 32.22,\n      \"unallocated_indirect_cost\": 750,\n      \"operating_profit\": 13750\n    },\n    \"comparison\": {\n      \"mode\": \"previous_period\",\n      \"window\": {\n        \"start_date\": \"2026-05-01\",\n        \"end_date\": \"2026-05-31\"\n      },\n      \"revenue\": 41000,\n      \"cogs\": 23500,\n      \"total_cost\": 28100,\n      \"profit\": 12900,\n      \"margin\": 31.46,\n      \"gross_profit\": 17500,\n      \"gross_margin\": 42.68,\n      \"indirect_cost\": 2100,\n      \"contribution_margin\": 12900,\n      \"contribution_margin_pct\": 31.46,\n      \"unallocated_indirect_cost\": 600,\n      \"operating_profit\": 12300,\n      \"ad_spend\": 900\n    },\n    \"fee_source_coverage\": [\n      {\n        \"integration_instance_id\": 2,\n        \"channel_name\": \"Shopify - Main Store\",\n        \"integration_name\": \"Shopify\",\n        \"state\": \"actual\",\n        \"charges_zero_configured_fee\": false,\n        \"orders_in_range\": 412,\n        \"orders_with_fee_line\": 409\n      },\n      {\n        \"integration_instance_id\": 5,\n        \"channel_name\": \"Walmart US\",\n        \"integration_name\": \"Walmart\",\n        \"state\": \"none\",\n        \"charges_zero_configured_fee\": false,\n        \"orders_in_range\": 87,\n        \"orders_with_fee_line\": 0\n      }\n    ],\n    \"marketplace_fee_financial_line_type_ids\": [\n      17,\n      20\n    ]\n  }\n}"}]},{"name":"Get Profitability Drilldown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/profitability/drilldown?dimension=brand&dimension_value=Acme","host":["{{baseUrl}}"],"path":["api","reporting","profitability","drilldown"],"query":[{"key":"dimension","value":"brand","description":"Dimension: brand | sku | supplier | sales_channel | product_type | sub_sales_channel | customer_tags (nullable). customer_tags fans out — an order counts toward each of its customer's tags — and the grand total is deduplicated. customer_tags does not require attribute_id.","disabled":true},{"key":"dimension_value","value":"Acme","description":"Value of the dimension to filter by (nullable)","disabled":true}]},"description":"Returns paginated order-level drilldown for a specific dimension value, showing revenue, COGS, total cost, profit, and margin per order. Includes financial line type column values when flt_ids[] is provided.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/profitability/drilldown?dimension=brand&dimension_value=Acme","host":["{{baseUrl}}"],"path":["api","reporting","profitability","drilldown"],"query":[{"key":"dimension","value":"brand","description":"Dimension: brand | sku | supplier | sales_channel | product_type | sub_sales_channel | customer_tags (nullable). customer_tags fans out — an order counts toward each of its customer's tags — and the grand total is deduplicated. customer_tags does not require attribute_id.","disabled":true},{"key":"dimension_value","value":"Acme","description":"Value of the dimension to filter by (nullable)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 1001,\n      \"sales_order_number\": \"SO-1001\",\n      \"order_date\": \"2024-06-10\",\n      \"sales_channel\": \"Shopify\",\n      \"product_revenue\": 150,\n      \"revenue\": 162,\n      \"cogs\": 80,\n      \"total_cost\": 95,\n      \"profit\": 67,\n      \"margin\": 41.36,\n      \"flt_values\": {\n        \"1\": 12,\n        \"2\": 3\n      }\n    }\n  ],\n  \"per_page\": 25,\n  \"total\": 145,\n  \"last_page\": 6,\n  \"totals\": {\n    \"revenue\": 23490,\n    \"cogs\": 11500,\n    \"total_cost\": 13775,\n    \"profit\": 9715,\n    \"margin\": 41.36\n  },\n  \"flt_headers\": [\n    {\n      \"id\": 1,\n      \"name\": \"Shipping\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Fulfilment Fee\"\n    }\n  ]\n}"}]},{"name":"Get Order Breakdown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/profitability/drilldown/{salesOrder}/breakdown","host":["{{baseUrl}}"],"path":["api","reporting","profitability","drilldown",":salesOrder","breakdown"],"variable":[{"key":"salesOrder","value":"1001","description":"Sales Order ID"}]},"description":"Returns a full financial breakdown for a single order, including all line items, COGS, allocated costs, and financial line type values.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nEach line's financials and the order summary include indirect_cost_allocated (this order's revenue-weighted share of cost-entry / overhead allocations) and contribution_profit (profit minus that indirect share). These are standalone figures layered on top of profit — profit and total_cost are unchanged by them."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/profitability/drilldown/{salesOrder}/breakdown","host":["{{baseUrl}}"],"path":["api","reporting","profitability","drilldown",":salesOrder","breakdown"],"variable":[{"key":"salesOrder","value":"1001","description":"Sales Order ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"order\": {\n    \"id\": 123,\n    \"sales_order_number\": \"SO-00123\",\n    \"order_date\": \"2026-06-15\",\n    \"sales_channel\": \"Shopify US\"\n  },\n  \"lines\": [\n    {\n      \"id\": 5541,\n      \"product_id\": 88,\n      \"sku\": \"WGT-001\",\n      \"name\": \"Widget A\",\n      \"quantity\": 3,\n      \"financials\": {\n        \"revenue\": 150,\n        \"revenue_allocated\": 0,\n        \"credits\": 0,\n        \"discount\": 0,\n        \"total_revenue\": 150,\n        \"cogs\": 60,\n        \"cost_allocated\": 12,\n        \"cogs_returned\": 0,\n        \"total_cost\": 72,\n        \"profit\": 78,\n        \"profit_margin\": 52,\n        \"indirect_cost_allocated\": 9.5,\n        \"contribution_profit\": 68.5\n      }\n    }\n  ],\n  \"financial_lines\": [],\n  \"summary\": {\n    \"total_revenue\": 150,\n    \"total_cogs\": 60,\n    \"revenue_lines_total\": 0,\n    \"cost_lines_total\": 12,\n    \"total_cost\": 72,\n    \"profit\": 78,\n    \"margin\": 52,\n    \"indirect_cost_allocated\": 9.5,\n    \"contribution_profit\": 68.5,\n    \"contribution_margin\": 45.67\n  }\n}"}]},{"name":"Get Financial Line Types","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/profitability/financial-line-types","host":["{{baseUrl}}"],"path":["api","reporting","profitability","financial-line-types"]},"description":"Returns all financial line types configured for the current tenant. Used to populate the drilldown column selector.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/profitability/financial-line-types","host":["{{baseUrl}}"],"path":["api","reporting","profitability","financial-line-types"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Shipping\",\n      \"type\": \"cost\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Fulfilment Fee\",\n      \"type\": \"cost\"\n    },\n    {\n      \"id\": 3,\n      \"name\": \"Platform Fee\",\n      \"type\": \"cost\"\n    }\n  ]\n}"}]},{"name":"List Indirect Cost Types","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/profitability/indirect-cost-types","host":["{{baseUrl}}"],"path":["api","reporting","profitability","indirect-cost-types"]},"description":"Lists the indirect cost types that have cost-entry allocation activity inside the report window, with allocated totals. Drives the per-type breakdown columns and the indirect cost type filter. Type id 0 represents allocations whose cost entry has no type (Uncategorized).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/profitability/indirect-cost-types","host":["{{baseUrl}}"],"path":["api","reporting","profitability","indirect-cost-types"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 2,\n      \"name\": \"Marketing\",\n      \"total\": 1700\n    },\n    {\n      \"id\": 5,\n      \"name\": \"Software\",\n      \"total\": 600\n    },\n    {\n      \"id\": 0,\n      \"name\": \"Uncategorized\",\n      \"total\": 120\n    }\n  ]\n}"}]},{"name":"Get Indirect Cost Drilldown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/profitability/indirect-cost-drilldown?group_by=brand&dimension_value=Acme Brand","host":["{{baseUrl}}"],"path":["api","reporting","profitability","indirect-cost-drilldown"],"query":[{"key":"group_by","value":"brand","description":"Dimension: brand | sku | supplier | sales_channel | product_type | sub_sales_channel | attribute | customer_tags. Required unless scope=unallocated. customer_tags fans out — an order counts toward each of its customer's tags — and the grand total is deduplicated. customer_tags does not require attribute_id.","disabled":true},{"key":"dimension_value","value":"Acme Brand","description":"The dimension row value to drill into (use 'Non-Product / Unallocated' for the unattributed row). Required unless scope=unallocated.","disabled":true}]},"description":"Itemizes the cost-entry activity behind a Profitability Report figure.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nscope=allocated returns the cost allocations attributed to one dimension row (matching the row's indirect cost amount exactly). scope=unallocated returns cost entries with an unallocated remainder in the window — the amount deducted from Contribution Margin to derive Operating Profit. Draft cost entries are excluded.\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/profitability/indirect-cost-drilldown?group_by=brand&dimension_value=Acme Brand","host":["{{baseUrl}}"],"path":["api","reporting","profitability","indirect-cost-drilldown"],"query":[{"key":"group_by","value":"brand","description":"Dimension: brand | sku | supplier | sales_channel | product_type | sub_sales_channel | attribute | customer_tags. Required unless scope=unallocated. customer_tags fans out — an order counts toward each of its customer's tags — and the grand total is deduplicated. customer_tags does not require attribute_id.","disabled":true},{"key":"dimension_value","value":"Acme Brand","description":"The dimension row value to drill into (use 'Non-Product / Unallocated' for the unattributed row). Required unless scope=unallocated.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"cost_allocation_id\": 991,\n      \"cost_entry_id\": 123,\n      \"description\": \"June Marketing Campaign\",\n      \"cost_entry_type\": {\n        \"id\": 2,\n        \"name\": \"Marketing\"\n      },\n      \"allocatable_type\": \"Product\",\n      \"allocatable_label\": \"WGT-001 — Widget A\",\n      \"allocation_date\": \"2026-06-30\",\n      \"period_start\": \"2026-06-01\",\n      \"period_end\": \"2026-06-30\",\n      \"proration_strategy\": \"revenue_based\",\n      \"amount_in_tenant_currency\": 1200\n    }\n  ],\n  \"current_page\": 1,\n  \"per_page\": 10,\n  \"total\": 3,\n  \"last_page\": 1\n}"}]},{"name":"Get Contribution Margin Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/contribution-margin/summary","host":["{{baseUrl}}"],"path":["api","reporting","contribution-margin","summary"]},"description":"Contribution margin measures profitability after both cost of goods sold and allocated operating costs:\n- Gross Profit = Revenue − COGS\n- Contribution Dollars = Gross Profit − Allocated Costs\n- Contribution Margin % = Contribution Dollars ÷ Revenue × 100\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRevenue and COGS come from per-order-line financial records that are kept current by background financial calculations. Allocated costs come from the cost allocation system (cost entries prorated across products, orders, brands, suppliers, or sales channels).\n\nReturns the account-wide profitability summary as a single `Overall` row: total revenue, COGS, allocated costs, gross profit, contribution dollars, margin percentages, units sold, and order count. Entity filters (product, brand, supplier, sales channel) narrow the underlying sales data; `cost_entry_type_id` narrows the allocated-cost figures.\n\nDate filtering: `date_from` and `date_to` must BOTH be provided for date filtering to apply — if either is omitted the report covers all available history. Sales figures are filtered by order date; allocated costs are matched by their allocation period when one is set (any overlap with the requested range counts), otherwise by their allocation date.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/contribution-margin/summary","host":["{{baseUrl}}"],"path":["api","reporting","contribution-margin","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"dimension\": \"summary\",\n    \"date_from\": \"2026-06-01\",\n    \"date_to\": \"2026-06-30\",\n    \"total_revenue\": 262480,\n    \"total_cogs\": 141739.2,\n    \"total_allocated_costs\": 26248,\n    \"total_gross_profit\": 120740.8,\n    \"total_contribution_dollars\": 94492.8,\n    \"overall_gross_profit_margin\": 46,\n    \"overall_contribution_margin\": 36,\n    \"total_units_sold\": 10499,\n    \"total_order_count\": 8321,\n    \"total_ad_spend\": 0,\n    \"total_attributed_sales\": 0,\n    \"overall_acos\": null,\n    \"overall_tacos\": 0,\n    \"overall_break_even_acos\": 36,\n    \"lines\": [\n      {\n        \"entity_id\": null,\n        \"entity_name\": \"Overall\",\n        \"entity_type\": \"summary\",\n        \"revenue\": 262480,\n        \"cogs\": 141739.2,\n        \"allocated_costs\": 26248,\n        \"gross_profit\": 120740.8,\n        \"contribution_dollars\": 94492.8,\n        \"gross_profit_margin\": 46,\n        \"contribution_margin\": 36,\n        \"units_sold\": 10499,\n        \"order_count\": 8321,\n        \"ad_spend\": 0,\n        \"attributed_sales\": 0,\n        \"acos\": null,\n        \"tacos\": 0,\n        \"break_even_acos\": 36,\n        \"sku\": null,\n        \"brand_name\": null,\n        \"brand_id\": 3,\n        \"supplier_name\": null\n      }\n    ]\n  }\n}"}]},{"name":"Get Contribution Margin by Product","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/contribution-margin/by-product","host":["{{baseUrl}}"],"path":["api","reporting","contribution-margin","by-product"]},"description":"Contribution margin measures profitability after both cost of goods sold and allocated operating costs:\n- Gross Profit = Revenue − COGS\n- Contribution Dollars = Gross Profit − Allocated Costs\n- Contribution Margin % = Contribution Dollars ÷ Revenue × 100\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRevenue and COGS come from per-order-line financial records that are kept current by background financial calculations. Allocated costs come from the cost allocation system (cost entries prorated across products, orders, brands, suppliers, or sales channels).\n\nReturns a profitability breakdown per product. Each row includes the product's SKU, brand, and default supplier alongside revenue, COGS, allocated costs, gross profit, contribution dollars, and margin percentages. Products that received cost allocations but had no sales in the period also appear, with zero revenue and negative contribution — so allocated spend never silently disappears from the report.\n\nWith `include_ad_metrics=true`, each row also carries Amazon Ads spend, ad-attributed sales, ACoS (ad spend ÷ attributed sales), TACoS (ad spend ÷ total revenue), and break-even ACoS (the ACoS at which the product's contribution reaches zero).\n\nDate filtering: `date_from` and `date_to` must BOTH be provided for date filtering to apply — if either is omitted the report covers all available history. Sales figures are filtered by order date; allocated costs are matched by their allocation period when one is set (any overlap with the requested range counts), otherwise by their allocation date.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/contribution-margin/by-product","host":["{{baseUrl}}"],"path":["api","reporting","contribution-margin","by-product"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"dimension\": \"product\",\n    \"date_from\": \"2026-06-01\",\n    \"date_to\": \"2026-06-30\",\n    \"total_revenue\": 78375,\n    \"total_cogs\": 38281.25,\n    \"total_allocated_costs\": 8682.5,\n    \"total_gross_profit\": 40093.75,\n    \"total_contribution_dollars\": 31411.25,\n    \"overall_gross_profit_margin\": 51.16,\n    \"overall_contribution_margin\": 40.08,\n    \"total_units_sold\": 2532,\n    \"total_order_count\": 2302,\n    \"total_ad_spend\": 3990,\n    \"total_attributed_sales\": 31250,\n    \"overall_acos\": 12.77,\n    \"overall_tacos\": 5.09,\n    \"overall_break_even_acos\": 45.17,\n    \"lines\": [\n      {\n        \"entity_id\": 812,\n        \"entity_name\": \"5m LED Light Strip - RGB\",\n        \"entity_type\": \"product\",\n        \"revenue\": 48250,\n        \"cogs\": 21712.5,\n        \"allocated_costs\": 5430,\n        \"gross_profit\": 26537.5,\n        \"contribution_dollars\": 21107.5,\n        \"gross_profit_margin\": 55,\n        \"contribution_margin\": 43.75,\n        \"units_sold\": 1930,\n        \"order_count\": 1712,\n        \"ad_spend\": 2410,\n        \"attributed_sales\": 19850,\n        \"acos\": 12.14,\n        \"tacos\": 4.99,\n        \"break_even_acos\": 48.74,\n        \"sku\": \"LED-STRIP-5M-RGB\",\n        \"brand_name\": \"Lumina\",\n        \"brand_id\": 3,\n        \"supplier_name\": \"Shenzhen Lumina Electronics\"\n      },\n      {\n        \"entity_id\": 654,\n        \"entity_name\": \"Desk Glow Lamp - Walnut\",\n        \"entity_type\": \"product\",\n        \"revenue\": 30125,\n        \"cogs\": 16568.75,\n        \"allocated_costs\": 3012.5,\n        \"gross_profit\": 13556.25,\n        \"contribution_dollars\": 10543.75,\n        \"gross_profit_margin\": 45,\n        \"contribution_margin\": 35,\n        \"units_sold\": 602,\n        \"order_count\": 590,\n        \"ad_spend\": 1520,\n        \"attributed_sales\": 11400,\n        \"acos\": 13.33,\n        \"tacos\": 5.05,\n        \"break_even_acos\": 40.05,\n        \"sku\": \"GLOW-DESK-WAL\",\n        \"brand_name\": \"NovaGlow\",\n        \"brand_id\": 3,\n        \"supplier_name\": \"Pacific Coast Distributors\"\n      },\n      {\n        \"entity_id\": 977,\n        \"entity_name\": \"Solar Path Light 4-Pack\",\n        \"entity_type\": \"product\",\n        \"revenue\": 0,\n        \"cogs\": 0,\n        \"allocated_costs\": 240,\n        \"gross_profit\": 0,\n        \"contribution_dollars\": -240,\n        \"gross_profit_margin\": 0,\n        \"contribution_margin\": 0,\n        \"units_sold\": 0,\n        \"order_count\": 0,\n        \"ad_spend\": 60,\n        \"attributed_sales\": 0,\n        \"acos\": null,\n        \"tacos\": null,\n        \"break_even_acos\": null,\n        \"sku\": \"SOLAR-PATH-4PK\",\n        \"brand_name\": \"Lumina\",\n        \"brand_id\": 3,\n        \"supplier_name\": \"Shenzhen Lumina Electronics\"\n      }\n    ]\n  }\n}"}]},{"name":"Get Contribution Margin by Brand","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/contribution-margin/by-brand","host":["{{baseUrl}}"],"path":["api","reporting","contribution-margin","by-brand"]},"description":"Contribution margin measures profitability after both cost of goods sold and allocated operating costs:\n- Gross Profit = Revenue − COGS\n- Contribution Dollars = Gross Profit − Allocated Costs\n- Contribution Margin % = Contribution Dollars ÷ Revenue × 100\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRevenue and COGS come from per-order-line financial records that are kept current by background financial calculations. Allocated costs come from the cost allocation system (cost entries prorated across products, orders, brands, suppliers, or sales channels).\n\nReturns a profitability breakdown per brand. Allocated costs include both costs allocated directly to a brand and product-level allocations rolled up to the product's brand. Sales of products without a brand are grouped under a `No Brand` row with `entity_id: 0`. Brands with cost allocations but no sales in the period also appear with zero revenue. `include_ad_metrics=true` adds Amazon Ads spend and attributed sales rolled up to brand level.\n\nDate filtering: `date_from` and `date_to` must BOTH be provided for date filtering to apply — if either is omitted the report covers all available history. Sales figures are filtered by order date; allocated costs are matched by their allocation period when one is set (any overlap with the requested range counts), otherwise by their allocation date.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/contribution-margin/by-brand","host":["{{baseUrl}}"],"path":["api","reporting","contribution-margin","by-brand"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"dimension\": \"brand\",\n    \"date_from\": \"2026-06-01\",\n    \"date_to\": \"2026-06-30\",\n    \"total_revenue\": 168120,\n    \"total_cogs\": 85277.2,\n    \"total_allocated_costs\": 16288,\n    \"total_gross_profit\": 82842.8,\n    \"total_contribution_dollars\": 66554.8,\n    \"overall_gross_profit_margin\": 49.28,\n    \"overall_contribution_margin\": 39.59,\n    \"total_units_sold\": 5619,\n    \"total_order_count\": 5003,\n    \"total_ad_spend\": 0,\n    \"total_attributed_sales\": 0,\n    \"overall_acos\": null,\n    \"overall_tacos\": 0,\n    \"overall_break_even_acos\": 39.59,\n    \"lines\": [\n      {\n        \"entity_id\": 14,\n        \"entity_name\": \"Lumina\",\n        \"entity_type\": \"brand\",\n        \"revenue\": 96410,\n        \"cogs\": 45312.7,\n        \"allocated_costs\": 9641,\n        \"gross_profit\": 51097.3,\n        \"contribution_dollars\": 41456.3,\n        \"gross_profit_margin\": 53,\n        \"contribution_margin\": 43,\n        \"units_sold\": 3960,\n        \"order_count\": 3421,\n        \"ad_spend\": 0,\n        \"attributed_sales\": 0,\n        \"acos\": null,\n        \"tacos\": 0,\n        \"break_even_acos\": 43,\n        \"sku\": null,\n        \"brand_name\": null,\n        \"brand_id\": 3,\n        \"supplier_name\": null\n      },\n      {\n        \"entity_id\": 22,\n        \"entity_name\": \"NovaGlow\",\n        \"entity_type\": \"brand\",\n        \"revenue\": 61230,\n        \"cogs\": 33676.5,\n        \"allocated_costs\": 6123,\n        \"gross_profit\": 27553.5,\n        \"contribution_dollars\": 21430.5,\n        \"gross_profit_margin\": 45,\n        \"contribution_margin\": 35,\n        \"units_sold\": 1240,\n        \"order_count\": 1180,\n        \"ad_spend\": 0,\n        \"attributed_sales\": 0,\n        \"acos\": null,\n        \"tacos\": 0,\n        \"break_even_acos\": 35,\n        \"sku\": null,\n        \"brand_name\": null,\n        \"brand_id\": 3,\n        \"supplier_name\": null\n      },\n      {\n        \"entity_id\": 0,\n        \"entity_name\": \"No Brand\",\n        \"entity_type\": \"brand\",\n        \"revenue\": 10480,\n        \"cogs\": 6288,\n        \"allocated_costs\": 524,\n        \"gross_profit\": 4192,\n        \"contribution_dollars\": 3668,\n        \"gross_profit_margin\": 40,\n        \"contribution_margin\": 35,\n        \"units_sold\": 419,\n        \"order_count\": 402,\n        \"ad_spend\": 0,\n        \"attributed_sales\": 0,\n        \"acos\": null,\n        \"tacos\": 0,\n        \"break_even_acos\": 35,\n        \"sku\": null,\n        \"brand_name\": null,\n        \"brand_id\": 3,\n        \"supplier_name\": null\n      }\n    ]\n  }\n}"}]},{"name":"Get Contribution Margin by Supplier","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/contribution-margin/by-supplier","host":["{{baseUrl}}"],"path":["api","reporting","contribution-margin","by-supplier"]},"description":"Contribution margin measures profitability after both cost of goods sold and allocated operating costs:\n- Gross Profit = Revenue − COGS\n- Contribution Dollars = Gross Profit − Allocated Costs\n- Contribution Margin % = Contribution Dollars ÷ Revenue × 100\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRevenue and COGS come from per-order-line financial records that are kept current by background financial calculations. Allocated costs come from the cost allocation system (cost entries prorated across products, orders, brands, suppliers, or sales channels).\n\nReturns a profitability breakdown per supplier. Products are attributed to their default supplier; sales of products without a default supplier are grouped under a `No Supplier` row with `entity_id: 0`. Allocated costs include both costs allocated directly to a supplier and product-level allocations rolled up via the product's default supplier. Suppliers with cost allocations but no sales in the period also appear with zero revenue. `include_ad_metrics=true` adds Amazon Ads spend and attributed sales rolled up to supplier level.\n\nDate filtering: `date_from` and `date_to` must BOTH be provided for date filtering to apply — if either is omitted the report covers all available history. Sales figures are filtered by order date; allocated costs are matched by their allocation period when one is set (any overlap with the requested range counts), otherwise by their allocation date.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/contribution-margin/by-supplier","host":["{{baseUrl}}"],"path":["api","reporting","contribution-margin","by-supplier"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"dimension\": \"supplier\",\n    \"date_from\": \"2026-06-01\",\n    \"date_to\": \"2026-06-30\",\n    \"total_revenue\": 167370,\n    \"total_cogs\": 85444.7,\n    \"total_allocated_costs\": 16274.5,\n    \"total_gross_profit\": 81925.3,\n    \"total_contribution_dollars\": 65650.8,\n    \"overall_gross_profit_margin\": 48.95,\n    \"overall_contribution_margin\": 39.22,\n    \"total_units_sold\": 5695,\n    \"total_order_count\": 5110,\n    \"total_ad_spend\": 0,\n    \"total_attributed_sales\": 0,\n    \"overall_acos\": null,\n    \"overall_tacos\": 0,\n    \"overall_break_even_acos\": 39.22,\n    \"lines\": [\n      {\n        \"entity_id\": 31,\n        \"entity_name\": \"Shenzhen Lumina Electronics\",\n        \"entity_type\": \"supplier\",\n        \"revenue\": 102340,\n        \"cogs\": 49123.2,\n        \"allocated_costs\": 10234,\n        \"gross_profit\": 53216.8,\n        \"contribution_dollars\": 42982.8,\n        \"gross_profit_margin\": 52,\n        \"contribution_margin\": 42,\n        \"units_sold\": 4210,\n        \"order_count\": 3688,\n        \"ad_spend\": 0,\n        \"attributed_sales\": 0,\n        \"acos\": null,\n        \"tacos\": 0,\n        \"break_even_acos\": 42,\n        \"sku\": null,\n        \"brand_name\": null,\n        \"brand_id\": 3,\n        \"supplier_name\": null\n      },\n      {\n        \"entity_id\": 45,\n        \"entity_name\": \"Pacific Coast Distributors\",\n        \"entity_type\": \"supplier\",\n        \"revenue\": 55780,\n        \"cogs\": 30679,\n        \"allocated_costs\": 5578,\n        \"gross_profit\": 25101,\n        \"contribution_dollars\": 19523,\n        \"gross_profit_margin\": 45,\n        \"contribution_margin\": 35,\n        \"units_sold\": 1115,\n        \"order_count\": 1071,\n        \"ad_spend\": 0,\n        \"attributed_sales\": 0,\n        \"acos\": null,\n        \"tacos\": 0,\n        \"break_even_acos\": 35,\n        \"sku\": null,\n        \"brand_name\": null,\n        \"brand_id\": 3,\n        \"supplier_name\": null\n      },\n      {\n        \"entity_id\": 0,\n        \"entity_name\": \"No Supplier\",\n        \"entity_type\": \"supplier\",\n        \"revenue\": 9250,\n        \"cogs\": 5642.5,\n        \"allocated_costs\": 462.5,\n        \"gross_profit\": 3607.5,\n        \"contribution_dollars\": 3145,\n        \"gross_profit_margin\": 39,\n        \"contribution_margin\": 34,\n        \"units_sold\": 370,\n        \"order_count\": 351,\n        \"ad_spend\": 0,\n        \"attributed_sales\": 0,\n        \"acos\": null,\n        \"tacos\": 0,\n        \"break_even_acos\": 34,\n        \"sku\": null,\n        \"brand_name\": null,\n        \"brand_id\": 3,\n        \"supplier_name\": null\n      }\n    ]\n  }\n}"}]},{"name":"Get Contribution Margin by Sales Channel","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/contribution-margin/by-sales-channel","host":["{{baseUrl}}"],"path":["api","reporting","contribution-margin","by-sales-channel"]},"description":"Contribution margin measures profitability after both cost of goods sold and allocated operating costs:\n- Gross Profit = Revenue − COGS\n- Contribution Dollars = Gross Profit − Allocated Costs\n- Contribution Margin % = Contribution Dollars ÷ Revenue × 100\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRevenue and COGS come from per-order-line financial records that are kept current by background financial calculations. Allocated costs come from the cost allocation system (cost entries prorated across products, orders, brands, suppliers, or sales channels).\n\nReturns a profitability breakdown per sales channel (e.g. Amazon, Shopify, eBay). The row name is the channel's connected store/integration name. Channels with cost allocations but no sales in the period also appear with zero revenue.\n\nNote: ad metrics (ad spend, attributed sales, ACoS/TACoS) are not computed for the sales-channel dimension — those fields are always zero/null here. Use the by-product or by-brand reports with `include_ad_metrics=true` for advertising performance.\n\nDate filtering: `date_from` and `date_to` must BOTH be provided for date filtering to apply — if either is omitted the report covers all available history. Sales figures are filtered by order date; allocated costs are matched by their allocation period when one is set (any overlap with the requested range counts), otherwise by their allocation date.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/contribution-margin/by-sales-channel","host":["{{baseUrl}}"],"path":["api","reporting","contribution-margin","by-sales-channel"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"dimension\": \"sales_channel\",\n    \"date_from\": \"2026-06-01\",\n    \"date_to\": \"2026-06-30\",\n    \"total_revenue\": 262480,\n    \"total_cogs\": 142010.8,\n    \"total_allocated_costs\": 26248,\n    \"total_gross_profit\": 120469.2,\n    \"total_contribution_dollars\": 94221.2,\n    \"overall_gross_profit_margin\": 45.9,\n    \"overall_contribution_margin\": 35.9,\n    \"total_units_sold\": 10499,\n    \"total_order_count\": 8321,\n    \"total_ad_spend\": 0,\n    \"total_attributed_sales\": 0,\n    \"overall_acos\": null,\n    \"overall_tacos\": 0,\n    \"overall_break_even_acos\": 35.9,\n    \"lines\": [\n      {\n        \"entity_id\": 3,\n        \"entity_name\": \"Amazon US\",\n        \"entity_type\": \"sales_channel\",\n        \"revenue\": 152480,\n        \"cogs\": 83864,\n        \"allocated_costs\": 15248,\n        \"gross_profit\": 68616,\n        \"contribution_dollars\": 53368,\n        \"gross_profit_margin\": 45,\n        \"contribution_margin\": 35,\n        \"units_sold\": 6099,\n        \"order_count\": 4881,\n        \"ad_spend\": 0,\n        \"attributed_sales\": 0,\n        \"acos\": null,\n        \"tacos\": 0,\n        \"break_even_acos\": 35,\n        \"sku\": null,\n        \"brand_name\": null,\n        \"brand_id\": 3,\n        \"supplier_name\": null\n      },\n      {\n        \"entity_id\": 5,\n        \"entity_name\": \"Shopify\",\n        \"entity_type\": \"sales_channel\",\n        \"revenue\": 78440,\n        \"cogs\": 40788.8,\n        \"allocated_costs\": 7844,\n        \"gross_profit\": 37651.2,\n        \"contribution_dollars\": 29807.2,\n        \"gross_profit_margin\": 48,\n        \"contribution_margin\": 38,\n        \"units_sold\": 3138,\n        \"order_count\": 2612,\n        \"ad_spend\": 0,\n        \"attributed_sales\": 0,\n        \"acos\": null,\n        \"tacos\": 0,\n        \"break_even_acos\": 38,\n        \"sku\": null,\n        \"brand_name\": null,\n        \"brand_id\": 3,\n        \"supplier_name\": null\n      },\n      {\n        \"entity_id\": 8,\n        \"entity_name\": \"eBay\",\n        \"entity_type\": \"sales_channel\",\n        \"revenue\": 31560,\n        \"cogs\": 17358,\n        \"allocated_costs\": 3156,\n        \"gross_profit\": 14202,\n        \"contribution_dollars\": 11046,\n        \"gross_profit_margin\": 45,\n        \"contribution_margin\": 35,\n        \"units_sold\": 1262,\n        \"order_count\": 828,\n        \"ad_spend\": 0,\n        \"attributed_sales\": 0,\n        \"acos\": null,\n        \"tacos\": 0,\n        \"break_even_acos\": 35,\n        \"sku\": null,\n        \"brand_name\": null,\n        \"brand_id\": 3,\n        \"supplier_name\": null\n      }\n    ]\n  }\n}"}]},{"name":"Get Top Products by Contribution Margin","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/contribution-margin/top-products","host":["{{baseUrl}}"],"path":["api","reporting","contribution-margin","top-products"]},"description":"Contribution margin measures profitability after both cost of goods sold and allocated operating costs:\n- Gross Profit = Revenue − COGS\n- Contribution Dollars = Gross Profit − Allocated Costs\n- Contribution Margin % = Contribution Dollars ÷ Revenue × 100\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRevenue and COGS come from per-order-line financial records that are kept current by background financial calculations. Allocated costs come from the cost allocation system (cost entries prorated across products, orders, brands, suppliers, or sales channels).\n\nReturns the products ranked by contribution dollars (gross profit minus allocated costs). Use `order=desc` (default) for your biggest profit contributors, or `order=asc` to surface the weakest performers. Each product row has the same shape as the by-product report lines, including SKU, brand, and default supplier.\n\nRanking note: candidates are drawn from the top revenue-generating products (twice the requested limit) before being re-ranked by contribution dollars, so a low-revenue product with very high margin can occasionally fall outside the candidate pool.\n\nDate filtering: `date_from` and `date_to` must BOTH be provided for date filtering to apply — if either is omitted the report covers all available history. Sales figures are filtered by order date; allocated costs are matched by their allocation period when one is set (any overlap with the requested range counts), otherwise by their allocation date.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/contribution-margin/top-products","host":["{{baseUrl}}"],"path":["api","reporting","contribution-margin","top-products"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"date_from\": \"2026-06-01\",\n    \"date_to\": \"2026-06-30\",\n    \"products\": [\n      {\n        \"entity_id\": 812,\n        \"entity_name\": \"5m LED Light Strip - RGB\",\n        \"entity_type\": \"product\",\n        \"revenue\": 48250,\n        \"cogs\": 21712.5,\n        \"allocated_costs\": 5430,\n        \"gross_profit\": 26537.5,\n        \"contribution_dollars\": 21107.5,\n        \"gross_profit_margin\": 55,\n        \"contribution_margin\": 43.75,\n        \"units_sold\": 1930,\n        \"order_count\": 1712,\n        \"ad_spend\": 0,\n        \"attributed_sales\": 0,\n        \"acos\": null,\n        \"tacos\": 0,\n        \"break_even_acos\": 43.75,\n        \"sku\": \"LED-STRIP-5M-RGB\",\n        \"brand_name\": \"Lumina\",\n        \"brand_id\": 3,\n        \"supplier_name\": \"Shenzhen Lumina Electronics\"\n      },\n      {\n        \"entity_id\": 654,\n        \"entity_name\": \"Desk Glow Lamp - Walnut\",\n        \"entity_type\": \"product\",\n        \"revenue\": 30125,\n        \"cogs\": 16568.75,\n        \"allocated_costs\": 3012.5,\n        \"gross_profit\": 13556.25,\n        \"contribution_dollars\": 10543.75,\n        \"gross_profit_margin\": 45,\n        \"contribution_margin\": 35,\n        \"units_sold\": 602,\n        \"order_count\": 590,\n        \"ad_spend\": 0,\n        \"attributed_sales\": 0,\n        \"acos\": null,\n        \"tacos\": 0,\n        \"break_even_acos\": 35,\n        \"sku\": \"GLOW-DESK-WAL\",\n        \"brand_name\": \"NovaGlow\",\n        \"brand_id\": 3,\n        \"supplier_name\": \"Pacific Coast Distributors\"\n      },\n      {\n        \"entity_id\": 721,\n        \"entity_name\": \"Smart Plug 2-Pack\",\n        \"entity_type\": \"product\",\n        \"revenue\": 18940,\n        \"cogs\": 9470,\n        \"allocated_costs\": 1894,\n        \"gross_profit\": 9470,\n        \"contribution_dollars\": 7576,\n        \"gross_profit_margin\": 50,\n        \"contribution_margin\": 40,\n        \"units_sold\": 947,\n        \"order_count\": 902,\n        \"ad_spend\": 0,\n        \"attributed_sales\": 0,\n        \"acos\": null,\n        \"tacos\": 0,\n        \"break_even_acos\": 40,\n        \"sku\": \"SMART-PLUG-2PK\",\n        \"brand_name\": \"Lumina\",\n        \"brand_id\": 3,\n        \"supplier_name\": \"Shenzhen Lumina Electronics\"\n      }\n    ]\n  }\n}"}]},{"name":"List Unprofitable Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/contribution-margin/unprofitable-products","host":["{{baseUrl}}"],"path":["api","reporting","contribution-margin","unprofitable-products"]},"description":"Contribution margin measures profitability after both cost of goods sold and allocated operating costs:\n- Gross Profit = Revenue − COGS\n- Contribution Dollars = Gross Profit − Allocated Costs\n- Contribution Margin % = Contribution Dollars ÷ Revenue × 100\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRevenue and COGS come from per-order-line financial records that are kept current by background financial calculations. Allocated costs come from the cost allocation system (cost entries prorated across products, orders, brands, suppliers, or sales channels).\n\nReturns products whose contribution dollars are negative for the period — items where COGS plus allocated costs exceed revenue. This includes products with cost allocations but no sales at all (zero revenue, negative contribution). Each product row has the same shape as the by-product report lines; `count` is the number of unprofitable products returned.\n\nNote: `limit` bounds the revenue-ranked product pool that is scanned, not the number of unprofitable rows returned — with a small limit, unprofitable products outside the top-revenue pool may be missed.\n\nDate filtering: `date_from` and `date_to` must BOTH be provided for date filtering to apply — if either is omitted the report covers all available history. Sales figures are filtered by order date; allocated costs are matched by their allocation period when one is set (any overlap with the requested range counts), otherwise by their allocation date.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/contribution-margin/unprofitable-products","host":["{{baseUrl}}"],"path":["api","reporting","contribution-margin","unprofitable-products"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"date_from\": \"2026-06-01\",\n    \"date_to\": \"2026-06-30\",\n    \"count\": 2,\n    \"products\": [\n      {\n        \"entity_id\": 903,\n        \"entity_name\": \"Bluetooth Shower Speaker\",\n        \"entity_type\": \"product\",\n        \"revenue\": 4210,\n        \"cogs\": 3960,\n        \"allocated_costs\": 890,\n        \"gross_profit\": 250,\n        \"contribution_dollars\": -640,\n        \"gross_profit_margin\": 5.94,\n        \"contribution_margin\": -15.2,\n        \"units_sold\": 421,\n        \"order_count\": 415,\n        \"ad_spend\": 0,\n        \"attributed_sales\": 0,\n        \"acos\": null,\n        \"tacos\": 0,\n        \"break_even_acos\": -15.2,\n        \"sku\": \"BT-SHOWER-SPKR\",\n        \"brand_name\": \"NovaGlow\",\n        \"brand_id\": 3,\n        \"supplier_name\": \"Pacific Coast Distributors\"\n      },\n      {\n        \"entity_id\": 977,\n        \"entity_name\": \"Solar Path Light 4-Pack\",\n        \"entity_type\": \"product\",\n        \"revenue\": 0,\n        \"cogs\": 0,\n        \"allocated_costs\": 240,\n        \"gross_profit\": 0,\n        \"contribution_dollars\": -240,\n        \"gross_profit_margin\": 0,\n        \"contribution_margin\": 0,\n        \"units_sold\": 0,\n        \"order_count\": 0,\n        \"ad_spend\": 0,\n        \"attributed_sales\": 0,\n        \"acos\": null,\n        \"tacos\": null,\n        \"break_even_acos\": null,\n        \"sku\": \"SOLAR-PATH-4PK\",\n        \"brand_name\": \"Lumina\",\n        \"brand_id\": 3,\n        \"supplier_name\": \"Shenzhen Lumina Electronics\"\n      }\n    ]\n  }\n}"}]},{"name":"Get Contribution Margin Cost Analysis","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/contribution-margin/cost-analysis","host":["{{baseUrl}}"],"path":["api","reporting","contribution-margin","cost-analysis"]},"description":"Contribution margin measures profitability after both cost of goods sold and allocated operating costs:\n- Gross Profit = Revenue − COGS\n- Contribution Dollars = Gross Profit − Allocated Costs\n- Contribution Margin % = Contribution Dollars ÷ Revenue × 100\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRevenue and COGS come from per-order-line financial records that are kept current by background financial calculations. Allocated costs come from the cost allocation system (cost entries prorated across products, orders, brands, suppliers, or sales channels).\n\nReturns an overview of all allocated costs feeding the contribution margin reports: the total amount allocated, how many individual allocations and distinct cost entries produced it, and three breakdowns —\n- `by_allocatable_type`: what kind of entity the costs were allocated to (products, orders, brands, suppliers, sales channels). `full_type` is the internal type identifier; `type` is its short name.\n- `by_cost_entry_type`: the user-defined cost categories (e.g. Amazon Advertising, Warehouse Rent). Allocations whose cost entry has no type are grouped under `Uncategorized`.\n- `by_proration_strategy`: how each allocation was split (`revenue_based`, `cost_based`, `weight_based`, `volume_based`, `quantity_based`, `specific_line`, or `manual`).\n\nAmounts are in the account's base currency. Breakdown rows are sorted by total amount, descending.\n\nDate filtering: `date_from` and `date_to` must BOTH be provided for date filtering to apply — if either is omitted the report covers all available history. Sales figures are filtered by order date; allocated costs are matched by their allocation period when one is set (any overlap with the requested range counts), otherwise by their allocation date.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/contribution-margin/cost-analysis","host":["{{baseUrl}}"],"path":["api","reporting","contribution-margin","cost-analysis"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"date_from\": \"2026-06-01\",\n    \"date_to\": \"2026-06-30\",\n    \"summary\": {\n      \"total_allocated\": 26248,\n      \"allocation_count\": 1842,\n      \"cost_entry_count\": 96\n    },\n    \"by_allocatable_type\": [\n      {\n        \"type\": \"Product\",\n        \"full_type\": \"App\\\\Models\\\\Product\",\n        \"allocation_count\": 1520,\n        \"total_amount\": 19870.5\n      },\n      {\n        \"type\": \"SalesChannel\",\n        \"full_type\": \"App\\\\Models\\\\SalesChannel\",\n        \"allocation_count\": 214,\n        \"total_amount\": 4562.25\n      },\n      {\n        \"type\": \"ProductBrand\",\n        \"full_type\": \"App\\\\Models\\\\ProductBrand\",\n        \"allocation_count\": 108,\n        \"total_amount\": 1815.25\n      }\n    ],\n    \"by_cost_entry_type\": [\n      {\n        \"type_id\": 4,\n        \"type_name\": \"Amazon Advertising\",\n        \"allocation_count\": 1180,\n        \"total_amount\": 14210\n      },\n      {\n        \"type_id\": 7,\n        \"type_name\": \"Warehouse Rent\",\n        \"allocation_count\": 402,\n        \"total_amount\": 8450\n      },\n      {\n        \"type_id\": null,\n        \"type_name\": \"Uncategorized\",\n        \"allocation_count\": 260,\n        \"total_amount\": 3588\n      }\n    ],\n    \"by_proration_strategy\": [\n      {\n        \"strategy\": \"revenue_based\",\n        \"allocation_count\": 1102,\n        \"total_amount\": 15980.75\n      },\n      {\n        \"strategy\": \"quantity_based\",\n        \"allocation_count\": 526,\n        \"total_amount\": 7412.25\n      },\n      {\n        \"strategy\": \"manual\",\n        \"allocation_count\": 214,\n        \"total_amount\": 2855\n      }\n    ]\n  }\n}"}]},{"name":"Get Sales Tax Liability","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/liability?date_from=2026-01-01&date_to=2026-03-31&group_by=state","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","liability"],"query":[{"key":"date_from","value":"2026-01-01","description":"Required. Start of the reporting period (date or datetime; treated as app timezone).","disabled":true},{"key":"date_to","value":"2026-03-31","description":"Required. End of the reporting period. Must be on or after date_from.","disabled":true},{"key":"group_by","value":"state","description":"Optional. Aggregation lens: state (one row per destination region) | in_state_out_of_state (collapses to two rows: In-State home jurisdiction vs Out-of-State) | responsibility (two rows: Seller-collected vs Marketplace facilitator) | state_responsibility (one row per state, each carrying a seller_net_tax / marketplace_net_tax split). Defaults to state.","disabled":true}]},"description":"Sales Tax Liability Report: tax collected and sales aggregated by destination jurisdiction (country + region), with a taxable / non-taxable split and Net Tax (tax collected minus refunded tax from credits). Amounts are converted to the tenant base currency.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope rw:reports).\n\nRequired parameters:\n- date_from: report period start\n- date_to: report period end (>= date_from)\n\nOptional:\n- channel_ids[]: restrict to specific sales channels\n- country_code: destination country ISO code (max 3)\n- order_statuses[]: draft | reserved | open | closed (default open + closed)\n- province_code: destination region/state code (max 10)\n- group_by: state | in_state_out_of_state | responsibility | state_responsibility (default state)\n\nThe `responsibility` and `state_responsibility` lenses (Phase 2) split tax by who collects & remits it: the seller, or a marketplace facilitator (Amazon/eBay/Walmart/Temu/Faire by default). Rows then also carry tax_collection_responsibility, is_default_classification, seller_net_tax and marketplace_net_tax. The `is_home_state` flag marks the tenant's resolved home nexus jurisdiction. Rows are sorted by net_tax descending."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/liability?date_from=2026-01-01&date_to=2026-03-31&group_by=state","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","liability"],"query":[{"key":"date_from","value":"2026-01-01","description":"Required. Start of the reporting period (date or datetime; treated as app timezone).","disabled":true},{"key":"date_to","value":"2026-03-31","description":"Required. End of the reporting period. Must be on or after date_from.","disabled":true},{"key":"group_by","value":"state","description":"Optional. Aggregation lens: state (one row per destination region) | in_state_out_of_state (collapses to two rows: In-State home jurisdiction vs Out-of-State) | responsibility (two rows: Seller-collected vs Marketplace facilitator) | state_responsibility (one row per state, each carrying a seller_net_tax / marketplace_net_tax split). Defaults to state.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"country_code\": \"US\",\n      \"province_code\": \"CA\",\n      \"province\": \"California\",\n      \"is_home_state\": true,\n      \"order_count\": 142,\n      \"gross_sales\": \"48230.50\",\n      \"taxable_sales\": \"41200.00\",\n      \"non_taxable_sales\": \"7030.50\",\n      \"tax_collected\": \"3502.00\",\n      \"refunded_tax\": \"120.50\",\n      \"net_tax\": \"3381.50\"\n    },\n    {\n      \"country_code\": \"US\",\n      \"province_code\": \"NY\",\n      \"province\": \"New York\",\n      \"is_home_state\": false,\n      \"order_count\": 88,\n      \"gross_sales\": \"26110.00\",\n      \"taxable_sales\": \"24000.00\",\n      \"non_taxable_sales\": \"2110.00\",\n      \"tax_collected\": \"2040.00\",\n      \"refunded_tax\": \"0.00\",\n      \"net_tax\": \"2040.00\"\n    }\n  ],\n  \"totals\": {\n    \"order_count\": 230,\n    \"gross_sales\": \"74340.50\",\n    \"taxable_sales\": \"65200.00\",\n    \"non_taxable_sales\": \"9140.50\",\n    \"tax_collected\": \"5542.00\",\n    \"refunded_tax\": \"120.50\",\n    \"net_tax\": \"5421.50\"\n  },\n  \"currency\": \"USD\",\n  \"period\": {\n    \"date_from\": \"2026-01-01\",\n    \"date_to\": \"2026-03-31\"\n  }\n}"}]},{"name":"Get Sales Tax Transactions (Drill-Down)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/liability/transactions?date_from=2026-01-01&date_to=2026-03-31&page=1&per_page=15","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","liability","transactions"],"query":[{"key":"date_from","value":"2026-01-01","description":"Required. Start of the reporting period (date or datetime; treated as app timezone).","disabled":true},{"key":"date_to","value":"2026-03-31","description":"Required. End of the reporting period. Must be on or after date_from.","disabled":true},{"key":"page","value":"1","description":"Optional. Page number (>= 1). Defaults to 1.","disabled":true},{"key":"per_page","value":"15","description":"Optional. Items per page (1-200). Defaults to 15.","disabled":true}]},"description":"Paginated transaction drill-down behind a single jurisdiction row of the liability report. Returns the underlying orders and credits (credit amounts are negative) that compose the aggregated row, filtered by the same parameters plus the selected province_code / country_code.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope rw:reports).\n\nRequired parameters:\n- date_from, date_to\n\nOptional: channel_ids[], country_code, order_statuses[], province_code, page, per_page (1-200).\n\nResults are a UNION of sales orders and sales credits ordered by transaction date descending, returned in the standard Laravel pagination envelope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/liability/transactions?date_from=2026-01-01&date_to=2026-03-31&page=1&per_page=15","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","liability","transactions"],"query":[{"key":"date_from","value":"2026-01-01","description":"Required. Start of the reporting period (date or datetime; treated as app timezone).","disabled":true},{"key":"date_to","value":"2026-03-31","description":"Required. End of the reporting period. Must be on or after date_from.","disabled":true},{"key":"page","value":"1","description":"Optional. Page number (>= 1). Defaults to 1.","disabled":true},{"key":"per_page","value":"15","description":"Optional. Items per page (1-200). Defaults to 15.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"document_type\": \"order\",\n      \"order_id\": 33421,\n      \"number\": \"SO-10044\",\n      \"date\": \"2026-03-28\",\n      \"channel\": \"Shopify US\",\n      \"customer_name\": \"Jane Doe\",\n      \"province_code\": \"CA\",\n      \"taxable_amount\": \"310.00\",\n      \"tax\": \"26.35\"\n    },\n    {\n      \"document_type\": \"credit\",\n      \"order_id\": 33380,\n      \"number\": \"CN-2051\",\n      \"date\": \"2026-03-20\",\n      \"channel\": \"Shopify US\",\n      \"customer_name\": \"John Smith\",\n      \"province_code\": \"CA\",\n      \"taxable_amount\": \"-45.00\",\n      \"tax\": \"-3.83\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 8,\n  \"per_page\": 15,\n  \"total\": 118,\n  \"from\": 1,\n  \"to\": 15,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/reporting/sales-tax/liability/transactions?page=1\",\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/reporting/sales-tax/liability/transactions?page=8\",\n  \"next_page_url\": \"{{protocol}}{{domain}}/api/reporting/sales-tax/liability/transactions?page=2\",\n  \"prev_page_url\": null,\n  \"path\": \"{{protocol}}{{domain}}/api/reporting/sales-tax/liability/transactions\"\n}"}]},{"name":"Export Sales Tax Liability","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/liability/export?date_from=2026-01-01&date_to=2026-03-31&group_by=state&format=csv","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","liability","export"],"query":[{"key":"date_from","value":"2026-01-01","description":"Required. Start of the reporting period (date or datetime; treated as app timezone).","disabled":true},{"key":"date_to","value":"2026-03-31","description":"Required. End of the reporting period. Must be on or after date_from.","disabled":true},{"key":"group_by","value":"state","description":"Optional. state | in_state_out_of_state (default state).","disabled":true},{"key":"format","value":"csv","description":"Optional. Export format: csv | xlsx | pdf. Defaults to csv.","disabled":true}]},"description":"Exports the Sales Tax Liability Report as a downloadable file (CSV, XLSX, or PDF). Accepts the same filter parameters as the liability report. The response is a binary file download (Content-Type varies: text/csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, or application/pdf), not JSON. Columns: Country, Region, Region Code, In-State, Orders, Gross Sales, Taxable Sales, Non-Taxable Sales, Tax Collected, Refunded Tax, Net Tax, with a trailing TOTAL row.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope rw:reports)."}},{"name":"Get Channel Tax Responsibility","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/channel-responsibility","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","channel-responsibility"]},"description":"Lists every sales channel (integration instance) with its resolved tax-collection responsibility for the Sales Tax Liability Report (Phase 2). Each channel is classified as `seller` (Seller-collected) or `marketplace_facilitator` (Marketplace facilitator).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe `source` field indicates how the classification was resolved:\n- override: a manual per-channel override was saved (is_default = false)\n- default: resolved from the built-in marketplace-facilitator map (Amazon, eBay, Walmart, Temu, Faire => marketplace_facilitator; Shopify, WooCommerce, BigCommerce, API Sales Channel, SKU.io => seller)\n- unclassified: the channel's integration name is in neither map, so it defaulted conservatively to seller and is flagged for review (is_unclassified = true)\n\nThe top-level `has_unclassified` flag is true when at least one channel is unclassified, so the UI can surface a review banner.\n\nAuthentication: Requires Bearer token (scope rw:reports)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/channel-responsibility","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","channel-responsibility"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"integration_instance_id\": 12,\n      \"name\": \"Amazon US\",\n      \"integration_name\": \"Amazon\",\n      \"tax_collection_responsibility\": \"marketplace_facilitator\",\n      \"tax_collection_responsibility_label\": \"Marketplace facilitator\",\n      \"source\": \"default\",\n      \"is_default\": true,\n      \"is_unclassified\": false\n    },\n    {\n      \"integration_instance_id\": 8,\n      \"name\": \"Shopify US\",\n      \"integration_name\": \"Shopify\",\n      \"tax_collection_responsibility\": \"seller\",\n      \"tax_collection_responsibility_label\": \"Seller-collected\",\n      \"source\": \"default\",\n      \"is_default\": true,\n      \"is_unclassified\": false\n    },\n    {\n      \"integration_instance_id\": 21,\n      \"name\": \"TikTok Shop\",\n      \"integration_name\": \"TikTok\",\n      \"tax_collection_responsibility\": \"seller\",\n      \"tax_collection_responsibility_label\": \"Seller-collected\",\n      \"source\": \"unclassified\",\n      \"is_default\": true,\n      \"is_unclassified\": true\n    }\n  ],\n  \"has_unclassified\": true\n}"}]},{"name":"Update Channel Tax Responsibility","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/channel-responsibility/{integrationInstance}","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","channel-responsibility",":integrationInstance"],"variable":[{"key":"integrationInstance","value":"8","description":"Required. The integration instance (sales channel) id whose tax-collection responsibility is being overridden."}]},"description":"Overrides a single sales channel's tax-collection responsibility (Phase 2). Saving an override sets `source` to override and `is_default` to false for that channel; it persists to integration_instances.tax_collection_responsibility and immediately affects the responsibility / state_responsibility lenses of the Sales Tax Liability Report.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest body:\n- tax_collection_responsibility (required): seller | marketplace_facilitator\n\nAuthentication: Requires Bearer token (scope rw:reports).","body":{"mode":"raw","raw":"{\n  \"tax_collection_responsibility\": \"marketplace_facilitator\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/channel-responsibility/{integrationInstance}","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","channel-responsibility",":integrationInstance"],"variable":[{"key":"integrationInstance","value":"8","description":"Required. The integration instance (sales channel) id whose tax-collection responsibility is being overridden."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"integration_instance_id\": 8,\n    \"name\": \"Shopify US\",\n    \"integration_name\": \"Shopify\",\n    \"tax_collection_responsibility\": \"marketplace_facilitator\",\n    \"tax_collection_responsibility_label\": \"Marketplace facilitator\",\n    \"source\": \"override\",\n    \"is_default\": false,\n    \"is_unclassified\": false\n  }\n}"}]},{"name":"Get Economic Nexus Exposure","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/nexus","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","nexus"]},"description":"Economic Nexus Monitoring dashboard (Phase 3). Returns one row per destination jurisdiction (state/region) with measured sales and transaction counts compared against that jurisdiction's configured economic-nexus threshold, a computed status, and the percent-of-threshold.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nMeasured volume is computed per jurisdiction over that jurisdiction's own measurement window (rolling 12 months, calendar year, or previous-or-current calendar year), honouring each threshold's counts_marketplace_sales and counts_exempt_sales flags.\n\nStatus values:\n- registered: tenant is registered in the jurisdiction (reported regardless of volume).\n- threshold_met: measured volume meets/exceeds the threshold and the tenant is NOT registered.\n- approaching: measured volume is at or above warning_threshold_percent of the threshold but below it.\n- not_approaching: measured volume is comfortably below the threshold.\n- no_threshold: no threshold configured for the jurisdiction.\n\nResults are read from a per-tenant cache populated by the recompute job; computed_at is the cache timestamp (null if never computed).\n\nAuthentication: Requires Bearer token (scope rw:reports)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/nexus","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","nexus"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"country_code\": \"US\",\n      \"region_code\": \"CA\",\n      \"region\": \"California\",\n      \"measured_sales\": \"612450.00\",\n      \"measured_transactions\": 4120,\n      \"threshold_amount\": \"500000.00\",\n      \"threshold_transaction_count\": null,\n      \"measurement_period\": \"previous_or_current_calendar_year\",\n      \"measurement_period_label\": \"Previous or current calendar year\",\n      \"percent_of_threshold\": 122.49,\n      \"is_registered\": false,\n      \"status\": \"threshold_met\",\n      \"status_label\": \"Threshold met\"\n    },\n    {\n      \"country_code\": \"US\",\n      \"region_code\": \"TX\",\n      \"region\": \"Texas\",\n      \"measured_sales\": \"421000.00\",\n      \"measured_transactions\": 1890,\n      \"threshold_amount\": \"500000.00\",\n      \"threshold_transaction_count\": null,\n      \"measurement_period\": \"previous_or_current_calendar_year\",\n      \"measurement_period_label\": \"Previous or current calendar year\",\n      \"percent_of_threshold\": 84.2,\n      \"is_registered\": false,\n      \"status\": \"approaching\",\n      \"status_label\": \"Approaching\"\n    },\n    {\n      \"country_code\": \"US\",\n      \"region_code\": \"NY\",\n      \"region\": \"New York\",\n      \"measured_sales\": \"95300.00\",\n      \"measured_transactions\": 540,\n      \"threshold_amount\": \"500000.00\",\n      \"threshold_transaction_count\": 100,\n      \"measurement_period\": \"previous_or_current_calendar_year\",\n      \"measurement_period_label\": \"Previous or current calendar year\",\n      \"percent_of_threshold\": 540,\n      \"is_registered\": true,\n      \"status\": \"registered\",\n      \"status_label\": \"Registered\"\n    }\n  ],\n  \"currency\": \"USD\",\n  \"computed_at\": \"2026-06-09T14:05:22+00:00\"\n}"}]},{"name":"Recompute Economic Nexus Exposure","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/nexus/recompute","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","nexus","recompute"]},"description":"Dispatches the 'Recompute Nexus Exposure' tracked background job (Phase 3), which measures sales by jurisdiction and refreshes the per-tenant nexus exposure cache read by GET /api/reporting/sales-tax/nexus. Progress is reported through the tracked job log endpoints.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest body:\n- country_code (optional): destination country ISO code; uppercased server-side. Defaults to US.\n\nReturns the tracked_job_log_id so the frontend can follow progress. A second dispatch is rejected with 409 while a recompute is already queued or running.\n\nAuthentication: Requires Bearer token (scope rw:reports).","body":{"mode":"raw","raw":"{\n  \"country_code\": \"US\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/nexus/recompute","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","nexus","recompute"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 8842\n  },\n  \"message\": \"Nexus exposure recompute started.\"\n}"}]},{"name":"List Economic Nexus Thresholds","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/nexus/thresholds","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","nexus","thresholds"]},"description":"Lists every configured economic-nexus threshold (Phase 3) — one per jurisdiction. Each threshold defines the dollar amount and/or transaction count, how they combine (threshold_logic), the measurement window, and whether marketplace-facilitated and exempt sales count toward the measured volume.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope rw:reports)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/nexus/thresholds","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","nexus","thresholds"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5,\n      \"country_code\": \"US\",\n      \"region_code\": \"CA\",\n      \"threshold_amount\": \"500000.00\",\n      \"threshold_transaction_count\": null,\n      \"threshold_logic\": \"or\",\n      \"threshold_logic_label\": \"Amount or transactions\",\n      \"measurement_period\": \"previous_or_current_calendar_year\",\n      \"measurement_period_label\": \"Previous or current calendar year\",\n      \"counts_marketplace_sales\": false,\n      \"counts_exempt_sales\": true,\n      \"effective_from\": \"2019-04-01\",\n      \"notes\": \"Wayfair economic nexus threshold.\"\n    },\n    {\n      \"id\": 12,\n      \"country_code\": \"US\",\n      \"region_code\": \"NY\",\n      \"threshold_amount\": \"500000.00\",\n      \"threshold_transaction_count\": 100,\n      \"threshold_logic\": \"and\",\n      \"threshold_logic_label\": \"Amount and transactions\",\n      \"measurement_period\": \"previous_or_current_calendar_year\",\n      \"measurement_period_label\": \"Previous or current calendar year\",\n      \"counts_marketplace_sales\": false,\n      \"counts_exempt_sales\": true,\n      \"effective_from\": \"2019-06-21\",\n      \"notes\": null\n    }\n  ]\n}"}]},{"name":"Update Economic Nexus Threshold","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/nexus/thresholds/{taxNexusThreshold}","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","nexus","thresholds",":taxNexusThreshold"],"variable":[{"key":"taxNexusThreshold","value":"5","description":"Required. The economic-nexus threshold id being updated."}]},"description":"Updates a single jurisdiction's economic-nexus threshold config (Phase 3). All fields are optional; only the supplied fields change.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest body:\n- threshold_amount (nullable, numeric >= 0): dollar threshold; null to disable the amount test.\n- threshold_transaction_count (nullable, integer >= 0): transaction-count threshold; null to disable the count test.\n- threshold_logic (sometimes): and | or — whether both or either test triggers nexus.\n- measurement_period (sometimes): rolling_12_months | calendar_year | previous_or_current_calendar_year.\n- counts_marketplace_sales (sometimes, boolean): whether marketplace-facilitated sales count toward measured volume.\n- counts_exempt_sales (sometimes, boolean): whether tax-exempt sales count toward measured volume.\n- effective_from (nullable, date): when the threshold takes effect.\n- notes (nullable, string <= 255).\n\nAuthentication: Requires Bearer token (scope rw:reports).","body":{"mode":"raw","raw":"{\n  \"threshold_amount\": 500000,\n  \"threshold_transaction_count\": 200,\n  \"threshold_logic\": \"or\",\n  \"measurement_period\": \"previous_or_current_calendar_year\",\n  \"counts_marketplace_sales\": false,\n  \"counts_exempt_sales\": true,\n  \"effective_from\": \"2019-04-01\",\n  \"notes\": \"Wayfair economic nexus threshold.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/nexus/thresholds/{taxNexusThreshold}","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","nexus","thresholds",":taxNexusThreshold"],"variable":[{"key":"taxNexusThreshold","value":"5","description":"Required. The economic-nexus threshold id being updated."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"country_code\": \"US\",\n    \"region_code\": \"CA\",\n    \"threshold_amount\": \"500000.00\",\n    \"threshold_transaction_count\": 200,\n    \"threshold_logic\": \"or\",\n    \"threshold_logic_label\": \"Amount or transactions\",\n    \"measurement_period\": \"previous_or_current_calendar_year\",\n    \"measurement_period_label\": \"Previous or current calendar year\",\n    \"counts_marketplace_sales\": false,\n    \"counts_exempt_sales\": true,\n    \"effective_from\": \"2019-04-01\",\n    \"notes\": \"Wayfair economic nexus threshold.\"\n  }\n}"}]},{"name":"List Sales-Tax Registrations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/nexus/registrations","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","nexus","registrations"]},"description":"Lists every sales-tax registration record (Phase 3). A registered jurisdiction is reported on the nexus dashboard with a 'registered' status regardless of measured volume (registration override).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope rw:reports)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/nexus/registrations","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","nexus","registrations"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3,\n      \"country_code\": \"US\",\n      \"region_code\": \"NY\",\n      \"is_registered\": true,\n      \"registration_date\": \"2024-01-15\",\n      \"registration_number\": \"NY-12345678\",\n      \"filing_frequency\": \"quarterly\",\n      \"filing_frequency_label\": \"Quarterly\",\n      \"notes\": \"Registered after crossing threshold in 2023.\"\n    }\n  ]\n}"}]},{"name":"Create Sales-Tax Registration","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/nexus/registrations","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","nexus","registrations"]},"description":"Creates a sales-tax registration record for a jurisdiction (Phase 3). The country+region pair must be unique.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest body:\n- country_code (required, size:2): ISO country code; uppercased server-side.\n- region_code (required, <= 10 chars): state/region code; uppercased server-side. Unique per country.\n- is_registered (sometimes, boolean): whether the tenant is registered in the jurisdiction.\n- registration_date (nullable, date).\n- registration_number (nullable, <= 100 chars).\n- filing_frequency (nullable): monthly | quarterly | annually.\n- notes (nullable, <= 255 chars).\n\nAuthentication: Requires Bearer token (scope rw:reports).","body":{"mode":"raw","raw":"{\n  \"country_code\": \"US\",\n  \"region_code\": \"NY\",\n  \"is_registered\": true,\n  \"registration_date\": \"2024-01-15\",\n  \"registration_number\": \"NY-12345678\",\n  \"filing_frequency\": \"quarterly\",\n  \"notes\": \"Registered after crossing threshold in 2023.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/nexus/registrations","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","nexus","registrations"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"country_code\": \"US\",\n    \"region_code\": \"NY\",\n    \"is_registered\": true,\n    \"registration_date\": \"2024-01-15\",\n    \"registration_number\": \"NY-12345678\",\n    \"filing_frequency\": \"quarterly\",\n    \"filing_frequency_label\": \"Quarterly\",\n    \"notes\": \"Registered after crossing threshold in 2023.\"\n  }\n}"}]},{"name":"Update Sales-Tax Registration","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/nexus/registrations/{taxNexusRegistration}","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","nexus","registrations",":taxNexusRegistration"],"variable":[{"key":"taxNexusRegistration","value":"3","description":"Required. The sales-tax registration id being updated. The jurisdiction (country + region) is fixed once created."}]},"description":"Updates an existing sales-tax registration record (Phase 3). The jurisdiction (country + region) is fixed once created; only the registration details and status may change. All fields are optional.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest body:\n- is_registered (sometimes, boolean).\n- registration_date (nullable, date).\n- registration_number (nullable, <= 100 chars).\n- filing_frequency (nullable): monthly | quarterly | annually.\n- notes (nullable, <= 255 chars).\n\nAuthentication: Requires Bearer token (scope rw:reports).","body":{"mode":"raw","raw":"{\n  \"is_registered\": true,\n  \"registration_date\": \"2024-01-15\",\n  \"registration_number\": \"NY-12345678\",\n  \"filing_frequency\": \"monthly\",\n  \"notes\": \"Switched to monthly filing for 2026.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/nexus/registrations/{taxNexusRegistration}","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","nexus","registrations",":taxNexusRegistration"],"variable":[{"key":"taxNexusRegistration","value":"3","description":"Required. The sales-tax registration id being updated. The jurisdiction (country + region) is fixed once created."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"country_code\": \"US\",\n    \"region_code\": \"NY\",\n    \"is_registered\": true,\n    \"registration_date\": \"2024-01-15\",\n    \"registration_number\": \"NY-12345678\",\n    \"filing_frequency\": \"monthly\",\n    \"filing_frequency_label\": \"Monthly\",\n    \"notes\": \"Switched to monthly filing for 2026.\"\n  }\n}"}]},{"name":"Delete Sales-Tax Registration","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/nexus/registrations/{taxNexusRegistration}","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","nexus","registrations",":taxNexusRegistration"],"variable":[{"key":"taxNexusRegistration","value":"3","description":"Required. The sales-tax registration id being deleted."}]},"description":"Deletes a sales-tax registration record (Phase 3). The jurisdiction reverts to volume-based status on the nexus dashboard.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope rw:reports)."},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/reporting/sales-tax/nexus/registrations/{taxNexusRegistration}","host":["{{baseUrl}}"],"path":["api","reporting","sales-tax","nexus","registrations",":taxNexusRegistration"],"variable":[{"key":"taxNexusRegistration","value":"3","description":"Required. The sales-tax registration id being deleted."}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":""}]},{"name":"Get Inventory Valuation","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-valuation","host":["{{baseUrl}}"],"path":["api","reporting","inventory-valuation"]},"description":"Returns the inventory valuation report summary grouped by warehouse/category.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\n**Accounting alignment (lock dates).** The response also carries an `accounting_alignment` object stating how much of this valuation the general ledger has not recognised yet. An accounting lock date cannot stop stock from moving — it only pushes the posting to the first open day — so until the GL catches up this report is right to be worth more than the inventory control account says, and the difference is not a discrepancy to chase. Fields: `as_of_date` (the valuation date the figures are stated at), `displaced_value` (value whose stock has already moved but whose posting a lock date pushed to a later day), `total_valuation` (this report's total), `ledger_recognized_valuation` (`total_valuation` − `displaced_value` — the figure that ties to the general ledger today) and `has_displacement` (`false` when nothing is held back, in which case `displaced_value` is `0` and the two totals are identical).\n\nINVENTORY DEBT: totals and every group also carry `inventory_debt` (units shipped with no stock behind them and not yet settled) and `inventory_debt_valuation` (the Inventory Debt Clearing balance still carried - posted less relieved). Reported as their own figures rather than folded into on-hand: debt units consumed no cost layer, so on-hand quantity and valuation are unchanged by them.\n\nA valuation for a past date is not necessarily reproducible: cost revisions can carry an effective date inside a period that has already been reported. `retroactive_restatements` discloses how many such revisions were recorded AFTER the as-of date and what they moved the figure by, and `computed_at` stamps when this response was produced — take every figure you are reconciling from a single run."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/inventory-valuation","host":["{{baseUrl}}"],"path":["api","reporting","inventory-valuation"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"group_by\": [\n      \"warehouse\"\n    ],\n    \"groups\": [\n      {\n        \"key\": {\n          \"id\": 1,\n          \"name\": \"Main Warehouse\"\n        },\n        \"totals\": {\n          \"available\": 1200,\n          \"reserved\": 150,\n          \"in_transit\": 200,\n          \"total_qty\": 1550,\n          \"available_valuation\": 98500,\n          \"reserved_valuation\": 12800,\n          \"in_transit_valuation\": 14100,\n          \"total_valuation\": 125400\n        },\n        \"children\": []\n      }\n    ],\n    \"grand_totals\": {\n      \"available\": 1200,\n      \"reserved\": 150,\n      \"in_transit\": 200,\n      \"total_qty\": 1550,\n      \"available_valuation\": 98500,\n      \"reserved_valuation\": 12800,\n      \"in_transit_valuation\": 14100,\n      \"total_valuation\": 125400\n    },\n    \"accounting_alignment\": {\n      \"as_of_date\": \"2026-07-31\",\n      \"displaced_value\": 1338.75,\n      \"total_valuation\": 125400,\n      \"ledger_recognized_valuation\": 124061.25,\n      \"has_displacement\": true\n    },\n    \"retroactive_restatements\": {\n      \"as_of_date\": \"2026-01-31\",\n      \"computed_at\": \"2026-03-05T09:14:22+00:00\",\n      \"count\": 802,\n      \"net_value\": -925.08,\n      \"latest_recorded_at\": \"2026-03-04T18:02:11+00:00\",\n      \"has_restatements\": true\n    }\n  },\n  \"success\": true\n}"}]},{"name":"Export Inventory Valuation","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-valuation/export","host":["{{baseUrl}}"],"path":["api","reporting","inventory-valuation","export"]},"description":"Streams the inventory valuation report as a CSV file download (Content-Type text/csv, attachment inventory-valuation.csv).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nColumns: the selected group_by dimensions (SKU, Warehouse, Supplier, Brand, COGS Nominal Code) followed by quantity/valuation pairs — Available, Allocated, On Hold, On Hand, On Hand Average Cost, Inventory Debt Units Owed, Inventory Debt Clearing, In Transit, Inbound, Prepaid, and Average Cost. The two Inventory Debt columns carry units that shipped before you owned them and the unrelieved balance still sitting in the Inventory Debt Clearing account; they are reported separately and are deliberately NOT part of On Hand, which reconciles to the inventory control account. On Hand / On Hand Valuation are the physical on-hand position as of the report date (on_hand_active_qty x average cost) and include allocated & on-hold stock but exclude In Transit.\n\nResponse: CSV file download (not JSON)."}},{"name":"Get Inventory Valuation Drilldown by Category","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-valuation/drilldown/category","host":["{{baseUrl}}"],"path":["api","reporting","inventory-valuation","drilldown","category"]},"description":"Returns inventory valuation broken down by category.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/inventory-valuation/drilldown/category","host":["{{baseUrl}}"],"path":["api","reporting","inventory-valuation","drilldown","category"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"category\": \"Electronics\",\n      \"total_qty\": 450,\n      \"total_valuation\": 42500,\n      \"available_valuation\": 35000\n    }\n  ]\n}"}]},{"name":"Get Inventory Valuation Drilldown by Source","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-valuation/drilldown/sources","host":["{{baseUrl}}"],"path":["api","reporting","inventory-valuation","drilldown","sources"]},"description":"Returns inventory valuation broken down by source (purchase order, initial inventory, etc.).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/inventory-valuation/drilldown/sources","host":["{{baseUrl}}"],"path":["api","reporting","inventory-valuation","drilldown","sources"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"source\": \"Purchase Order\",\n      \"total_qty\": 1200,\n      \"total_valuation\": 98000\n    }\n  ]\n}"}]},{"name":"Get Inventory Valuation Drilldown by Transactions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-valuation/drilldown/transactions","host":["{{baseUrl}}"],"path":["api","reporting","inventory-valuation","drilldown","transactions"]},"description":"Returns inventory valuation broken down by individual inventory transactions.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\n`consumed_quantity` and `remaining_quantity` are reconstructed AS AT the requested `date` from the layer's own movements, so the header always agrees with the transactions listed beneath it (and with the row it was opened from) rather than reporting the layer's balance today."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/inventory-valuation/drilldown/transactions","host":["{{baseUrl}}"],"path":["api","reporting","inventory-valuation","drilldown","transactions"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5001,\n      \"type\": \"receipt\",\n      \"quantity\": 100,\n      \"cost\": 9.25,\n      \"valuation\": 925,\n      \"date\": \"2024-05-01\"\n    }\n  ]\n}"}]},{"name":"Get Realtime Inventory","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/realtime-inventory","host":["{{baseUrl}}"],"path":["api","reporting","realtime-inventory"]},"description":"Returns real-time inventory levels per product per warehouse. Supports both application UI format (filter[search], filter[warehouse_id]) and legacy Vue 2 format (search, warehouse_id) — both are auto-normalized.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/realtime-inventory","host":["{{baseUrl}}"],"path":["api","reporting","realtime-inventory"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product\": {\n        \"id\": 42,\n        \"sku\": \"WIDGET-001\",\n        \"name\": \"Blue Widget\"\n      },\n      \"warehouse\": {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      },\n      \"inventory\": {\n        \"available\": 150,\n        \"allocated\": 20,\n        \"on_hold\": 5,\n        \"in_transit\": 10,\n        \"total\": 185,\n        \"inbound\": 50,\n        \"projected_total\": 235\n      }\n    }\n  ]\n}"}]},{"name":"Get Real-Time Inventory Column Totals","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/realtime-inventory/aggregates?aggregates[on_hand]=sum","host":["{{baseUrl}}"],"path":["api","reporting","realtime-inventory","aggregates"],"query":[{"key":"aggregates[on_hand]","value":"sum","description":"aggregates[&lt;column>]=&lt;calc>. Repeat for each column to total.","disabled":true}]},"description":"Column-summary totals for the report's \"Calculate\" footer — computes Sum, Average, Min, Max, Range, or a Count variant per column over the FULL filtered set (not just the current page), in one query.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAggregatable columns: on_hand, available, allocated, on_hold, in_transit, total, inbound, projected_total, backorder, backorder_planned, backorder_awaiting_receipt. Supported calcs: sum, avg, min, max, range, count_all, count_values, count_unique, count_empty, count_not_empty. Pass them as aggregates[&lt;column>]=&lt;calc>. Totals cover the by-warehouse quantity columns.\n\nApply the same filters and search as the list endpoint to scope the totals. An unknown calc returns 422. Authentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/realtime-inventory/aggregates?aggregates[on_hand]=sum","host":["{{baseUrl}}"],"path":["api","reporting","realtime-inventory","aggregates"],"query":[{"key":"aggregates[on_hand]","value":"sum","description":"aggregates[&lt;column>]=&lt;calc>. Repeat for each column to total.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"available\": {\n      \"calc\": \"sum\",\n      \"value\": 58210\n    },\n    \"on_hand\": {\n      \"calc\": \"sum\",\n      \"value\": 61180\n    },\n    \"backorder\": {\n      \"calc\": \"sum\",\n      \"value\": 430\n    }\n  }\n}"}]},{"name":"Get Realtime Inventory Pivot","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/realtime-inventory/pivot","host":["{{baseUrl}}"],"path":["api","reporting","realtime-inventory","pivot"]},"description":"Returns the wide (pivot) shape of the realtime inventory report: one row per SKU, one column per warehouse and one column per sales channel, plus a totals block per row. The channel columns carry both the SKU-side quantity and the channel-side quantity so sync drift is visible per SKU per channel.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRows are grouped and paginated at the SKU level (default 25 per page); warehouse and channel cells are keyed by the ids listed in `columns`.\n\nFilters: `filter[search]` (product SKU or name), `filter[warehouse_id]`, and numeric advanced filters on `available`, `on_hold`, `allocated`, `in_transit`, `total`, `inbound`, `projected_total`, `backorder_planned`, `backorder_awaiting_receipt` and `backorder` (the sum of the two backorder columns). Numeric filters accept either the bare key (`filter[available]`) or an operator suffix: `is`, `is_not`, `is_one_of`, `is_not_one_of`, `greater_than`, `less_than`, `greater_than_or_equal`, `less_than_or_equal`, `between`, `is_empty`, `is_not_empty` — e.g. `filter[available.greater_than]=100`.\n\nLegacy top-level `search` and `warehouse_id` params are auto-normalized into `filter[]`.\n\nSort: `product_sku` (default), `product_name`, `available`, `on_hold`, `allocated`, `in_transit`, `total`, `inbound`, `projected_total`, `backorder_planned`, `backorder_awaiting_receipt`, `backorder`, `on_hand`. Prefix with `-` for descending. Per-warehouse and per-channel columns are not sortable.\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/realtime-inventory/pivot","host":["{{baseUrl}}"],"path":["api","reporting","realtime-inventory","pivot"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"columns\": {\n      \"warehouses\": [\n        {\n          \"id\": 1,\n          \"name\": \"Main Warehouse\"\n        },\n        {\n          \"id\": 2,\n          \"name\": \"Overflow Warehouse\"\n        }\n      ],\n      \"channels\": [\n        {\n          \"id\": 40,\n          \"name\": \"Shopify - Acme Store\"\n        },\n        {\n          \"id\": 41,\n          \"name\": \"Amazon - US Marketplace\"\n        }\n      ]\n    },\n    \"items\": [\n      {\n        \"product\": {\n          \"id\": 42,\n          \"sku\": \"WIDGET-001\",\n          \"name\": \"Blue Widget\"\n        },\n        \"warehouses\": {\n          \"1\": {\n            \"available\": 150,\n            \"allocated\": 20,\n            \"on_hold\": 5,\n            \"in_transit\": 10,\n            \"total\": 185,\n            \"inbound\": 50\n          },\n          \"2\": {\n            \"available\": 30,\n            \"allocated\": 0,\n            \"on_hold\": 0,\n            \"in_transit\": 0,\n            \"total\": 30,\n            \"inbound\": 0\n          }\n        },\n        \"channels\": {\n          \"40\": {\n            \"sku_quantity\": 180,\n            \"channel_quantity\": 180,\n            \"variance\": 0,\n            \"listing_count\": 1,\n            \"in_sync\": true,\n            \"never_synced\": false\n          },\n          \"41\": {\n            \"sku_quantity\": 180,\n            \"channel_quantity\": 165,\n            \"variance\": -15,\n            \"listing_count\": 2,\n            \"in_sync\": false,\n            \"never_synced\": false\n          }\n        },\n        \"totals\": {\n          \"available\": 180,\n          \"allocated\": 20,\n          \"backorder_planned\": 4,\n          \"backorder_awaiting_receipt\": 2,\n          \"backorder\": 6,\n          \"on_hold\": 5,\n          \"in_transit\": 10,\n          \"total\": 215,\n          \"inbound\": 50,\n          \"projected_total\": 265\n        }\n      }\n    ],\n    \"pagination\": {\n      \"total\": 1280,\n      \"current_page\": 1,\n      \"per_page\": 25,\n      \"last_page\": 52\n    }\n  }\n}"}]},{"name":"Get Realtime Inventory Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/realtime-inventory/summary","host":["{{baseUrl}}"],"path":["api","reporting","realtime-inventory","summary"]},"description":"Returns aggregate summary totals for the realtime inventory report, over the same filtered set as the list endpoint: pass the same filter[...] parameters (e.g. filter[warehouse_id], filter[search]) to total one warehouse or search result. With no filters, totals cover all warehouses.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/realtime-inventory/summary","host":["{{baseUrl}}"],"path":["api","reporting","realtime-inventory","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_available\": 12500,\n    \"total_allocated\": 850,\n    \"total_in_transit\": 1200,\n    \"total_inbound\": 3400,\n    \"total_projected\": 17950\n  }\n}"}]},{"name":"Export Realtime Inventory","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/realtime-inventory/export","host":["{{baseUrl}}"],"path":["api","reporting","realtime-inventory","export"]},"description":"Exports real-time inventory data as a CSV file. Columns: SKU, Product Name, Warehouse, Available, Allocated, On Hold, In Transit, Total, Inbound, Projected Total.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nResponse: text/csv file download (realtime-inventory.csv)"}},{"name":"Get Financials by Brand","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/financials-by-brand?interval=month&metric=revenue&limit=10&sort=desc","host":["{{baseUrl}}"],"path":["api","reporting","financials-by-brand"],"query":[{"key":"interval","value":"month","description":"Required. Time interval: day | week | month | quarter | year","disabled":true},{"key":"metric","value":"revenue","description":"Required. Metric: revenue | profit | profit margin | units sold | # of orders","disabled":true},{"key":"limit","value":"10","description":"Optional. Number of results","disabled":true},{"key":"sort","value":"desc","description":"Optional. Sort: asc | desc","disabled":true}]},"description":"Returns financial summary data grouped by brand.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nRequired: interval (day|week|month|quarter|year), metric (revenue|profit|profit margin|units sold|# of orders)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/financials-by-brand?interval=month&metric=revenue&limit=10&sort=desc","host":["{{baseUrl}}"],"path":["api","reporting","financials-by-brand"],"query":[{"key":"interval","value":"month","description":"Required. Time interval: day | week | month | quarter | year","disabled":true},{"key":"metric","value":"revenue","description":"Required. Metric: revenue | profit | profit margin | units sold | # of orders","disabled":true},{"key":"limit","value":"10","description":"Optional. Number of results","disabled":true},{"key":"sort","value":"desc","description":"Optional. Sort: asc | desc","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"brand\": \"Acme\",\n      \"period\": \"2024-06\",\n      \"revenue\": 15200,\n      \"profit\": 6080,\n      \"units_sold\": 320,\n      \"num_orders\": 45\n    }\n  ]\n}"}]},{"name":"Get Financials by SKU","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/financials-by-sku?interval=month&metric=profit&limit=20&sort=desc","host":["{{baseUrl}}"],"path":["api","reporting","financials-by-sku"],"query":[{"key":"interval","value":"month","description":"Required: day | week | month | quarter | year","disabled":true},{"key":"metric","value":"profit","description":"Required: revenue | profit | profit margin | units sold | # of orders","disabled":true},{"key":"limit","value":"20","description":"Optional","disabled":true},{"key":"sort","value":"desc","description":"Optional: asc | desc","disabled":true}]},"description":"Returns financial summary data grouped by SKU/product.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/financials-by-sku?interval=month&metric=profit&limit=20&sort=desc","host":["{{baseUrl}}"],"path":["api","reporting","financials-by-sku"],"query":[{"key":"interval","value":"month","description":"Required: day | week | month | quarter | year","disabled":true},{"key":"metric","value":"profit","description":"Required: revenue | profit | profit margin | units sold | # of orders","disabled":true},{"key":"limit","value":"20","description":"Optional","disabled":true},{"key":"sort","value":"desc","description":"Optional: asc | desc","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"sku\": \"WIDGET-001\",\n      \"period\": \"2024-06\",\n      \"revenue\": 4500,\n      \"profit\": 1800\n    }\n  ]\n}"}]},{"name":"Get Financials by Supplier","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/financials-by-supplier?interval=month&metric=revenue","host":["{{baseUrl}}"],"path":["api","reporting","financials-by-supplier"],"query":[{"key":"interval","value":"month","description":"Required: day | week | month | quarter | year","disabled":true},{"key":"metric","value":"revenue","description":"Required: revenue | profit | profit margin | units sold | # of orders","disabled":true}]},"description":"Returns financial summary data grouped by supplier.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/financials-by-supplier?interval=month&metric=revenue","host":["{{baseUrl}}"],"path":["api","reporting","financials-by-supplier"],"query":[{"key":"interval","value":"month","description":"Required: day | week | month | quarter | year","disabled":true},{"key":"metric","value":"revenue","description":"Required: revenue | profit | profit margin | units sold | # of orders","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"supplier\": \"Widget Co\",\n      \"period\": \"2024-06\",\n      \"revenue\": 28000,\n      \"units_sold\": 600\n    }\n  ]\n}"}]},{"name":"Get Financials by Sales Channel","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/financials-by-sales-channel?interval=month&metric=revenue","host":["{{baseUrl}}"],"path":["api","reporting","financials-by-sales-channel"],"query":[{"key":"interval","value":"month","description":"Required: day | week | month | quarter | year","disabled":true},{"key":"metric","value":"revenue","description":"Required: revenue | profit | profit margin | units sold | # of orders","disabled":true}]},"description":"Returns financial summary data grouped by sales channel.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/financials-by-sales-channel?interval=month&metric=revenue","host":["{{baseUrl}}"],"path":["api","reporting","financials-by-sales-channel"],"query":[{"key":"interval","value":"month","description":"Required: day | week | month | quarter | year","disabled":true},{"key":"metric","value":"revenue","description":"Required: revenue | profit | profit margin | units sold | # of orders","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"sales_channel\": \"Shopify\",\n      \"period\": \"2024-06\",\n      \"revenue\": 12000,\n      \"num_orders\": 85\n    }\n  ]\n}"}]},{"name":"Get Financials by Sub-Sales Channel","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/financials-by-sub-sales-channel?interval=month&metric=revenue","host":["{{baseUrl}}"],"path":["api","reporting","financials-by-sub-sales-channel"],"query":[{"key":"interval","value":"month","description":"Required: day | week | month | quarter | year","disabled":true},{"key":"metric","value":"revenue","description":"Required: revenue | profit | profit margin | units sold | # of orders","disabled":true}]},"description":"Returns financial summary data grouped by sub-sales channel.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/financials-by-sub-sales-channel?interval=month&metric=revenue","host":["{{baseUrl}}"],"path":["api","reporting","financials-by-sub-sales-channel"],"query":[{"key":"interval","value":"month","description":"Required: day | week | month | quarter | year","disabled":true},{"key":"metric","value":"revenue","description":"Required: revenue | profit | profit margin | units sold | # of orders","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"sub_sales_channel\": \"Shopify US\",\n      \"period\": \"2024-06\",\n      \"revenue\": 8500\n    }\n  ]\n}"}]},{"name":"Get Financials by Product Type","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/financials-by-product-type?interval=month&metric=revenue","host":["{{baseUrl}}"],"path":["api","reporting","financials-by-product-type"],"query":[{"key":"interval","value":"month","description":"Required: day | week | month | quarter | year","disabled":true},{"key":"metric","value":"revenue","description":"Required: revenue | profit | profit margin | units sold | # of orders","disabled":true}]},"description":"Returns financial summary data grouped by product type (standard, blemished, kit).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/financials-by-product-type?interval=month&metric=revenue","host":["{{baseUrl}}"],"path":["api","reporting","financials-by-product-type"],"query":[{"key":"interval","value":"month","description":"Required: day | week | month | quarter | year","disabled":true},{"key":"metric","value":"revenue","description":"Required: revenue | profit | profit margin | units sold | # of orders","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_type\": \"standard\",\n      \"period\": \"2024-06\",\n      \"revenue\": 35000,\n      \"units_sold\": 780\n    }\n  ]\n}"}]},{"name":"Get Monthly COGS","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/cogs?year=2024","host":["{{baseUrl}}"],"path":["api","reporting","cogs"],"query":[{"key":"year","value":"2024","description":"Year to get COGS data for (default: current year)","disabled":true}]},"description":"Returns monthly COGS (cost of goods sold) data for a specific year.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/cogs?year=2024","host":["{{baseUrl}}"],"path":["api","reporting","cogs"],"query":[{"key":"year","value":"2024","description":"Year to get COGS data for (default: current year)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"month\": \"2024-06\",\n      \"total_cost\": 45200\n    },\n    {\n      \"month\": \"2024-05\",\n      \"total_cost\": 38900\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Get Purchase Summary by Period","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/purchases/summary-by-period?period=month&trailing_days=90","host":["{{baseUrl}}"],"path":["api","reporting","purchases","summary-by-period"],"query":[{"key":"period","value":"month","description":"Required. Period grouping: month | year","disabled":true},{"key":"trailing_days","value":"90","description":"Optional. Number of trailing days to include","disabled":true}]},"description":"Returns a summary of purchases grouped by period (month or year).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nRequired: period (month|year)\nOptional: trailing_days (integer)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/purchases/summary-by-period?period=month&trailing_days=90","host":["{{baseUrl}}"],"path":["api","reporting","purchases","summary-by-period"],"query":[{"key":"period","value":"month","description":"Required. Period grouping: month | year","disabled":true},{"key":"trailing_days","value":"90","description":"Optional. Number of trailing days to include","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"period_date\": \"2024-06\",\n      \"total\": 18500,\n      \"count\": 12\n    },\n    {\n      \"period_date\": \"2024-05\",\n      \"total\": 22100,\n      \"count\": 15\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Get Accounting COGS","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/accounting-cogs","host":["{{baseUrl}}"],"path":["api","reporting","accounting-cogs"]},"description":"Returns accounting COGS report data including average cost, quantities sold/returned/adjusted, and COGS totals (gross, returned, adjusted, net).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/accounting-cogs","host":["{{baseUrl}}"],"path":["api","reporting","accounting-cogs"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"avg_cost\": 9.25,\n      \"qty_sold\": 450,\n      \"qty_returned\": 12,\n      \"qty_adjusted\": -5,\n      \"net_qty\": 433,\n      \"gross_cogs\": 4162.5,\n      \"returned_cogs\": 111,\n      \"adjusted_cogs\": -46.25,\n      \"net_cogs\": 4005.25\n    }\n  ]\n}"}]},{"name":"Get Accounting COGS Drilldown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/accounting-cogs/drilldown","host":["{{baseUrl}}"],"path":["api","reporting","accounting-cogs","drilldown"]},"description":"Underlying movements behind one COGS report row — the individual `accounting_cogs` view rows (sales fulfillments, customer returns, inventory adjustments, FBA movements) that compose the clicked group/SKU aggregate. Paginated, sortable, searchable, with a `totals` block that reconciles to the report row that opened it (same 9 metrics).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nIdentify the clicked row with `dimensions[<group_by>]=<value>` for each active group-by dimension down to that row's level (e.g. sales_channel, then sku). Pass the same `filter[date_range_from]`/`filter[date_range_to]` the report was generated with. Each row includes a `product_link` to the product detail page and a `source_label`/`src_id` for the originating accounting record.\n\nAuthentication: Requires Bearer token. Scope: reports.\n\nEach movement row includes a cross-link to the underlying business document via `link_route` (e.g. the sales order fulfillment, inventory adjustment, or return receipt that produced it), plus `link_type_label`, `link_id`, and `link_reference`. `link_route` is null for batch-sourced movements that don't resolve to a single document."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/accounting-cogs/drilldown","host":["{{baseUrl}}"],"path":["api","reporting","accounting-cogs","drilldown"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"transaction_date\": \"2026-05-20T14:03:00.000000Z\",\n      \"type\": \"sales_order_fulfillment\",\n      \"type_label\": \"Sale\",\n      \"category\": \"sold\",\n      \"sales_channel_name\": \"Shopify US\",\n      \"brand\": \"Acme\",\n      \"brand_id\": 3,\n      \"country\": \"US\",\n      \"sku\": \"ACM-001\",\n      \"product_id\": 42,\n      \"product_link\": \"/products/42\",\n      \"raw_qty\": 10,\n      \"sign\": -1,\n      \"unit_cost\": 9.25,\n      \"ext_cost\": 92.5,\n      \"cogs_impact\": 92.5,\n      \"src_id\": 1187,\n      \"src_type\": \"Modules\\\\Ledger\\\\Entities\\\\LedgerJournalLine\",\n      \"source_label\": \"Journal Line\",\n      \"link_route\": \"/orders/sales-orders/fulfillments/512\",\n      \"link_type_label\": \"Sales Order Fulfillment\",\n      \"link_id\": 512,\n      \"link_reference\": \"SO-1042.1\"\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"...\",\n  \"from\": 1,\n  \"last_page\": 3,\n  \"last_page_url\": \"...\",\n  \"next_page_url\": \"...\",\n  \"path\": \"...\",\n  \"per_page\": 15,\n  \"prev_page_url\": null,\n  \"to\": 15,\n  \"total\": 42,\n  \"totals\": {\n    \"count\": 42,\n    \"qty_sold\": 450,\n    \"qty_returned\": 12,\n    \"qty_adjusted\": -5,\n    \"net_qty\": 433,\n    \"gross_cogs\": 4162.5,\n    \"returned_cogs\": 111,\n    \"adjusted_cogs\": -46.25,\n    \"net_cogs\": 4005.25,\n    \"avg_cost\": 9.25\n  }\n}"}]},{"name":"Export Accounting COGS","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/accounting-cogs/export?group_by[]=sales_channel","host":["{{baseUrl}}"],"path":["api","reporting","accounting-cogs","export"],"query":[{"key":"group_by[]","value":"sales_channel","description":"Grouping dimensions: sales_channel | country | brand | sku","disabled":true}]},"description":"Exports accounting COGS report as a CSV file. group_by[] determines which extra columns appear before the metric columns (Avg Cost, Qty Sold, Qty Returned, Qty Adjusted, Net Qty, Gross COGS, Returned COGS, Adjusted COGS, Net COGS).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nResponse: text/csv file download (accounting-cogs.csv)"}},{"name":"Export Accounting COGS Drilldown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/accounting-cogs/drilldown/export","host":["{{baseUrl}}"],"path":["api","reporting","accounting-cogs","drilldown","export"]},"description":"All underlying movements behind one COGS report row, unpaginated (capped at 5,000 rows), so every transaction for the row can be exported at once. Identify the row the same way as the paginated drilldown endpoint (`dimensions[<group_by>]=<value>` for each active group-by dimension) and pass the same `filter[date_range_from]`/`filter[date_range_to]` the report was generated with.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nUnlike the paginated drilldown, this returns the full movement set as `{ data, total, truncated }`: `data` is every movement (up to 5,000, same fields as the drilldown), `total` is the true count of movements in the row, and `truncated` is true when `total` exceeded the 5,000 cap (narrow the date range to export the remainder).\n\nAuthentication: Requires Bearer token. Scope: reports.\n\nEach movement row includes a cross-link to the underlying business document via `link_route` (e.g. the sales order fulfillment, inventory adjustment, or return receipt that produced it), plus `link_type_label`, `link_id`, and `link_reference`. `link_route` is null for batch-sourced movements that don't resolve to a single document."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/accounting-cogs/drilldown/export","host":["{{baseUrl}}"],"path":["api","reporting","accounting-cogs","drilldown","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"transaction_date\": \"2026-05-20T14:03:00.000000Z\",\n      \"type\": \"sales_order_fulfillment\",\n      \"type_label\": \"Sale\",\n      \"category\": \"sold\",\n      \"sales_channel_name\": \"Shopify US\",\n      \"brand\": \"Acme\",\n      \"brand_id\": 3,\n      \"country\": \"US\",\n      \"sku\": \"ACM-001\",\n      \"product_id\": 42,\n      \"product_link\": \"/products/42\",\n      \"raw_qty\": 10,\n      \"sign\": -1,\n      \"unit_cost\": 9.25,\n      \"ext_cost\": 92.5,\n      \"cogs_impact\": 92.5,\n      \"src_id\": 1187,\n      \"src_type\": \"Modules\\\\Ledger\\\\Entities\\\\LedgerJournalLine\",\n      \"source_label\": \"Journal Line\",\n      \"link_route\": \"/orders/sales-orders/fulfillments/512\",\n      \"link_type_label\": \"Sales Order Fulfillment\",\n      \"link_id\": 512,\n      \"link_reference\": \"SO-1042.1\"\n    }\n  ],\n  \"total\": 42,\n  \"truncated\": false\n}"}]},{"name":"Get GINR Report (Prepaid Inventory)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/ginr?start_date=2024-01-01&end_date=2024-06-30","host":["{{baseUrl}}"],"path":["api","reporting","ginr"],"query":[{"key":"start_date","value":"2024-01-01","description":"Optional. Report start date","disabled":true},{"key":"end_date","value":"2024-06-30","description":"Optional. Report end date","disabled":true}]},"description":"Returns the Goods In Not Received (GINR) / prepaid inventory report — purchase invoices paid but not yet received into inventory.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/ginr?start_date=2024-01-01&end_date=2024-06-30","host":["{{baseUrl}}"],"path":["api","reporting","ginr"],"query":[{"key":"start_date","value":"2024-01-01","description":"Optional. Report start date","disabled":true},{"key":"end_date","value":"2024-06-30","description":"Optional. Report end date","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"purchase_order_number\": \"PO-350\",\n      \"supplier\": \"Widget Co\",\n      \"invoice_date\": \"2024-05-01\",\n      \"total_invoiced\": 5000,\n      \"total_received\": 2500,\n      \"outstanding\": 2500\n    }\n  ]\n}"}]},{"name":"Get GINR Balance","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/ginr/balance","host":["{{baseUrl}}"],"path":["api","reporting","ginr","balance"]},"description":"GINR (Goods Invoiced, Not Received) — also called Accrued Purchases or GRNI — tracks the value of goods that suppliers have invoiced but that have not yet been received into inventory. The balance is calculated as: invoiced − received + discrepancies − adjustments, accumulated from the account's inventory start date (so it behaves like a balance-sheet figure, not a period movement).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the single cumulative GINR balance as of an arbitrary date, broken into its four components (invoiced, received, discrepancy, adjustments) plus the resulting `unreceived` balance. Figures are computed live from purchasing and receiving records at request time.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write).\n\nLedger tie-out: the response also states whether this figure agrees with the books. `ledger_accounts` lists the purchase clearing account(s) the report is compared against, as \"&lt;code> &lt;name>\" labels: Accrued Purchases, plus Goods Received Not Invoiced when that account is mapped (when it is not, receipts and supplier invoices net in Accrued Purchases alone). `ledger_balance` is the net debit balance of those accounts in SKU.io's ledger as of the same date, in the report's own sign (positive = invoiced ahead of receipts, negative = received ahead of invoices). `ledger_difference` is `unreceived` minus `ledger_balance`; a non-zero difference is clearing-account activity that belongs to no purchase order line (for example a landed-cost bill still awaiting its revaluation, or a manual journal). When no clearing account is mapped, `ledger_accounts` is an empty array and `ledger_balance` and `ledger_difference` are null.\n\n`adjustments` includes purchase-price true-ups: when a supplier invoice is priced differently from the receipt it covers, the resulting inventory revaluation's posting to the clearing account is counted here, so a purchase order that has already been trued up no longer shows as open."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/ginr/balance","host":["{{baseUrl}}"],"path":["api","reporting","ginr","balance"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"as_of_date\": \"2026-06-30\",\n    \"invoiced\": 148230.5,\n    \"received\": 121480.25,\n    \"discrepancy\": -350.75,\n    \"adjustments\": 1200,\n    \"unreceived\": 25199.5,\n    \"ledger_accounts\": [\n      \"207 Accrued Purchases\"\n    ],\n    \"ledger_balance\": 24899.5,\n    \"ledger_difference\": 300\n  }\n}"}]},{"name":"Get GINR Trend","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/ginr/trend","host":["{{baseUrl}}"],"path":["api","reporting","ginr","trend"]},"description":"GINR (Goods Invoiced, Not Received) — also called Accrued Purchases or GRNI — tracks the value of goods that suppliers have invoiced but that have not yet been received into inventory. The balance is calculated as: invoiced − received + discrepancies − adjustments, accumulated from the account's inventory start date (so it behaves like a balance-sheet figure, not a period movement).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the end-of-month unreceived (GINR) balance for the last N months — one point per month — suitable for rendering a sparkline or trend chart. The final point is the current in-flight month, measured as of today, and is flagged with `is_current: true`.\n\nData freshness: closed months are served from precomputed month-end snapshots that are refreshed nightly (the trailing three months are recomputed each night to absorb backdated entries). If a closed month has no snapshot yet, it is computed live as a fallback. The current month is always computed live.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/ginr/trend","host":["{{baseUrl}}"],"path":["api","reporting","ginr","trend"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"points\": [\n      {\n        \"period_end_date\": \"2026-02-28\",\n        \"unreceived\": 19875.1,\n        \"is_current\": false\n      },\n      {\n        \"period_end_date\": \"2026-03-31\",\n        \"unreceived\": 22140.85,\n        \"is_current\": false\n      },\n      {\n        \"period_end_date\": \"2026-04-30\",\n        \"unreceived\": 18320,\n        \"is_current\": false\n      },\n      {\n        \"period_end_date\": \"2026-05-31\",\n        \"unreceived\": 27410.6,\n        \"is_current\": false\n      },\n      {\n        \"period_end_date\": \"2026-06-30\",\n        \"unreceived\": 25199.5,\n        \"is_current\": false\n      },\n      {\n        \"period_end_date\": \"2026-07-31\",\n        \"unreceived\": 23847.25,\n        \"is_current\": true\n      }\n    ]\n  }\n}"}]},{"name":"Get GINR Breakdown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/ginr/breakdown","host":["{{baseUrl}}"],"path":["api","reporting","ginr","breakdown"]},"description":"GINR (Goods Invoiced, Not Received) — also called Accrued Purchases or GRNI — tracks the value of goods that suppliers have invoiced but that have not yet been received into inventory. The balance is calculated as: invoiced − received + discrepancies − adjustments, accumulated from the account's inventory start date (so it behaves like a balance-sheet figure, not a period movement).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the per-purchase-order breakdown of the GINR balance as of a given month's end (or an arbitrary date). Each row carries that PO's cumulative invoiced/received/discrepancy/adjustments values and resulting balance; the rows sum to the same balance reported by the balance endpoint for the same date. Rows are sorted by descending absolute balance. By default, settled and fully-received purchase orders are hidden to reduce noise. Figures are computed live at request time.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/ginr/breakdown","host":["{{baseUrl}}"],"path":["api","reporting","ginr","breakdown"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"as_of_date\": \"2026-06-30\",\n    \"items\": [\n      {\n        \"purchase_order_id\": 412,\n        \"purchase_order_number\": \"PO-2041\",\n        \"purchase_order_date\": \"2026-04-12 00:00:00\",\n        \"supplier_name\": \"Shenzhen Lumina Electronics\",\n        \"invoiced\": 42800,\n        \"received\": 21400,\n        \"discrepancy\": 0,\n        \"adjustments\": 0,\n        \"balance\": 21400,\n        \"is_fully_received\": false\n      },\n      {\n        \"purchase_order_id\": 448,\n        \"purchase_order_number\": \"PO-2087\",\n        \"purchase_order_date\": \"2026-05-03 00:00:00\",\n        \"supplier_name\": \"Pacific Coast Distributors\",\n        \"invoiced\": 6120,\n        \"received\": 2295,\n        \"discrepancy\": -75.5,\n        \"adjustments\": 0,\n        \"balance\": 3749.5,\n        \"is_fully_received\": false\n      },\n      {\n        \"purchase_order_id\": 395,\n        \"purchase_order_number\": \"PO-1998\",\n        \"purchase_order_date\": \"2026-03-20 00:00:00\",\n        \"supplier_name\": \"Nordic Textiles AB\",\n        \"invoiced\": 980,\n        \"received\": 890,\n        \"discrepancy\": 0,\n        \"adjustments\": 40,\n        \"balance\": 50,\n        \"is_fully_received\": false\n      }\n    ],\n    \"totals\": {\n      \"invoiced\": 49900,\n      \"received\": 24585,\n      \"discrepancy\": -75.5,\n      \"adjustments\": 40,\n      \"balance\": 25199.5\n    }\n  }\n}"}]},{"name":"Get GINR Aging","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/ginr/aging","host":["{{baseUrl}}"],"path":["api","reporting","ginr","aging"]},"description":"GINR (Goods Invoiced, Not Received) — also called Accrued Purchases or GRNI — tracks the value of goods that suppliers have invoiced but that have not yet been received into inventory. The balance is calculated as: invoiced − received + discrepancies − adjustments, accumulated from the account's inventory start date (so it behaves like a balance-sheet figure, not a period movement).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the aging breakdown of purchase orders that still carry a non-zero GINR balance as of the given date. Each PO's age is measured in days from its earliest activity (first supplier invoice or first receipt) to the as-of date, and POs are grouped into fixed buckets: 0-30, 31-60, 61-90, 91-180, 181-365, and 365+ days. Each bucket reports the PO count, the net balance, and the sum of absolute balances (so offsetting positive/negative balances remain visible). Figures are computed live at request time.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/ginr/aging","host":["{{baseUrl}}"],"path":["api","reporting","ginr","aging"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"as_of_date\": \"2026-06-30\",\n    \"buckets\": [\n      {\n        \"label\": \"0-30 days\",\n        \"min_days\": 0,\n        \"max_days\": 30,\n        \"po_count\": 4,\n        \"total_balance\": 8210.25,\n        \"absolute_balance\": 8210.25\n      },\n      {\n        \"label\": \"31-60 days\",\n        \"min_days\": 31,\n        \"max_days\": 60,\n        \"po_count\": 3,\n        \"total_balance\": 6485,\n        \"absolute_balance\": 6485\n      },\n      {\n        \"label\": \"61-90 days\",\n        \"min_days\": 61,\n        \"max_days\": 90,\n        \"po_count\": 2,\n        \"total_balance\": 5320.75,\n        \"absolute_balance\": 5320.75\n      },\n      {\n        \"label\": \"91-180 days\",\n        \"min_days\": 91,\n        \"max_days\": 180,\n        \"po_count\": 2,\n        \"total_balance\": 4890.5,\n        \"absolute_balance\": 4890.5\n      },\n      {\n        \"label\": \"181-365 days\",\n        \"min_days\": 181,\n        \"max_days\": 365,\n        \"po_count\": 1,\n        \"total_balance\": 293,\n        \"absolute_balance\": 293\n      },\n      {\n        \"label\": \"365+ days\",\n        \"min_days\": 366,\n        \"max_days\": null,\n        \"po_count\": 0,\n        \"total_balance\": 0,\n        \"absolute_balance\": 0\n      }\n    ],\n    \"totals\": {\n      \"po_count\": 12,\n      \"total_balance\": 25199.5,\n      \"absolute_balance\": 25199.5\n    }\n  }\n}"}]},{"name":"Get GINR Aging Drilldown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/ginr/aging/drilldown?min_days=91","host":["{{baseUrl}}"],"path":["api","reporting","ginr","aging","drilldown"],"query":[{"key":"min_days","value":"91","description":"Required. Minimum age in days (inclusive, integer ≥ 0) of the bucket to drill into.","disabled":true}]},"description":"GINR (Goods Invoiced, Not Received) — also called Accrued Purchases or GRNI — tracks the value of goods that suppliers have invoiced but that have not yet been received into inventory. The balance is calculated as: invoiced − received + discrepancies − adjustments, accumulated from the account's inventory start date (so it behaves like a balance-sheet figure, not a period movement).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nLists the purchase orders that fall into a specific aging bucket as of a given date — the drill-through for a bucket returned by the aging endpoint. Each row includes the PO's earliest activity date, its age in days, and the full per-metric breakdown (invoiced, received, discrepancy, adjustments, balance). Rows are sorted oldest first. Figures are computed live at request time.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/ginr/aging/drilldown?min_days=91","host":["{{baseUrl}}"],"path":["api","reporting","ginr","aging","drilldown"],"query":[{"key":"min_days","value":"91","description":"Required. Minimum age in days (inclusive, integer ≥ 0) of the bucket to drill into.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"as_of_date\": \"2026-06-30\",\n    \"bucket\": {\n      \"label\": \"91-180 days\",\n      \"min_days\": 91,\n      \"max_days\": 180\n    },\n    \"items\": [\n      {\n        \"purchase_order_id\": 361,\n        \"purchase_order_number\": \"PO-1912\",\n        \"supplier_name\": \"Nordic Textiles AB\",\n        \"earliest_activity_date\": \"2026-02-18 08:15:42\",\n        \"age_days\": 132,\n        \"invoiced\": 3200,\n        \"received\": 0,\n        \"discrepancy\": 0,\n        \"adjustments\": 0,\n        \"balance\": 3200\n      },\n      {\n        \"purchase_order_id\": 372,\n        \"purchase_order_number\": \"PO-1935\",\n        \"supplier_name\": \"Guangzhou Homeware Co\",\n        \"earliest_activity_date\": \"2026-03-08 11:40:03\",\n        \"age_days\": 114,\n        \"invoiced\": 2140.5,\n        \"received\": 450,\n        \"discrepancy\": 0,\n        \"adjustments\": 0,\n        \"balance\": 1690.5\n      }\n    ],\n    \"totals\": {\n      \"po_count\": 2,\n      \"total_balance\": 4890.5,\n      \"absolute_balance\": 4890.5\n    }\n  }\n}"}]},{"name":"Get GINR Drilldown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/ginr/drilldown?metric=invoiced","host":["{{baseUrl}}"],"path":["api","reporting","ginr","drilldown"],"query":[{"key":"metric","value":"invoiced","description":"Required. Which GINR component to list the backing records for. One of: `invoiced`, `received`, `discrepancy`, `adjustments`.","disabled":true}]},"description":"GINR (Goods Invoiced, Not Received) — also called Accrued Purchases or GRNI — tracks the value of goods that suppliers have invoiced but that have not yet been received into inventory. The balance is calculated as: invoiced − received + discrepancies − adjustments, accumulated from the account's inventory start date (so it behaves like a balance-sheet figure, not a period movement).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the raw backing records for one GINR component (metric) through a given date, optionally scoped to a single purchase order. Records cover the cumulative window from the account's inventory start date through the as-of date, so the sum of each item's `value` equals the matching aggregate figure from the balance/breakdown endpoints.\n\nThe fields of each item vary by metric:\n- `invoiced` — supplier invoice lines (invoice reference, quantities, unit price, discount).\n- `received` — receipt lines from both warehouse receipts and Amazon FBA inbound ledger events (`receipt_source` is `warehouse_receipt` or `fba_ledger`), with the cost proration inputs used to value each receipt.\n- `discrepancy` — receiving-discrepancy journal postings (credit/debit lines).\n- `adjustments` — PO-linked inventory adjustment movements with the cost layer used to value them, plus purchase-price true-ups (the revaluation posted to the purchase clearing account when a supplier invoice is priced differently from the receipt). A true-up row carries `inventory_revaluation_id`, the journal reference in `adjustment_notes` and the supplier invoice number in `adjustment_reason`; its `movement_id`, `inventory_adjustment_id`, `purchase_order_line_id`, `quantity` and `avg_cost` are null, and `value` is the amount posted to the clearing account (credit minus debit). Ordinary adjustment rows do not include `inventory_revaluation_id`.\n\nFigures are computed live at request time.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/ginr/drilldown?metric=invoiced","host":["{{baseUrl}}"],"path":["api","reporting","ginr","drilldown"],"query":[{"key":"metric","value":"invoiced","description":"Required. Which GINR component to list the backing records for. One of: `invoiced`, `received`, `discrepancy`, `adjustments`.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"metric\": \"invoiced\",\n    \"as_of_date\": \"2026-06-30\",\n    \"purchase_order_id\": null,\n    \"items\": [\n      {\n        \"invoice_line_id\": 5211,\n        \"purchase_invoice_id\": 1287,\n        \"invoice_reference\": \"INV-88213\",\n        \"date\": \"2026-04-15 00:00:00\",\n        \"purchase_order_id\": 412,\n        \"purchase_order_number\": \"PO-2041\",\n        \"supplier_name\": \"Shenzhen Lumina Electronics\",\n        \"purchase_order_line_id\": 3320,\n        \"product_sku\": \"LED-STRIP-5M-RGB\",\n        \"product_name\": \"5m LED Light Strip - RGB\",\n        \"quantity_invoiced\": 2000,\n        \"unit_price\": 21.4,\n        \"po_line_amount\": 21.4,\n        \"po_line_quantity\": 2000,\n        \"discount_rate\": 0,\n        \"value\": 42800\n      },\n      {\n        \"invoice_line_id\": 5304,\n        \"purchase_invoice_id\": 1301,\n        \"invoice_reference\": \"INV-88790\",\n        \"date\": \"2026-05-06 00:00:00\",\n        \"purchase_order_id\": 448,\n        \"purchase_order_number\": \"PO-2087\",\n        \"supplier_name\": \"Pacific Coast Distributors\",\n        \"purchase_order_line_id\": 3391,\n        \"product_sku\": \"GLOW-DESK-WAL\",\n        \"product_name\": \"Desk Glow Lamp - Walnut\",\n        \"quantity_invoiced\": 360,\n        \"unit_price\": 17,\n        \"po_line_amount\": 17,\n        \"po_line_quantity\": 360,\n        \"discount_rate\": 0,\n        \"value\": 6120\n      }\n    ],\n    \"total\": 48920\n  }\n}"}]},{"name":"Get GINR PO Receipts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/ginr/po-receipts?purchase_order_id=412","host":["{{baseUrl}}"],"path":["api","reporting","ginr","po-receipts"],"query":[{"key":"purchase_order_id","value":"412","description":"Required. The purchase order to list receipts for (must be an existing purchase order ID).","disabled":true}]},"description":"GINR (Goods Invoiced, Not Received) — also called Accrued Purchases or GRNI — tracks the value of goods that suppliers have invoiced but that have not yet been received into inventory. The balance is calculated as: invoiced − received + discrepancies − adjustments, accumulated from the account's inventory start date (so it behaves like a balance-sheet figure, not a period movement).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the full lifetime receiving history for a single purchase order, grouped by receipt date. Unlike the other GINR endpoints this is not bounded by an as-of date — it shows every receipt so you can see when and how much of the PO arrived. Each row includes the quantity and value received that day plus its share of the PO's total value and quantity. Both warehouse receipts and Amazon FBA inbound receipts are included. Figures are computed live at request time.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write). Receipts are grouped by the day they were received in the account's timezone."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/ginr/po-receipts?purchase_order_id=412","host":["{{baseUrl}}"],"path":["api","reporting","ginr","po-receipts"],"query":[{"key":"purchase_order_id","value":"412","description":"Required. The purchase order to list receipts for (must be an existing purchase order ID).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"purchase_order_id\": 412,\n    \"purchase_order_number\": \"PO-2041\",\n    \"supplier_name\": \"Shenzhen Lumina Electronics\",\n    \"po_total_value\": 42800,\n    \"po_total_quantity\": 2000,\n    \"items\": [\n      {\n        \"date\": \"2026-05-14\",\n        \"received_at\": \"2026-05-14 09:32:11\",\n        \"quantity_received\": 500,\n        \"value\": 10700,\n        \"percent_of_po_value\": 25,\n        \"percent_of_po_quantity\": 25\n      },\n      {\n        \"date\": \"2026-06-02\",\n        \"received_at\": \"2026-06-02 00:00:00\",\n        \"quantity_received\": 500,\n        \"value\": 10700,\n        \"percent_of_po_value\": 25,\n        \"percent_of_po_quantity\": 25\n      }\n    ],\n    \"total_value\": 21400,\n    \"total_quantity\": 1000\n  }\n}"}]},{"name":"Get Accounting Income Statement","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/accounting/income-statement?period=month","host":["{{baseUrl}}"],"path":["api","reporting","accounting","income-statement"],"query":[{"key":"period","value":"month","description":"Period grouping: month | year","disabled":true}]},"description":"Returns an income statement grouped by accounting nominal codes and period. Supports date filtering and period granularity.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/accounting/income-statement?period=month","host":["{{baseUrl}}"],"path":["api","reporting","accounting","income-statement"],"query":[{"key":"period","value":"month","description":"Period grouping: month | year","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"nominal_code_id\": 10,\n      \"nominal_code\": \"4000\",\n      \"nominal_code_name\": \"Sales Revenue\",\n      \"nominal_code_type\": \"income\",\n      \"period_date\": \"2024-06\",\n      \"total\": 45200\n    }\n  ]\n}"}]},{"name":"Get Accounting Balance Sheet","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/accounting/balance-sheet","host":["{{baseUrl}}"],"path":["api","reporting","accounting","balance-sheet"]},"description":"Returns a balance sheet snapshot as of a date, built from the same accounting_transaction_lines data source as the income statement so the two reconcile.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAccounts are grouped into assets (reported as net debit balance), liabilities and equity (reported as net credit balance). Cumulative net income is appended to equity as a computed \"Retained Earnings\" line so the statement balances (Assets = Liabilities + Equity).\n\nAlso scoped by scope by object:reports`.\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/accounting/balance-sheet","host":["{{baseUrl}}"],"path":["api","reporting","accounting","balance-sheet"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"as_of_date\": \"2024-06-30\",\n    \"assets\": [\n      {\n        \"nominal_code_id\": 1,\n        \"nominal_code\": \"1000\",\n        \"nominal_code_name\": \"Bank\",\n        \"nominal_code_type\": \"Bank\",\n        \"balance\": 1000,\n        \"is_computed\": false\n      },\n      {\n        \"nominal_code_id\": 2,\n        \"nominal_code\": \"1200\",\n        \"nominal_code_name\": \"Inventory\",\n        \"nominal_code_type\": \"Inventory\",\n        \"balance\": 200,\n        \"is_computed\": false\n      }\n    ],\n    \"liabilities\": [\n      {\n        \"nominal_code_id\": 3,\n        \"nominal_code\": \"2100\",\n        \"nominal_code_name\": \"Accounts Payable\",\n        \"nominal_code_type\": \"Currliab\",\n        \"balance\": 600,\n        \"is_computed\": false\n      }\n    ],\n    \"equity\": [\n      {\n        \"nominal_code_id\": null,\n        \"nominal_code\": null,\n        \"nominal_code_name\": \"Retained Earnings\",\n        \"nominal_code_type\": \"Equity\",\n        \"balance\": 600,\n        \"is_computed\": true\n      }\n    ],\n    \"totals\": {\n      \"assets\": 1200,\n      \"liabilities\": 600,\n      \"equity\": 600,\n      \"liabilities_and_equity\": 1200,\n      \"retained_earnings\": 600,\n      \"difference\": 0,\n      \"is_balanced\": true\n    }\n  }\n}"}]},{"name":"Get Sales Channel Coverage","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/sales-channel-coverage/paginated?page=1&per_page=25&by_integration=1&show_count=0","host":["{{baseUrl}}"],"path":["api","reporting","sales-channel-coverage","paginated"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"per_page","value":"25","description":"Items per page (default: 25)","disabled":true},{"key":"by_integration","value":"1","description":"1 = group by integration, 0 = group by instance (default: 1)","disabled":true},{"key":"show_count","value":"0","description":"1 = show coverage count, 0 = show boolean (default: 0)","disabled":true}]},"description":"Returns paginated sales channel coverage data per product using Spatie QueryBuilder. Shows which products are listed on which sales channels.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nFilters: filter[search] (matches SKU OR supplier name), filter[product_types] (comma-separated: standard|blemished|kit), filter[integration_ids] (comma-separated), filter[instance_ids] (comma-separated)\n\nFlags:\n- by_integration (1/0) — group coverage by integration vs instance\n- show_count (1/0) — return numeric coverage counts vs boolean 0/1\n- in_stock_only (1/0) — restrict to products with on-hand stock at warehouse 0 (default 1)\n\nSort: sku, type, id"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/sales-channel-coverage/paginated?page=1&per_page=25&by_integration=1&show_count=0","host":["{{baseUrl}}"],"path":["api","reporting","sales-channel-coverage","paginated"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"per_page","value":"25","description":"Items per page (default: 25)","disabled":true},{"key":"by_integration","value":"1","description":"1 = group by integration, 0 = group by instance (default: 1)","disabled":true},{"key":"show_count","value":"0","description":"1 = show coverage count, 0 = show boolean (default: 0)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 42,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Blue Widget\",\n      \"supplier\": {\n        \"name\": \"Widget Co\"\n      },\n      \"type\": \"standard\",\n      \"stock\": 150,\n      \"valuation\": 1387.5,\n      \"coverage\": {\n        \"Shopify\": 1,\n        \"Amazon\": 0,\n        \"WooCommerce\": 1\n      }\n    }\n  ],\n  \"per_page\": 25,\n  \"total\": 342\n}"}]},{"name":"Get Full Sales Channel Coverage","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/reporting/sales-channel-coverage","host":["{{baseUrl}}"],"path":["api","reporting","sales-channel-coverage"]},"description":"Returns listing coverage for every product across the selected sales channels in a single unpaginated response. For each product you get its stock on hand, inventory valuation, default supplier, and a coverage map keyed by channel name plus a Total.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest fields (all required):\n- `byIntegration` (boolean) — true groups coverage by integration (e.g. all Amazon instances combined); false groups by individual integration instance.\n- `showNumberOfCoverages` (boolean) — true returns the count of active listings per channel; false returns a binary indicator (1 = listed, 0 = not listed).\n- `ids` (array of integers) — the integration IDs (when `byIntegration` is true) or integration instance IDs (when false) to include in the coverage calculation.\n\nCoverage for kit/bundle products includes listings of their parent products. This endpoint computes coverage for the entire catalog in one request and can be slow on large catalogs — prefer `GET /api/reporting/sales-channel-coverage/paginated` for interactive use.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write).","body":{"mode":"raw","raw":"{\n  \"byIntegration\": true,\n  \"showNumberOfCoverages\": false,\n  \"ids\": [\n    1,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/reporting/sales-channel-coverage","host":["{{baseUrl}}"],"path":["api","reporting","sales-channel-coverage"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 101,\n      \"sku\": \"TSHIRT-BLK-M\",\n      \"type\": \"standard\",\n      \"stock\": 240,\n      \"valuation\": 1820.5,\n      \"supplier\": {\n        \"id\": 3,\n        \"name\": \"Acme Apparel Co\"\n      },\n      \"coverage\": {\n        \"Amazon\": 1,\n        \"Shopify\": 1,\n        \"Total\": 2\n      }\n    },\n    {\n      \"id\": 102,\n      \"sku\": \"TSHIRT-BLK-L\",\n      \"type\": \"standard\",\n      \"stock\": 0,\n      \"valuation\": 0,\n      \"supplier\": {\n        \"id\": 3,\n        \"name\": \"Acme Apparel Co\"\n      },\n      \"coverage\": {\n        \"Amazon\": 0,\n        \"Shopify\": 1,\n        \"Total\": 1\n      }\n    }\n  ]\n}"}]},{"name":"Export Sales Channel Coverage (Async)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/sales-channel-coverage/export?by_integration=1&show_count=0&in_stock_only=1","host":["{{baseUrl}}"],"path":["api","reporting","sales-channel-coverage","export"],"query":[{"key":"by_integration","value":"1","description":"1 = group by integration, 0 = group by instance (default: 1)","disabled":true},{"key":"show_count","value":"0","description":"1 = show coverage count, 0 = show boolean (default: 0)","disabled":true},{"key":"in_stock_only","value":"1","description":"1 = only include products with stock at warehouse 0 (default), 0 = include all products","disabled":true}]},"description":"Dispatches an async job (a background job) that builds the full unpaginated sales channel coverage CSV in the background. Returns immediately with a tracked_job_log_id that the frontend uses to poll progress via the tracked job log endpoints.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nFlow:\n1. Client calls this endpoint with the same filters/flags supported by the paginated endpoint.\n2. Server queues the job and returns { data: { tracked_job_log_id }, message }.\n3. Frontend subscribes via the tracked job log endpoints; when job completes, log.results.file contains the filename on the model-exports disk.\n4. Client downloads the file via GET /api/export/download?file={filename}.\n\nCSV columns: SKU, Product Name, Supplier, Type, Stock, Valuation, Total Coverage, plus one column per integration (or instance when by_integration=0) covering the union of channels across all returned rows. Channel columns are computed from the union of all rows in the result set."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/sales-channel-coverage/export?by_integration=1&show_count=0&in_stock_only=1","host":["{{baseUrl}}"],"path":["api","reporting","sales-channel-coverage","export"],"query":[{"key":"by_integration","value":"1","description":"1 = group by integration, 0 = group by instance (default: 1)","disabled":true},{"key":"show_count","value":"0","description":"1 = show coverage count, 0 = show boolean (default: 0)","disabled":true},{"key":"in_stock_only","value":"1","description":"1 = only include products with stock at warehouse 0 (default), 0 = include all products","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 1234\n  },\n  \"message\": \"Export started\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Release Schedule","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/reporting/release-schedule","host":["{{baseUrl}}"],"path":["api","v2","reporting","release-schedule"]},"description":"Forward-looking release schedule for held sales orders. Returns held orders (those with a future hold_until_date — a \"do not ship before\" release date, used for preorders and scheduled holds) grouped by the application-timezone date on which they become shippable.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nOrders are excluded if they have no hold, the hold has already passed, are cancelled (canceled_at set), or are closed. Buckets are ordered by hold_until_date ascending.\n\nThis differs from Ship By Schedule: ship_by_date is a deadline (\"ship no later than\"), whereas hold_until_date is a floor (\"do not ship before\").\n\nAuthentication: Requires Bearer token. Scope: reports (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/reporting/release-schedule","host":["{{baseUrl}}"],"path":["api","v2","reporting","release-schedule"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"hold_until_date\": \"2026-05-23\",\n      \"order_count\": 2,\n      \"orders\": [\n        {\n          \"id\": 10432,\n          \"order_number\": \"SO-10432\",\n          \"channel\": \"Acme Shopify Store\",\n          \"order_status\": \"reserved\",\n          \"hold_until_date\": \"2026-05-23T00:00:00+00:00\",\n          \"ship_by_date\": \"2026-05-25T00:00:00+00:00\",\n          \"item_count\": 3\n        },\n        {\n          \"id\": 10455,\n          \"order_number\": \"SO-10455\",\n          \"channel\": \"Acme Shopify Store\",\n          \"order_status\": \"reserved\",\n          \"hold_until_date\": \"2026-05-23T00:00:00+00:00\",\n          \"ship_by_date\": \"2026-05-26T00:00:00+00:00\",\n          \"item_count\": 1\n        }\n      ]\n    },\n    {\n      \"hold_until_date\": \"2026-06-01\",\n      \"order_count\": 1,\n      \"orders\": [\n        {\n          \"id\": 10501,\n          \"order_number\": \"SO-10501\",\n          \"channel\": \"Acme Shopify Store\",\n          \"order_status\": \"reserved\",\n          \"hold_until_date\": \"2026-06-01T00:00:00+00:00\",\n          \"ship_by_date\": \"2026-06-03T00:00:00+00:00\",\n          \"item_count\": 5\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"Get Supplier Overview","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/suppliers/overview?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","suppliers","overview"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}]},"description":"Macro supplier dashboard for the resolved period: headline KPIs (purchase volume, sales volume, profit, active suppliers), the hottest/coldest movers, a 12-month growth heatmap, and the full leaderboard. KPIs and trends are read from monthly supplier metric snapshots.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nRequired parameters:\n- period_mode: yoy | qoq | mom | custom\n\nWhen period_mode=custom, date_from and date_to (YYYY-MM-DD or m/d/Y) are both required and date_to must be on or after date_from.\n\nOptional:\n- growth_basis: spend | sales | profit | margin (default: sales) — the basis used to rank movers and the heatmap\n- min_volume: numeric — minimum purchase volume before a supplier qualifies as a mover (default: 1000)\n- include_archived: boolean — include archived suppliers (default: false)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/suppliers/overview?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","suppliers","overview"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"kpis\": {\n      \"purchase_volume\": {\n        \"current\": 1842500.5,\n        \"previous\": 1640200,\n        \"delta_pct\": 12.3\n      },\n      \"sales_volume\": {\n        \"current\": 3120900,\n        \"previous\": 2890400,\n        \"delta_pct\": 8\n      },\n      \"profit\": {\n        \"current\": 961000.25,\n        \"previous\": 905100,\n        \"delta_pct\": 6.2\n      },\n      \"active_suppliers\": {\n        \"current\": 48,\n        \"previous\": 45,\n        \"delta_pct\": 6.7\n      }\n    },\n    \"hottest\": [\n      {\n        \"supplier_id\": 42,\n        \"name\": \"Acme Components Ltd\",\n        \"value\": 184250.5,\n        \"delta_pct\": 31.5,\n        \"spark\": [\n          12000,\n          13400,\n          15600,\n          14200,\n          16800,\n          17100,\n          18900,\n          16400,\n          19200,\n          21000,\n          20400,\n          22300\n        ]\n      }\n    ],\n    \"coldest\": [\n      {\n        \"supplier_id\": 71,\n        \"name\": \"Borealis Supply Co\",\n        \"value\": 22400,\n        \"delta_pct\": -18.7,\n        \"spark\": [\n          9100,\n          8700,\n          8200,\n          7600,\n          6900,\n          6400,\n          5800,\n          5100,\n          4700,\n          4200,\n          3900,\n          3400\n        ]\n      }\n    ],\n    \"heatmap\": {\n      \"columns\": [\n        \"2025-06\",\n        \"2025-07\",\n        \"2025-08\",\n        \"2025-09\",\n        \"2025-10\",\n        \"2025-11\",\n        \"2025-12\",\n        \"2026-01\",\n        \"2026-02\",\n        \"2026-03\",\n        \"2026-04\",\n        \"2026-05\"\n      ],\n      \"rows\": [\n        {\n          \"supplier_id\": 42,\n          \"name\": \"Acme Components Ltd\",\n          \"cells\": [\n            null,\n            8.2,\n            12.4,\n            -3.1,\n            6.7,\n            9,\n            14.2,\n            -2.4,\n            5.5,\n            11,\n            7.8,\n            12.4\n          ]\n        }\n      ]\n    },\n    \"leaderboard\": [\n      {\n        \"supplier_id\": 42,\n        \"name\": \"Acme Components Ltd\",\n        \"purchase_volume\": 184250.5,\n        \"sales_volume\": 312900,\n        \"units_purchased\": 8400,\n        \"units_sold\": 7950,\n        \"profit\": 96100.25,\n        \"margin_pct\": 30.7,\n        \"growth_pct\": 12.4,\n        \"active_po_count\": 23,\n        \"po_count\": 21,\n        \"is_low_volume\": false\n      }\n    ],\n    \"meta\": {\n      \"data_as_of\": \"2026-05-14T03:12:08+00:00\",\n      \"period_label\": \"YoY — May 2025 – May 2026 vs May 2024 – May 2025\",\n      \"current_range\": [\n        \"2025-05-16\",\n        \"2026-05-15\"\n      ],\n      \"previous_range\": [\n        \"2024-05-16\",\n        \"2025-05-15\"\n      ],\n      \"is_partial\": false,\n      \"is_live_query\": false\n    }\n  }\n}"}]},{"name":"Get Supplier Leaderboard","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/suppliers/leaderboard?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","suppliers","leaderboard"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}]},"description":"Leaderboard rows only — a lightweight endpoint for table page/sort changes on the macro supplier view, without recomputing KPIs, movers, or the heatmap. Rows are computed in-memory from snapshots, then filtered, sorted and sliced; low-volume suppliers are flagged via is_low_volume.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nServer-paginated (PRD edge case 15: 500+ suppliers) — returns the standard Laravel pagination envelope (data, current_page, last_page, per_page, total, etc.).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nRequired parameters:\n- period_mode: yoy | qoq | mom | custom\n\nWhen period_mode=custom, date_from and date_to (YYYY-MM-DD or m/d/Y) are both required.\n\nOptional:\n- growth_basis: spend | sales | profit | margin (default: sales)\n- min_volume: numeric — low-volume threshold (default: 1000)\n- include_archived: boolean (default: false)\n- sort: sort key, prefix - for DESC. Allowed: name, purchase_volume, sales_volume, units_purchased, units_sold, profit, margin_pct, growth_pct, active_po_count (default: -purchase_volume)\n- page: page number (default: 1)\n- per_page: rows per page (default: 10)\n- filter[search]: substring match on supplier name\n- filter[volume]: above | low — volume band filter"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/suppliers/leaderboard?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","suppliers","leaderboard"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"supplier_id\": 42,\n      \"name\": \"Acme Components Ltd\",\n      \"purchase_volume\": 184250.5,\n      \"sales_volume\": 312900,\n      \"units_purchased\": 8400,\n      \"units_sold\": 7950,\n      \"profit\": 96100.25,\n      \"margin_pct\": 30.7,\n      \"growth_pct\": 12.4,\n      \"active_po_count\": 23,\n      \"po_count\": 21,\n      \"is_low_volume\": false\n    },\n    {\n      \"supplier_id\": 71,\n      \"name\": \"Borealis Supply Co\",\n      \"purchase_volume\": 22400,\n      \"sales_volume\": 41200,\n      \"units_purchased\": 980,\n      \"units_sold\": 910,\n      \"profit\": 9100.5,\n      \"margin_pct\": 22.1,\n      \"growth_pct\": -18.7,\n      \"active_po_count\": 4,\n      \"po_count\": 5,\n      \"is_low_volume\": false\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/reporting/suppliers/leaderboard?page=1\",\n  \"from\": 1,\n  \"last_page\": 5,\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/reporting/suppliers/leaderboard?page=5\",\n  \"next_page_url\": \"{{protocol}}{{domain}}/api/reporting/suppliers/leaderboard?page=2\",\n  \"path\": \"{{protocol}}{{domain}}/api/reporting/suppliers/leaderboard\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 10,\n  \"total\": 48\n}"}]},{"name":"Rebuild Supplier Metric Snapshots","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/reporting/suppliers/rebuild-snapshots","host":["{{baseUrl}}"],"path":["api","reporting","suppliers","rebuild-snapshots"]},"description":"Dispatches the supplier-metric snapshot rebuild as a tracked background job and returns its tracked job log ID for progress polling via the tracked job log endpoints. Rejects a second dispatch with 409 while a rebuild is already queued or running.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nThis endpoint takes no request body.\n\nNote: no request body is required.","body":{"mode":"raw","raw":"","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/reporting/suppliers/rebuild-snapshots","host":["{{baseUrl}}"],"path":["api","reporting","suppliers","rebuild-snapshots"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 90871\n  },\n  \"message\": \"Supplier metric rebuild started.\"\n}"}]},{"name":"Get Supplier Scorecard Settings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/suppliers/scorecard-settings","host":["{{baseUrl}}"],"path":["api","reporting","suppliers","scorecard-settings"]},"description":"Returns the tenant-global supplier scorecard configuration: the six dimension weights (sum to 100), the ordered {min, score} threshold bands per dimension, and the minimum in-period PO count required before a supplier is scored. A default row is created on first access if none exists.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/suppliers/scorecard-settings","host":["{{baseUrl}}"],"path":["api","reporting","suppliers","scorecard-settings"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"weights\": {\n      \"on_time_delivery\": 25,\n      \"lead_time_reliability\": 15,\n      \"fill_rate\": 15,\n      \"defect_rate\": 15,\n      \"cost_competitiveness\": 25,\n      \"price_stability\": 5\n    },\n    \"thresholds\": {\n      \"on_time_delivery\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"lead_time_reliability\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"fill_rate\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"defect_rate\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"cost_competitiveness\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"price_stability\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ]\n    },\n    \"min_po_count\": 3,\n    \"created_at\": \"2026-01-04T09:00:00.000000Z\",\n    \"updated_at\": \"2026-05-10T14:22:31.000000Z\"\n  }\n}"}]},{"name":"Update Supplier Scorecard Settings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/reporting/suppliers/scorecard-settings","host":["{{baseUrl}}"],"path":["api","reporting","suppliers","scorecard-settings"]},"description":"Updates the tenant-global supplier scorecard configuration. The six weights must each be present and sum to exactly 100; every threshold band needs a numeric min and an integer 1-5 score.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nRequest body fields:\n- weights: object (required) — must contain all six metric keys; values numeric 0-100, summing to exactly 100\n- weights.on_time_delivery: numeric, 0-100 (required)\n- weights.lead_time_reliability: numeric, 0-100 (required)\n- weights.fill_rate: numeric, 0-100 (required)\n- weights.defect_rate: numeric, 0-100 (required)\n- weights.cost_competitiveness: numeric, 0-100 (required)\n- weights.price_stability: numeric, 0-100 (required)\n- thresholds: object (required) — must contain all six metric keys\n- thresholds.{metric}: array, min 1 item (required) — ordered {min, score} bands\n- thresholds.{metric}.*.min: numeric (required) — lower bound of the band\n- thresholds.{metric}.*.score: integer 1-5 (required) — score awarded for the band\n- min_po_count: integer, min 1 (required)","body":{"mode":"raw","raw":"{\n  \"weights\": {\n    \"on_time_delivery\": 25,\n    \"lead_time_reliability\": 15,\n    \"fill_rate\": 15,\n    \"defect_rate\": 15,\n    \"cost_competitiveness\": 25,\n    \"price_stability\": 5\n  },\n  \"thresholds\": {\n    \"on_time_delivery\": [\n      {\n        \"min\": 97,\n        \"score\": 5\n      },\n      {\n        \"min\": 90,\n        \"score\": 4\n      },\n      {\n        \"min\": 80,\n        \"score\": 3\n      },\n      {\n        \"min\": 60,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ],\n    \"lead_time_reliability\": [\n      {\n        \"min\": 97,\n        \"score\": 5\n      },\n      {\n        \"min\": 90,\n        \"score\": 4\n      },\n      {\n        \"min\": 80,\n        \"score\": 3\n      },\n      {\n        \"min\": 60,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ],\n    \"fill_rate\": [\n      {\n        \"min\": 97,\n        \"score\": 5\n      },\n      {\n        \"min\": 90,\n        \"score\": 4\n      },\n      {\n        \"min\": 80,\n        \"score\": 3\n      },\n      {\n        \"min\": 60,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ],\n    \"defect_rate\": [\n      {\n        \"min\": 97,\n        \"score\": 5\n      },\n      {\n        \"min\": 90,\n        \"score\": 4\n      },\n      {\n        \"min\": 80,\n        \"score\": 3\n      },\n      {\n        \"min\": 60,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ],\n    \"cost_competitiveness\": [\n      {\n        \"min\": 97,\n        \"score\": 5\n      },\n      {\n        \"min\": 90,\n        \"score\": 4\n      },\n      {\n        \"min\": 80,\n        \"score\": 3\n      },\n      {\n        \"min\": 60,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ],\n    \"price_stability\": [\n      {\n        \"min\": 97,\n        \"score\": 5\n      },\n      {\n        \"min\": 90,\n        \"score\": 4\n      },\n      {\n        \"min\": 80,\n        \"score\": 3\n      },\n      {\n        \"min\": 60,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ]\n  },\n  \"min_po_count\": 3\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/reporting/suppliers/scorecard-settings","host":["{{baseUrl}}"],"path":["api","reporting","suppliers","scorecard-settings"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"weights\": {\n      \"on_time_delivery\": 25,\n      \"lead_time_reliability\": 15,\n      \"fill_rate\": 15,\n      \"defect_rate\": 15,\n      \"cost_competitiveness\": 25,\n      \"price_stability\": 5\n    },\n    \"thresholds\": {\n      \"on_time_delivery\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"lead_time_reliability\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"fill_rate\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"defect_rate\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"cost_competitiveness\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"price_stability\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ]\n    },\n    \"min_po_count\": 3,\n    \"created_at\": \"2026-01-04T09:00:00.000000Z\",\n    \"updated_at\": \"2026-05-10T14:22:31.000000Z\"\n  }\n}"}]},{"name":"Get Supplier Scorecard Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/suppliers/{supplier}/scorecard-summary","host":["{{baseUrl}}"],"path":["api","reporting","suppliers",":supplier","scorecard-summary"],"variable":[{"key":"supplier","value":"","description":"The supplier ID."}]},"description":"Lightweight scorecard + volume sparklines for the supplier Profile chip. Returns the composite scorecard with its dimension breakdown, a directional composite trend hint (up | down | flat), and trailing-12-month purchase/sales volume sparklines. The period is fixed to YoY.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameters:\n- supplier: the Supplier ID"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/suppliers/{supplier}/scorecard-summary","host":["{{baseUrl}}"],"path":["api","reporting","suppliers",":supplier","scorecard-summary"],"variable":[{"key":"supplier","value":"","description":"The supplier ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"supplier\": {\n      \"id\": 42,\n      \"name\": \"Acme Components Ltd\"\n    },\n    \"scorecard\": {\n      \"composite\": 4.15,\n      \"band\": \"good\",\n      \"is_sufficient\": true,\n      \"po_count\": 18,\n      \"dimensions\": [\n        {\n          \"key\": \"on_time_delivery\",\n          \"label\": \"On-Time Delivery\",\n          \"raw\": 92.3,\n          \"raw_display\": \"92.3%\",\n          \"score\": 4,\n          \"weight\": 25\n        },\n        {\n          \"key\": \"lead_time_reliability\",\n          \"label\": \"Lead-Time Reliability\",\n          \"raw\": 88,\n          \"raw_display\": \"88.0%\",\n          \"score\": 3,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"fill_rate\",\n          \"label\": \"Fill Rate\",\n          \"raw\": 98.6,\n          \"raw_display\": \"98.6%\",\n          \"score\": 5,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"defect_rate\",\n          \"label\": \"Defect / RTV Rate\",\n          \"raw\": 1.2,\n          \"raw_display\": \"1.2%\",\n          \"score\": 5,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"cost_competitiveness\",\n          \"label\": \"Cost Competitiveness\",\n          \"raw\": 74.5,\n          \"raw_display\": \"74.5 / 100\",\n          \"score\": 3,\n          \"weight\": 25\n        },\n        {\n          \"key\": \"price_stability\",\n          \"label\": \"Price Stability\",\n          \"raw\": 0.18,\n          \"raw_display\": \"18% price increases\",\n          \"score\": 4,\n          \"weight\": 5\n        }\n      ]\n    },\n    \"composite_trend\": \"up\",\n    \"purchase_volume\": {\n      \"current\": 184250.5,\n      \"previous\": 158900,\n      \"delta_pct\": 16,\n      \"spark\": [\n        12000,\n        13400,\n        15600,\n        14200,\n        16800,\n        17100,\n        18900,\n        16400,\n        19200,\n        21000,\n        20400,\n        22300\n      ]\n    },\n    \"sales_volume\": {\n      \"current\": 312900,\n      \"previous\": 289400,\n      \"delta_pct\": 8.1,\n      \"spark\": [\n        22000,\n        23100,\n        24800,\n        25600,\n        26100,\n        27400,\n        26900,\n        28200,\n        29400,\n        30100,\n        31200,\n        32400\n      ]\n    },\n    \"meta\": {\n      \"data_as_of\": \"2026-05-14T03:12:08+00:00\",\n      \"period_label\": \"YoY — May 2025 – May 2026 vs May 2024 – May 2025\",\n      \"current_range\": [\n        \"2025-05-16\",\n        \"2026-05-15\"\n      ],\n      \"previous_range\": [\n        \"2024-05-16\",\n        \"2025-05-15\"\n      ],\n      \"is_partial\": false,\n      \"is_live_query\": false\n    }\n  }\n}"}]},{"name":"Get Supplier Analytics","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/suppliers/{supplier}/analytics?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","suppliers",":supplier","analytics"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"supplier","value":"","description":"The supplier ID."}]},"description":"Full per-supplier analytics for the Analytics tab: headline KPIs (purchase volume, sales volume, units purchased/sold, profit, margin %), the composite scorecard, a dual-line current-vs-previous trend chart, and the hottest/coolest products ranked by growth.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameters:\n- supplier: the Supplier ID\n\nRequired parameters:\n- period_mode: yoy | qoq | mom | custom\n\nWhen period_mode=custom, date_from and date_to (YYYY-MM-DD or m/d/Y) are both required.\n\nOptional:\n- growth_basis: spend | sales | profit | margin (default: sales) — drives the trend metric and product mover ranking\n- min_volume: numeric (default: 1000)\n- include_archived: boolean (default: false)\n\nThe response also includes `product_heatmap` (a period-aware per-product growth heatmap — the supplier's top products by volume, each cell a period-over-period change that follows the `period_mode` selector) and `calendar_year_volume` (per calendar year for the trailing 5 years: non-draft PO spend with units/PO counts plus non-draft sales revenue with units/order counts; the current year is year-to-date and is independent of `period_mode`)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/suppliers/{supplier}/analytics?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","suppliers",":supplier","analytics"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"supplier","value":"","description":"The supplier ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"supplier\": {\n      \"id\": 42,\n      \"name\": \"Acme Components Ltd\"\n    },\n    \"kpis\": {\n      \"purchase_volume\": {\n        \"current\": 184250.5,\n        \"previous\": 158900,\n        \"delta_pct\": 16\n      },\n      \"sales_volume\": {\n        \"current\": 312900,\n        \"previous\": 289400,\n        \"delta_pct\": 8.1\n      },\n      \"units_purchased\": {\n        \"current\": 8400,\n        \"previous\": 7600,\n        \"delta_pct\": 10.5\n      },\n      \"units_sold\": {\n        \"current\": 7950,\n        \"previous\": 7400,\n        \"delta_pct\": 7.4\n      },\n      \"profit\": {\n        \"current\": 96100.25,\n        \"previous\": 88200,\n        \"delta_pct\": 9\n      },\n      \"margin_pct\": {\n        \"current\": 30.7,\n        \"previous\": 30.5,\n        \"delta_pct\": 0.2\n      }\n    },\n    \"scorecard\": {\n      \"composite\": 4.15,\n      \"band\": \"good\",\n      \"is_sufficient\": true,\n      \"po_count\": 18,\n      \"dimensions\": [\n        {\n          \"key\": \"on_time_delivery\",\n          \"label\": \"On-Time Delivery\",\n          \"raw\": 92.3,\n          \"raw_display\": \"92.3%\",\n          \"score\": 4,\n          \"weight\": 25\n        },\n        {\n          \"key\": \"lead_time_reliability\",\n          \"label\": \"Lead-Time Reliability\",\n          \"raw\": 88,\n          \"raw_display\": \"88.0%\",\n          \"score\": 3,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"fill_rate\",\n          \"label\": \"Fill Rate\",\n          \"raw\": 98.6,\n          \"raw_display\": \"98.6%\",\n          \"score\": 5,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"defect_rate\",\n          \"label\": \"Defect / RTV Rate\",\n          \"raw\": 1.2,\n          \"raw_display\": \"1.2%\",\n          \"score\": 5,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"cost_competitiveness\",\n          \"label\": \"Cost Competitiveness\",\n          \"raw\": 74.5,\n          \"raw_display\": \"74.5 / 100\",\n          \"score\": 3,\n          \"weight\": 25\n        },\n        {\n          \"key\": \"price_stability\",\n          \"label\": \"Price Stability\",\n          \"raw\": 0.18,\n          \"raw_display\": \"18% price increases\",\n          \"score\": 4,\n          \"weight\": 5\n        }\n      ]\n    },\n    \"trend\": {\n      \"metric\": \"sales\",\n      \"buckets\": [\n        \"2025-05\",\n        \"2025-06\",\n        \"2025-07\",\n        \"2025-08\",\n        \"2025-09\",\n        \"2025-10\",\n        \"2025-11\",\n        \"2025-12\",\n        \"2026-01\",\n        \"2026-02\",\n        \"2026-03\",\n        \"2026-04\"\n      ],\n      \"current\": [\n        14200,\n        15600,\n        16800,\n        17100,\n        18900,\n        16400,\n        19200,\n        21000,\n        20400,\n        22300,\n        21800,\n        23400\n      ],\n      \"previous\": [\n        12100,\n        13400,\n        14000,\n        14900,\n        15600,\n        14100,\n        16800,\n        18200,\n        17900,\n        19400,\n        18800,\n        20100\n      ]\n    },\n    \"hottest_products\": [\n      {\n        \"product_id\": 9001,\n        \"sku\": \"ACM-WIDGET-01\",\n        \"name\": \"Widget Assembly Type A\",\n        \"value\": 42100,\n        \"delta_pct\": 34.2\n      }\n    ],\n    \"coolest_products\": [\n      {\n        \"product_id\": 9044,\n        \"sku\": \"ACM-BRACKET-12\",\n        \"name\": \"Mounting Bracket XL\",\n        \"value\": 6200,\n        \"delta_pct\": -21.8\n      }\n    ],\n    \"product_heatmap\": {\n      \"columns\": [\n        \"2025-05\",\n        \"2025-06\",\n        \"2025-07\",\n        \"2025-08\",\n        \"2025-09\",\n        \"2025-10\",\n        \"2025-11\",\n        \"2025-12\",\n        \"2026-01\",\n        \"2026-02\",\n        \"2026-03\",\n        \"2026-04\"\n      ],\n      \"rows\": [\n        {\n          \"product_id\": 9001,\n          \"sku\": \"ACM-WIDGET-01\",\n          \"name\": \"Widget Assembly Type A\",\n          \"cells\": [\n            {\n              \"delta\": null,\n              \"current_value\": 11800,\n              \"previous_value\": null,\n              \"compare_period\": \"2024-05\"\n            },\n            {\n              \"delta\": 12.4,\n              \"current_value\": 13300,\n              \"previous_value\": 11830,\n              \"compare_period\": \"2024-06\"\n            },\n            {\n              \"delta\": 8.1,\n              \"current_value\": 14380,\n              \"previous_value\": 13300,\n              \"compare_period\": \"2024-07\"\n            },\n            {\n              \"delta\": -3.2,\n              \"current_value\": 13920,\n              \"previous_value\": 14380,\n              \"compare_period\": \"2024-08\"\n            },\n            {\n              \"delta\": 19.5,\n              \"current_value\": 16640,\n              \"previous_value\": 13920,\n              \"compare_period\": \"2024-09\"\n            },\n            {\n              \"delta\": -6.7,\n              \"current_value\": 15520,\n              \"previous_value\": 16640,\n              \"compare_period\": \"2024-10\"\n            },\n            {\n              \"delta\": 22,\n              \"current_value\": 18940,\n              \"previous_value\": 15520,\n              \"compare_period\": \"2024-11\"\n            },\n            {\n              \"delta\": 9.3,\n              \"current_value\": 20700,\n              \"previous_value\": 18940,\n              \"compare_period\": \"2024-12\"\n            },\n            {\n              \"delta\": -2.4,\n              \"current_value\": 20200,\n              \"previous_value\": 20700,\n              \"compare_period\": \"2025-01\"\n            },\n            {\n              \"delta\": 10.4,\n              \"current_value\": 22300,\n              \"previous_value\": 20200,\n              \"compare_period\": \"2025-02\"\n            },\n            {\n              \"delta\": -2.2,\n              \"current_value\": 21800,\n              \"previous_value\": 22300,\n              \"compare_period\": \"2025-03\"\n            },\n            {\n              \"delta\": 7.3,\n              \"current_value\": 23400,\n              \"previous_value\": 21800,\n              \"compare_period\": \"2025-04\"\n            }\n          ]\n        }\n      ]\n    },\n    \"calendar_year_volume\": [\n      {\n        \"year\": 2022,\n        \"purchase_volume\": 142300,\n        \"units_purchased\": 6100,\n        \"po_count\": 12,\n        \"sales_volume\": 238700,\n        \"units_sold\": 5840,\n        \"so_count\": 214\n      },\n      {\n        \"year\": 2023,\n        \"purchase_volume\": 168900,\n        \"units_purchased\": 7050,\n        \"po_count\": 14,\n        \"sales_volume\": 281400,\n        \"units_sold\": 6720,\n        \"so_count\": 248\n      },\n      {\n        \"year\": 2024,\n        \"purchase_volume\": 191450,\n        \"units_purchased\": 7780,\n        \"po_count\": 16,\n        \"sales_volume\": 309600,\n        \"units_sold\": 7410,\n        \"so_count\": 271\n      },\n      {\n        \"year\": 2025,\n        \"purchase_volume\": 224800,\n        \"units_purchased\": 8640,\n        \"po_count\": 19,\n        \"sales_volume\": 352100,\n        \"units_sold\": 8190,\n        \"so_count\": 305\n      },\n      {\n        \"year\": 2026,\n        \"purchase_volume\": 86200,\n        \"units_purchased\": 3320,\n        \"po_count\": 7,\n        \"sales_volume\": 131900,\n        \"units_sold\": 3040,\n        \"so_count\": 118\n      }\n    ],\n    \"meta\": {\n      \"data_as_of\": \"2026-05-14T03:12:08+00:00\",\n      \"period_label\": \"YoY — May 2025 – May 2026 vs May 2024 – May 2025\",\n      \"current_range\": [\n        \"2025-05-16\",\n        \"2026-05-15\"\n      ],\n      \"previous_range\": [\n        \"2024-05-16\",\n        \"2025-05-15\"\n      ],\n      \"is_partial\": false,\n      \"is_live_query\": false\n    }\n  }\n}"}]},{"name":"Get Supplier Margin Ladder","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/suppliers/{supplier}/margin-ladder?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","suppliers",":supplier","margin-ladder"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"supplier","value":"14","description":"Supplier id"}]},"description":"Plan-versus-actual margin for one supplier over the resolved period.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nTwo halves, deliberately kept apart because they are not the same measurement:\n\n**`list` — the plan, from price lists only.** The gap between the retail price on the chosen sell tier and this supplier's cost on the chosen cost tier, across the products they supply. Two reads:\n- `catalogue` — a straight average across every priced product (each SKU counts once)\n- `units_weighted` — weighted by units actually sold in the period, so the busy SKUs dominate\n\nThe two diverge whenever sales concentrate in a subset of the catalogue; `units_weighted` is the fairer thing to compare the actuals against. `avg_sell_price` / `avg_cost_price` / `weighted_sell_price` / `weighted_cost_price` expose the inputs, and `coverage` reports how many of the supplier's products carry both a price and a cost (`available` is false when none do).\n\n**`actual` — what really happened**, from shipped sales lines in the period. Three rungs, each subtracting one more layer of cost from the same revenue:\n- `gross` — revenue less cost of goods sold, net of returned goods\n- `net` — gross less direct costs (shipping, fees, packaging)\n- `contribution` — net less allocated indirect costs\n\n`gross_vs_list_pts`, `gross_to_net_pts` and `net_to_contribution_pts` are the step sizes in percentage points (negative = margin lost at that step). `coverage` reports how many sold lines had a recorded cost of goods, which is the honesty check on the whole actual half.\n\n**Margin conventions.** Every margin object restates one gap five ways: `margin_pct` ((sell - cost) / sell), `off_retail_pct` (the same number read as a discount off the retail list price), `cost_ratio_pct` (cost / sell), `markup_pct` ((sell - cost) / cost), `multiplier` (sell / cost) and `amount` (the money). `off_retail_pct` is null on `net` and `contribution` — once shipping and overhead are subtracted the result is no longer a discount off a list price, so publishing it there would be wrong. Every field is null when either side is missing or revenue is zero.\n\nOnly the actual half can carry direct and indirect costs; list prices have none, so the plan half stops at gross by design.\n\nRequired: `period_mode`. When `period_mode=custom`, `date_from` and `date_to` are both required (YYYY-MM-DD or MM/DD/YYYY) and `date_to` must be on or after `date_from`. `sell_tier_id` / `cost_tier_id` default to the account's default retail and supplier tiers; `tiers` and `selected_tiers` come back on every response so a caller can render the pickers without a second request."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/suppliers/{supplier}/margin-ladder?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","suppliers",":supplier","margin-ladder"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"supplier","value":"14","description":"Supplier id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"list\": {\n      \"available\": true,\n      \"catalogue\": {\n        \"margin_pct\": 55.4,\n        \"off_retail_pct\": 55.4,\n        \"cost_ratio_pct\": 44.6,\n        \"markup_pct\": 124.4,\n        \"multiplier\": 2.24,\n        \"amount\": 13.64\n      },\n      \"units_weighted\": {\n        \"margin_pct\": 65.3,\n        \"off_retail_pct\": 65.3,\n        \"cost_ratio_pct\": 34.7,\n        \"markup_pct\": 188.3,\n        \"multiplier\": 2.88,\n        \"amount\": 16.32\n      },\n      \"avg_sell_price\": 24.61,\n      \"avg_cost_price\": 10.96,\n      \"weighted_sell_price\": 24.99,\n      \"weighted_cost_price\": 8.67,\n      \"weighted_units\": 1686,\n      \"coverage\": {\n        \"total_products\": 171,\n        \"priced_products\": 170,\n        \"pct\": 99.4\n      }\n    },\n    \"actual\": {\n      \"available\": true,\n      \"revenue\": 39875.56,\n      \"cogs\": 15066.61,\n      \"cogs_returned\": 0,\n      \"direct_cost\": 11790.54,\n      \"indirect_cost\": 0,\n      \"units_sold\": 1686,\n      \"gross\": {\n        \"margin_pct\": 62.2,\n        \"off_retail_pct\": 62.2,\n        \"cost_ratio_pct\": 37.8,\n        \"markup_pct\": 164.7,\n        \"multiplier\": 2.65,\n        \"amount\": 24808.95\n      },\n      \"net\": {\n        \"margin_pct\": 32.6,\n        \"off_retail_pct\": null,\n        \"cost_ratio_pct\": 67.4,\n        \"markup_pct\": 48.5,\n        \"multiplier\": 1.48,\n        \"amount\": 13018.41\n      },\n      \"contribution\": {\n        \"margin_pct\": 32.6,\n        \"off_retail_pct\": null,\n        \"cost_ratio_pct\": 67.4,\n        \"markup_pct\": 48.5,\n        \"multiplier\": 1.48,\n        \"amount\": 13018.41\n      },\n      \"gross_vs_list_pts\": 6.8,\n      \"gross_to_net_pts\": -29.6,\n      \"net_to_contribution_pts\": 0,\n      \"coverage\": {\n        \"total_lines\": 1439,\n        \"lines_with_cogs\": 1424,\n        \"pct\": 98.96\n      }\n    },\n    \"tiers\": {\n      \"sell\": [\n        {\n          \"id\": 1,\n          \"name\": \"Retail\",\n          \"currency_code\": \"USD\",\n          \"is_default\": true\n        },\n        {\n          \"id\": 4,\n          \"name\": \"Wholesale\",\n          \"currency_code\": \"USD\",\n          \"is_default\": false\n        }\n      ],\n      \"cost\": [\n        {\n          \"id\": 1,\n          \"name\": \"Wholesale\",\n          \"currency_code\": \"USD\",\n          \"is_default\": true\n        },\n        {\n          \"id\": 3,\n          \"name\": \"Volume 500+\",\n          \"currency_code\": \"USD\",\n          \"is_default\": false\n        }\n      ]\n    },\n    \"selected_tiers\": {\n      \"sell_tier_id\": 1,\n      \"sell_tier_name\": \"Retail\",\n      \"sell_tier_currency\": \"USD\",\n      \"cost_tier_id\": 1,\n      \"cost_tier_name\": \"Wholesale\",\n      \"cost_tier_currency\": \"USD\"\n    }\n  }\n}"}]},{"name":"Get Supplier Product Breakdown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/suppliers/{supplier}/products?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","suppliers",":supplier","products"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"supplier","value":"","description":"The supplier ID."}]},"description":"Paginated, sortable per-product breakdown for one supplier (Analytics-tab product table). Each row carries units, purchase/sales volume, profit, margin %, sell-through %, landed unit cost, the landed-cost trend direction (cost_trend), and period-over-period growth %.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameters:\n- supplier: the Supplier ID\n\nRequired parameters:\n- period_mode: yoy | qoq | mom | custom\n\nWhen period_mode=custom, date_from and date_to (YYYY-MM-DD or m/d/Y) are both required.\n\nFilters (Spatie QueryBuilder):\n- filter[search]: partial match on product SKU or name\n- filter[cost_trend]: landed-unit-cost trend direction — up | down | flat\n- filter[margin]: margin sign — positive (margin > 0) | negative (margin < 0)\n\nSort (sort param, prefix - for DESC). Allowed: units_purchased, units_sold, purchase_volume, sales_volume, profit, margin_pct, sell_through_pct, growth_pct. Default: -purchase_volume\n\nPagination: page (default 1), per_page (default 10).\n\nOptional:\n- growth_basis: spend | sales | profit | margin (default: sales) — drives the growth_pct column\n- min_volume, include_archived\n\nResponse field cost_trend: up | down | flat — direction of the landed unit cost vs the prior period (always present, defaults to flat)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/suppliers/{supplier}/products?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","suppliers",":supplier","products"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"supplier","value":"","description":"The supplier ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 9001,\n      \"sku\": \"ACM-WIDGET-01\",\n      \"name\": \"Widget Assembly Type A\",\n      \"units_purchased\": 3200,\n      \"units_sold\": 3050,\n      \"purchase_volume\": 42100,\n      \"sales_volume\": 78900,\n      \"profit\": 24600,\n      \"margin_pct\": 31.2,\n      \"sell_through_pct\": 95.3,\n      \"landed_unit_cost\": 13.16,\n      \"cost_trend\": \"up\",\n      \"growth_pct\": 34.2\n    },\n    {\n      \"product_id\": 9044,\n      \"sku\": \"ACM-BRACKET-12\",\n      \"name\": \"Mounting Bracket XL\",\n      \"units_purchased\": 540,\n      \"units_sold\": 410,\n      \"purchase_volume\": 6200,\n      \"sales_volume\": 9800,\n      \"profit\": 2100,\n      \"margin_pct\": 21.4,\n      \"sell_through_pct\": 75.9,\n      \"landed_unit_cost\": 11.48,\n      \"cost_trend\": \"down\",\n      \"growth_pct\": -21.8\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/reporting/suppliers/42/products?page=1\",\n  \"from\": 1,\n  \"last_page\": 5,\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/reporting/suppliers/42/products?page=5\",\n  \"next_page_url\": \"{{protocol}}{{domain}}/api/reporting/suppliers/42/products?page=2\",\n  \"path\": \"{{protocol}}{{domain}}/api/reporting/suppliers/42/products\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 10,\n  \"total\": 47\n}"}]},{"name":"Get Supplier Product Drilldown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/suppliers/{supplier}/products/{product}?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","suppliers",":supplier","products",":product"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"supplier","value":"","description":"The supplier ID."},{"key":"product","value":"","description":"The product ID."}]},"description":"Single-product drill-down for one supplier: the KPI strip (current vs previous window), on-hand units, quoted vs actual lead time from this supplier, landed-cost trend, monthly units purchased vs sold, the alternate suppliers carrying the same SKU, and the most recent PO lines from this supplier.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameters:\n- supplier: the Supplier ID\n- product: the Product ID\n\nRequired parameters:\n- period_mode: yoy | qoq | mom | custom\n\nWhen period_mode=custom, date_from and date_to (YYYY-MM-DD or m/d/Y) are both required.\n\nOptional:\n- growth_basis: spend | sales | profit | margin (default: sales)\n- min_volume, include_archived\n\nResponse fields:\n- product {id, sku, name}\n- kpis: units_purchased, units_sold, purchase_volume, sales_volume, profit, margin_pct, sell_through_pct, gmroi — each {current, previous, delta_pct}. Amounts and GMROI report % growth (null when previous <= 0); margin_pct / sell_through_pct report the percentage-point change (null when either side has no rate). GMROI = profit / inventory cost at window end.\n- inventory_on_hand: current on-hand units\n- lead_time: quoted lead time in days; avg_actual_lead_time: average days from PO date to full receipt\n- landed_cost_trend {dates, costs}: landed unit cost per PO in the current window\n- units_chart {buckets (Y-m), purchased, sold}: monthly units, zero-filled\n- recent_po_lines: last 10 non-draft PO lines, newest first"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/suppliers/{supplier}/products/{product}?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","suppliers",":supplier","products",":product"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"supplier","value":"","description":"The supplier ID."},{"key":"product","value":"","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"supplier\": {\n      \"id\": 42,\n      \"name\": \"Acme Components Ltd\"\n    },\n    \"product\": {\n      \"id\": 9001,\n      \"sku\": \"ACM-WIDGET-01\",\n      \"name\": \"Widget Assembly Type A\"\n    },\n    \"kpis\": {\n      \"units_purchased\": {\n        \"current\": 3200,\n        \"previous\": 2400,\n        \"delta_pct\": 33.3\n      },\n      \"units_sold\": {\n        \"current\": 3050,\n        \"previous\": 2280,\n        \"delta_pct\": 33.8\n      },\n      \"purchase_volume\": {\n        \"current\": 42100,\n        \"previous\": 31400,\n        \"delta_pct\": 34.1\n      },\n      \"sales_volume\": {\n        \"current\": 78900,\n        \"previous\": 58200,\n        \"delta_pct\": 35.6\n      },\n      \"profit\": {\n        \"current\": 24600,\n        \"previous\": 17900,\n        \"delta_pct\": 37.4\n      },\n      \"margin_pct\": {\n        \"current\": 31.2,\n        \"previous\": 30.8,\n        \"delta_pct\": 0.4\n      },\n      \"sell_through_pct\": {\n        \"current\": 95.3,\n        \"previous\": 95,\n        \"delta_pct\": 0.3\n      },\n      \"gmroi\": {\n        \"current\": 2.46,\n        \"previous\": 2.11,\n        \"delta_pct\": 16.6\n      }\n    },\n    \"inventory_on_hand\": 640,\n    \"lead_time\": 28,\n    \"avg_actual_lead_time\": 31.4,\n    \"landed_cost_trend\": {\n      \"dates\": [\n        \"2025-07-12\",\n        \"2025-11-03\",\n        \"2026-02-21\"\n      ],\n      \"costs\": [\n        13.4,\n        13.16,\n        12.95\n      ]\n    },\n    \"units_chart\": {\n      \"buckets\": [\n        \"2025-05\",\n        \"2025-06\",\n        \"2025-07\"\n      ],\n      \"purchased\": [\n        0,\n        0,\n        1200\n      ],\n      \"sold\": [\n        210,\n        240,\n        265\n      ]\n    },\n    \"alternate_suppliers\": [\n      {\n        \"supplier_id\": 58,\n        \"name\": \"Globex Manufacturing\",\n        \"landed_unit_cost\": 12.1,\n        \"lead_time\": 21,\n        \"is_default\": false\n      },\n      {\n        \"supplier_id\": 73,\n        \"name\": \"Initech Hardware\",\n        \"landed_unit_cost\": null,\n        \"lead_time\": 35,\n        \"is_default\": false\n      }\n    ],\n    \"recent_po_lines\": [\n      {\n        \"po_id\": 5120,\n        \"po_number\": \"PO-103120\",\n        \"purchase_order_date\": \"2026-02-21\",\n        \"quantity\": 1000,\n        \"received_quantity\": 1000,\n        \"landed_unit_cost\": 12.95\n      },\n      {\n        \"po_id\": 4981,\n        \"po_number\": \"PO-101655\",\n        \"purchase_order_date\": \"2025-11-03\",\n        \"quantity\": 1000,\n        \"received_quantity\": 950,\n        \"landed_unit_cost\": 13.16\n      }\n    ],\n    \"meta\": {\n      \"data_as_of\": \"2026-05-14T03:12:08+00:00\",\n      \"period_label\": \"YoY — May 2025 – May 2026 vs May 2024 – May 2025\",\n      \"current_range\": [\n        \"2025-05-16\",\n        \"2026-05-15\"\n      ],\n      \"previous_range\": [\n        \"2024-05-16\",\n        \"2025-05-15\"\n      ],\n      \"is_partial\": false,\n      \"is_live_query\": false\n    }\n  }\n}"}]},{"name":"Get Supplier PO History","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/suppliers/{supplier}/po-history","host":["{{baseUrl}}"],"path":["api","reporting","suppliers",":supplier","po-history"],"variable":[{"key":"supplier","value":"42","description":"The Supplier ID"}]},"description":"Recent purchase orders for one supplier — backs the \"Recent Purchase Orders\" mini-table on the supplier Analytics tab. Returns up to the 10 most recent non-draft purchase orders ordered by purchase order date (DESC), each with its receipt status and an on-time flag.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameters:\n- supplier: the Supplier ID\n\nThis endpoint is period-independent — it always returns the most recent purchase orders and takes no query parameters. Draft purchase orders are excluded.\n\nResponse fields per row:\n- id: purchase order ID\n- po_number: human-readable purchase order number\n- purchase_order_date: order date (Y-m-d)\n- total_cost: total purchase order cost\n- receipt_status: receipt status of the PO (e.g. unreceived, partially_received, fully_received)\n- on_time: true if fully received on or before the estimated delivery date, false if late, null if not yet fully received"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/suppliers/{supplier}/po-history","host":["{{baseUrl}}"],"path":["api","reporting","suppliers",":supplier","po-history"],"variable":[{"key":"supplier","value":"42","description":"The Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 8821,\n      \"po_number\": \"PO-2025-0142\",\n      \"purchase_order_date\": \"2025-05-08\",\n      \"total_cost\": 42100,\n      \"receipt_status\": \"fully_received\",\n      \"on_time\": true\n    },\n    {\n      \"id\": 8790,\n      \"po_number\": \"PO-2025-0118\",\n      \"purchase_order_date\": \"2025-04-22\",\n      \"total_cost\": 6200.5,\n      \"receipt_status\": \"partially_received\",\n      \"on_time\": null\n    },\n    {\n      \"id\": 8755,\n      \"po_number\": \"PO-2025-0091\",\n      \"purchase_order_date\": \"2025-03-30\",\n      \"total_cost\": 18750,\n      \"receipt_status\": \"fully_received\",\n      \"on_time\": false\n    }\n  ]\n}"}]},{"name":"Get Supplier Scorecard","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/suppliers/{supplier}/scorecard?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","suppliers",":supplier","scorecard"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"supplier","value":"","description":"The supplier ID."}]},"description":"Standalone composite scorecard for a supplier over the resolved period — the composite, its band, and the per-dimension breakdown.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe band is one of: excellent, good, needs_attention, poor. When the supplier has fewer received POs than the configured minimum (is_sufficient=false), the band is not_rated and composite is 0.\n\nLive preview: when the optional preview_weights, preview_thresholds and preview_min_po_count parameters are supplied (all three together), the score is computed against a transient, non-persisted settings instance built from those values. Nothing is written to the scorecard settings table. preview_weights must contain all six metric keys and sum to exactly 100.\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameters:\n- supplier: the Supplier ID\n\nRequired parameters:\n- period_mode: yoy | qoq | mom | custom\n\nWhen period_mode=custom, date_from and date_to (YYYY-MM-DD or m/d/Y) are both required.\n\nOptional period params:\n- growth_basis: spend | sales | profit | margin (default: sales)\n- min_volume, include_archived\n\nOptional live-preview params (all three required together when previewing):\n- preview_weights[{metric}]: numeric 0-100 — one per metric, summing to 100\n- preview_thresholds[{metric}][{i}][min]: numeric; preview_thresholds[{metric}][{i}][score]: integer 1-5\n- preview_min_po_count: integer, min 1\n\npreview_weights and preview_thresholds may be passed either as bracketed array params (shown above) or as a single URL-encoded JSON string — the live-preview UI sends them as JSON strings."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/suppliers/{supplier}/scorecard?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","suppliers",":supplier","scorecard"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"supplier","value":"","description":"The supplier ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"composite\": 4.15,\n    \"band\": \"good\",\n    \"is_sufficient\": true,\n    \"po_count\": 18,\n    \"dimensions\": [\n      {\n        \"key\": \"on_time_delivery\",\n        \"label\": \"On-Time Delivery\",\n        \"raw\": 92.3,\n        \"raw_display\": \"92.3%\",\n        \"score\": 4,\n        \"weight\": 25\n      },\n      {\n        \"key\": \"lead_time_reliability\",\n        \"label\": \"Lead-Time Reliability\",\n        \"raw\": 88,\n        \"raw_display\": \"88.0%\",\n        \"score\": 3,\n        \"weight\": 15\n      },\n      {\n        \"key\": \"fill_rate\",\n        \"label\": \"Fill Rate\",\n        \"raw\": 98.6,\n        \"raw_display\": \"98.6%\",\n        \"score\": 5,\n        \"weight\": 15\n      },\n      {\n        \"key\": \"defect_rate\",\n        \"label\": \"Defect / RTV Rate\",\n        \"raw\": 1.2,\n        \"raw_display\": \"1.2%\",\n        \"score\": 5,\n        \"weight\": 15\n      },\n      {\n        \"key\": \"cost_competitiveness\",\n        \"label\": \"Cost Competitiveness\",\n        \"raw\": 74.5,\n        \"raw_display\": \"74.5 / 100\",\n        \"score\": 3,\n        \"weight\": 25\n      },\n      {\n        \"key\": \"price_stability\",\n        \"label\": \"Price Stability\",\n        \"raw\": 0.18,\n        \"raw_display\": \"18% price increases\",\n        \"score\": 4,\n        \"weight\": 5\n      }\n    ]\n  }\n}"}]},{"name":"Get Product Overview","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/products/overview?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","products","overview"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}]},"description":"Macro product dashboard for the resolved period: headline KPIs (sales volume, units sold, profit, active products), the hottest/coldest movers, a 12-month growth heatmap, and the full leaderboard. KPIs and trends are read from monthly product metric snapshots.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nRequired parameters:\n- period_mode: yoy | qoq | mom | custom\n\nWhen period_mode=custom, date_from and date_to (YYYY-MM-DD or m/d/Y) are both required and date_to must be on or after date_from.\n\nOptional:\n- growth_basis: sales | units | profit | margin (default: sales) — the basis used to rank movers and the heatmap\n- min_volume: numeric — minimum sales volume before a product qualifies as a mover\n- sales_channel_id: integer — restrict the dashboard to a single sales channel\n- include_archived: boolean — include archived products (default: false)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/products/overview?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","products","overview"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"kpis\": {\n      \"sales_volume\": {\n        \"current\": 3120900,\n        \"previous\": 2890400,\n        \"delta_pct\": 8\n      },\n      \"units_sold\": {\n        \"current\": 41200,\n        \"previous\": 39800,\n        \"delta_pct\": 3.5\n      },\n      \"profit\": {\n        \"current\": 961000.25,\n        \"previous\": 905100,\n        \"delta_pct\": 6.2\n      },\n      \"active_products\": {\n        \"current\": 612,\n        \"previous\": 588,\n        \"delta_pct\": 4.1\n      }\n    },\n    \"hottest\": [\n      {\n        \"product_id\": 1042,\n        \"sku\": \"WIDGET-RED-L\",\n        \"name\": \"Red Widget Large\",\n        \"value\": 184250.5,\n        \"delta_pct\": 31.5,\n        \"spark\": [\n          12000,\n          13400,\n          15600,\n          14200,\n          16800,\n          17100,\n          18900,\n          16400,\n          19200,\n          21000,\n          20400,\n          22300\n        ]\n      }\n    ],\n    \"coldest\": [\n      {\n        \"product_id\": 871,\n        \"sku\": \"GADGET-BLU-S\",\n        \"name\": \"Blue Gadget Small\",\n        \"value\": 22400,\n        \"delta_pct\": -18.7,\n        \"spark\": [\n          9100,\n          8700,\n          8200,\n          7600,\n          6900,\n          6400,\n          5800,\n          5100,\n          4700,\n          4200,\n          3900,\n          3400\n        ]\n      }\n    ],\n    \"heatmap\": {\n      \"columns\": [\n        \"2025-06\",\n        \"2025-07\",\n        \"2025-08\",\n        \"2025-09\",\n        \"2025-10\",\n        \"2025-11\",\n        \"2025-12\",\n        \"2026-01\",\n        \"2026-02\",\n        \"2026-03\",\n        \"2026-04\",\n        \"2026-05\"\n      ],\n      \"rows\": [\n        {\n          \"product_id\": 1042,\n          \"sku\": \"WIDGET-RED-L\",\n          \"name\": \"Red Widget Large\",\n          \"cells\": [\n            null,\n            8.2,\n            12.4,\n            -3.1,\n            6.7,\n            9,\n            14.2,\n            -2.4,\n            5.5,\n            11,\n            7.8,\n            12.4\n          ]\n        }\n      ]\n    },\n    \"leaderboard\": [\n      {\n        \"product_id\": 1042,\n        \"sku\": \"WIDGET-RED-L\",\n        \"name\": \"Red Widget Large\",\n        \"sales_volume\": 312900,\n        \"units_sold\": 7950,\n        \"cogs\": 216800,\n        \"profit\": 96100.25,\n        \"margin_pct\": 30.7,\n        \"return_rate_pct\": 2.1,\n        \"growth_pct\": 12.4,\n        \"so_count\": 421,\n        \"is_low_volume\": false\n      }\n    ],\n    \"meta\": {\n      \"data_as_of\": \"2026-05-14T03:12:08+00:00\",\n      \"period_label\": \"YoY — May 2025 – May 2026 vs May 2024 – May 2025\",\n      \"current_range\": [\n        \"2025-05-16\",\n        \"2026-05-15\"\n      ],\n      \"previous_range\": [\n        \"2024-05-16\",\n        \"2025-05-15\"\n      ],\n      \"is_partial\": false,\n      \"is_live_query\": false\n    }\n  }\n}"}]},{"name":"Get Product Leaderboard","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/products/leaderboard?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","products","leaderboard"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}]},"description":"Server-paginated, sortable product leaderboard for the resolved period — used when the macro dashboard's leaderboard table changes page or sort. Returns the standard Laravel pagination envelope.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nAccepts the same period parameters as Get Product Overview, plus:\n- sort: column to sort by, prefix with - for descending\n- page / per_page: standard pagination controls (per_page default 25)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/products/leaderboard?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","products","leaderboard"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 1042,\n      \"sku\": \"WIDGET-RED-L\",\n      \"name\": \"Red Widget Large\",\n      \"sales_volume\": 312900,\n      \"units_sold\": 7950,\n      \"cogs\": 216800,\n      \"profit\": 96100.25,\n      \"margin_pct\": 30.7,\n      \"return_rate_pct\": 2.1,\n      \"growth_pct\": 12.4,\n      \"so_count\": 421,\n      \"is_low_volume\": false\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 25,\n  \"per_page\": 25,\n  \"total\": 612,\n  \"from\": 1,\n  \"to\": 25,\n  \"path\": \"{{protocol}}{{domain}}/api/reporting/products/leaderboard\"\n}"}]},{"name":"Rebuild Product Metric Snapshots","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/reporting/products/rebuild-snapshots","host":["{{baseUrl}}"],"path":["api","reporting","products","rebuild-snapshots"]},"description":"Dispatches the product-metric snapshot rebuild as a tracked background job and returns its tracked job log ID for progress polling via the tracked job log endpoints. The rebuild recomputes per-channel sales snapshots first, then derives the per-product snapshots by summation. Rejects a second dispatch with 409 while a rebuild is already queued or running.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nThis endpoint takes no request body.","body":{"mode":"raw","raw":"","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/reporting/products/rebuild-snapshots","host":["{{baseUrl}}"],"path":["api","reporting","products","rebuild-snapshots"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 90871\n  },\n  \"message\": \"Product metric rebuild started.\"\n}"}]},{"name":"Get Product Scorecard Settings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/products/scorecard-settings","host":["{{baseUrl}}"],"path":["api","reporting","products","scorecard-settings"]},"description":"Returns the tenant's product scorecard configuration: the six dimension weights (must sum to 100), the threshold bands that map a raw metric value to a 1–5 score, and min_so_count — the minimum sales-order count before a product is scored.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/products/scorecard-settings","host":["{{baseUrl}}"],"path":["api","reporting","products","scorecard-settings"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"weights\": {\n      \"gross_margin\": 25,\n      \"gmroi\": 15,\n      \"sales_growth\": 20,\n      \"sell_through\": 15,\n      \"stock_cover\": 15,\n      \"return_rate\": 10\n    },\n    \"thresholds\": {\n      \"gross_margin\": [\n        {\n          \"min\": 40,\n          \"score\": 5\n        },\n        {\n          \"min\": 30,\n          \"score\": 4\n        },\n        {\n          \"min\": 20,\n          \"score\": 3\n        },\n        {\n          \"min\": 10,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"gmroi\": [\n        {\n          \"min\": 4,\n          \"score\": 5\n        },\n        {\n          \"min\": 3,\n          \"score\": 4\n        },\n        {\n          \"min\": 2,\n          \"score\": 3\n        },\n        {\n          \"min\": 1,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"sales_growth\": [\n        {\n          \"min\": 25,\n          \"score\": 5\n        },\n        {\n          \"min\": 10,\n          \"score\": 4\n        },\n        {\n          \"min\": 0,\n          \"score\": 3\n        },\n        {\n          \"min\": -15,\n          \"score\": 2\n        },\n        {\n          \"min\": -100,\n          \"score\": 1\n        }\n      ],\n      \"sell_through\": [\n        {\n          \"min\": 90,\n          \"score\": 5\n        },\n        {\n          \"min\": 75,\n          \"score\": 4\n        },\n        {\n          \"min\": 60,\n          \"score\": 3\n        },\n        {\n          \"min\": 40,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"stock_cover\": [\n        {\n          \"min\": 30,\n          \"score\": 5\n        },\n        {\n          \"min\": 21,\n          \"score\": 4\n        },\n        {\n          \"min\": 14,\n          \"score\": 3\n        },\n        {\n          \"min\": 7,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"return_rate\": [\n        {\n          \"min\": 98,\n          \"score\": 5\n        },\n        {\n          \"min\": 95,\n          \"score\": 4\n        },\n        {\n          \"min\": 90,\n          \"score\": 3\n        },\n        {\n          \"min\": 85,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ]\n    },\n    \"min_so_count\": 3\n  }\n}"}]},{"name":"Update Product Scorecard Settings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/reporting/products/scorecard-settings","host":["{{baseUrl}}"],"path":["api","reporting","products","scorecard-settings"]},"description":"Updates the tenant's product scorecard configuration.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nRequest body (all fields required):\n- weights: object — a numeric weight (0–100) for each of the six dimensions (gross_margin, gmroi, sales_growth, sell_through, stock_cover, return_rate). The six weights must sum to exactly 100.\n- thresholds: object — for each dimension, an array of at least one band; each band has a numeric `min` and an integer `score` between 1 and 5.\n- min_so_count: integer — minimum sales-order count before a product is scored (at least 1).","body":{"mode":"raw","raw":"{\n  \"weights\": {\n    \"gross_margin\": 25,\n    \"gmroi\": 15,\n    \"sales_growth\": 20,\n    \"sell_through\": 15,\n    \"stock_cover\": 15,\n    \"return_rate\": 10\n  },\n  \"thresholds\": {\n    \"gross_margin\": [\n      {\n        \"min\": 40,\n        \"score\": 5\n      },\n      {\n        \"min\": 30,\n        \"score\": 4\n      },\n      {\n        \"min\": 20,\n        \"score\": 3\n      },\n      {\n        \"min\": 10,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ],\n    \"gmroi\": [\n      {\n        \"min\": 4,\n        \"score\": 5\n      },\n      {\n        \"min\": 3,\n        \"score\": 4\n      },\n      {\n        \"min\": 2,\n        \"score\": 3\n      },\n      {\n        \"min\": 1,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ],\n    \"sales_growth\": [\n      {\n        \"min\": 25,\n        \"score\": 5\n      },\n      {\n        \"min\": 10,\n        \"score\": 4\n      },\n      {\n        \"min\": 0,\n        \"score\": 3\n      },\n      {\n        \"min\": -15,\n        \"score\": 2\n      },\n      {\n        \"min\": -100,\n        \"score\": 1\n      }\n    ],\n    \"sell_through\": [\n      {\n        \"min\": 90,\n        \"score\": 5\n      },\n      {\n        \"min\": 75,\n        \"score\": 4\n      },\n      {\n        \"min\": 60,\n        \"score\": 3\n      },\n      {\n        \"min\": 40,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ],\n    \"stock_cover\": [\n      {\n        \"min\": 30,\n        \"score\": 5\n      },\n      {\n        \"min\": 21,\n        \"score\": 4\n      },\n      {\n        \"min\": 14,\n        \"score\": 3\n      },\n      {\n        \"min\": 7,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ],\n    \"return_rate\": [\n      {\n        \"min\": 98,\n        \"score\": 5\n      },\n      {\n        \"min\": 95,\n        \"score\": 4\n      },\n      {\n        \"min\": 90,\n        \"score\": 3\n      },\n      {\n        \"min\": 85,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ]\n  },\n  \"min_so_count\": 3\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/reporting/products/scorecard-settings","host":["{{baseUrl}}"],"path":["api","reporting","products","scorecard-settings"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"weights\": {\n      \"gross_margin\": 25,\n      \"gmroi\": 15,\n      \"sales_growth\": 20,\n      \"sell_through\": 15,\n      \"stock_cover\": 15,\n      \"return_rate\": 10\n    },\n    \"thresholds\": {\n      \"gross_margin\": [\n        {\n          \"min\": 40,\n          \"score\": 5\n        },\n        {\n          \"min\": 30,\n          \"score\": 4\n        },\n        {\n          \"min\": 20,\n          \"score\": 3\n        },\n        {\n          \"min\": 10,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"gmroi\": [\n        {\n          \"min\": 4,\n          \"score\": 5\n        },\n        {\n          \"min\": 3,\n          \"score\": 4\n        },\n        {\n          \"min\": 2,\n          \"score\": 3\n        },\n        {\n          \"min\": 1,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"sales_growth\": [\n        {\n          \"min\": 25,\n          \"score\": 5\n        },\n        {\n          \"min\": 10,\n          \"score\": 4\n        },\n        {\n          \"min\": 0,\n          \"score\": 3\n        },\n        {\n          \"min\": -15,\n          \"score\": 2\n        },\n        {\n          \"min\": -100,\n          \"score\": 1\n        }\n      ],\n      \"sell_through\": [\n        {\n          \"min\": 90,\n          \"score\": 5\n        },\n        {\n          \"min\": 75,\n          \"score\": 4\n        },\n        {\n          \"min\": 60,\n          \"score\": 3\n        },\n        {\n          \"min\": 40,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"stock_cover\": [\n        {\n          \"min\": 30,\n          \"score\": 5\n        },\n        {\n          \"min\": 21,\n          \"score\": 4\n        },\n        {\n          \"min\": 14,\n          \"score\": 3\n        },\n        {\n          \"min\": 7,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"return_rate\": [\n        {\n          \"min\": 98,\n          \"score\": 5\n        },\n        {\n          \"min\": 95,\n          \"score\": 4\n        },\n        {\n          \"min\": 90,\n          \"score\": 3\n        },\n        {\n          \"min\": 85,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ]\n    },\n    \"min_so_count\": 3\n  }\n}"}]},{"name":"Get Product Scorecard Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/products/{product}/scorecard-summary","host":["{{baseUrl}}"],"path":["api","reporting","products",":product","scorecard-summary"],"variable":[{"key":"product","value":"","description":"The product ID."}]},"description":"Lightweight scorecard payload for the product Profile-tab performance chip: the composite scorecard, a directional trend hint (up | flat | down) and two short volume sparklines (sales volume and units sold).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameter:\n- product: the product ID"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/products/{product}/scorecard-summary","host":["{{baseUrl}}"],"path":["api","reporting","products",":product","scorecard-summary"],"variable":[{"key":"product","value":"","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product\": {\n      \"id\": 1042,\n      \"sku\": \"WIDGET-RED-L\",\n      \"name\": \"Red Widget Large\"\n    },\n    \"scorecard\": {\n      \"composite\": 3.85,\n      \"band\": \"good\",\n      \"is_sufficient\": true,\n      \"so_count\": 421,\n      \"dimensions\": [\n        {\n          \"key\": \"gross_margin\",\n          \"label\": \"Gross Margin\",\n          \"raw\": 30.7,\n          \"raw_display\": \"30.7%\",\n          \"score\": 4,\n          \"weight\": 25\n        },\n        {\n          \"key\": \"gmroi\",\n          \"label\": \"GMROI\",\n          \"raw\": 3.2,\n          \"raw_display\": \"3.2\",\n          \"score\": 4,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"sales_growth\",\n          \"label\": \"Sales Growth\",\n          \"raw\": 12.4,\n          \"raw_display\": \"+12.4%\",\n          \"score\": 4,\n          \"weight\": 20\n        },\n        {\n          \"key\": \"sell_through\",\n          \"label\": \"Sell-Through\",\n          \"raw\": 78,\n          \"raw_display\": \"78%\",\n          \"score\": 4,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"stock_cover\",\n          \"label\": \"Stock Cover\",\n          \"raw\": 18.5,\n          \"raw_display\": \"18.5 days\",\n          \"score\": 3,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"return_rate\",\n          \"label\": \"Return Rate\",\n          \"raw\": 2.1,\n          \"raw_display\": \"2.1%\",\n          \"score\": 5,\n          \"weight\": 10\n        }\n      ]\n    },\n    \"composite_trend\": \"up\",\n    \"sales_volume\": [\n      22100,\n      24300,\n      23800,\n      26100,\n      27400,\n      28900\n    ],\n    \"units_sold\": [\n      560,\n      612,\n      598,\n      651,\n      683,\n      720\n    ],\n    \"meta\": {\n      \"data_as_of\": \"2026-05-14T03:12:08+00:00\",\n      \"period_label\": \"Trailing 6 months\"\n    }\n  }\n}"}]},{"name":"Get Product Stock Coverage","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/products/{product}/stock-coverage","host":["{{baseUrl}}"],"path":["api","reporting","products",":product","stock-coverage"],"variable":[{"key":"product","value":"1042","description":"The product ID"}]},"description":"Daily Average Consumption (DAC) and Days of Stock reconstructed as a 12-month monthly time series for a product. No history is stored for either metric, so both are rebuilt from source ledgers: DAC at each month is gross units sold over the trailing sales-history window divided by that window length; on-hand at each month is the running balance of stock movements up to that month; Days of Stock is on-hand divided by DAC (null when DAC is 0, i.e. coverage is effectively infinite).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameter:\n- product: the product ID\n\nQuery parameter:\n- months: span of the series - an integer count of trailing months (e.g. 12, 24) or 'all' for the product's full ledger history (capped at 60 months). Default: 12.\n\nResponse fields:\n- buckets: 12 month labels (YYYY-MM), oldest first\n- window_days: the trailing DAC window length in days\n- dac / units_sold / on_hand / days_of_stock: parallel arrays aligned to buckets (units_sold is the raw units sold that calendar month; dac is the trailing-window daily average) (days_of_stock entries are null where DAC is 0)\n- current: the latest bucket's dac, on_hand and days_of_stock"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/products/{product}/stock-coverage","host":["{{baseUrl}}"],"path":["api","reporting","products",":product","stock-coverage"],"variable":[{"key":"product","value":"1042","description":"The product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"buckets\": [\n      \"2025-08\",\n      \"2025-09\",\n      \"2025-10\",\n      \"2025-11\",\n      \"2025-12\",\n      \"2026-01\",\n      \"2026-02\",\n      \"2026-03\",\n      \"2026-04\",\n      \"2026-05\",\n      \"2026-06\",\n      \"2026-07\"\n    ],\n    \"window_days\": 90,\n    \"dac\": [\n      0,\n      0,\n      0.12,\n      0.18,\n      0.2,\n      0.26,\n      0.3,\n      0.34,\n      0.4,\n      0.46,\n      0.52,\n      0.6\n    ],\n    \"units_sold\": [\n      0,\n      0,\n      14,\n      18,\n      20,\n      26,\n      30,\n      34,\n      40,\n      46,\n      52,\n      60\n    ],\n    \"on_hand\": [\n      0,\n      0,\n      120,\n      118,\n      150,\n      210,\n      190,\n      175,\n      160,\n      140,\n      120,\n      100\n    ],\n    \"days_of_stock\": [\n      null,\n      null,\n      1000,\n      655.6,\n      750,\n      807.7,\n      633.3,\n      514.7,\n      400,\n      304.3,\n      230.8,\n      166.7\n    ],\n    \"current\": {\n      \"dac\": 0.6,\n      \"units_sold\": 60,\n      \"on_hand\": 100,\n      \"days_of_stock\": 166.7\n    }\n  }\n}"}]},{"name":"Export Product Stock Coverage","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/products/{product}/stock-coverage/export","host":["{{baseUrl}}"],"path":["api","reporting","products",":product","stock-coverage","export"],"variable":[{"key":"product","value":"1042","description":"The product ID"}]},"description":"Download the month-by-month Stock Coverage series as a spreadsheet. Columns: Month, Units Sold (that calendar month), DAC (units/day, trailing-window daily average), On Hand (month-end), Days of Stock (on-hand / DAC). Honors the same 'months' span as the JSON endpoint.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameter:\n- product: the product ID\n\nQuery parameters:\n- months: trailing-month count or 'all' (default 12)\n- format: 'xlsx' or 'csv' (default xlsx)\n\nReturns a file download (Content-Type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet for xlsx, text/csv for csv), not JSON. Filename: stock-coverage-{sku}-{timestamp}.{format}."}},{"name":"Get Product Analytics","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/products/{product}/analytics?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","products",":product","analytics"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"product","value":"","description":"The product ID."}]},"description":"Full per-product analytics for the Analytics tab: period KPIs (sales volume, units sold, profit, margin), the computed scorecard, a monthly trend series, the hottest/coolest sales channels, a per-channel growth heatmap and recent orders.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameter:\n- product: the product ID\n\nAccepts the same period parameters as Get Product Overview."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/products/{product}/analytics?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","products",":product","analytics"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"product","value":"","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product\": {\n      \"id\": 1042,\n      \"sku\": \"WIDGET-RED-L\",\n      \"name\": \"Red Widget Large\"\n    },\n    \"kpis\": {\n      \"sales_volume\": {\n        \"current\": 312900,\n        \"previous\": 278400,\n        \"delta_pct\": 12.4\n      },\n      \"units_sold\": {\n        \"current\": 7950,\n        \"previous\": 7420,\n        \"delta_pct\": 7.1\n      },\n      \"profit\": {\n        \"current\": 96100.25,\n        \"previous\": 88200,\n        \"delta_pct\": 9\n      },\n      \"margin_pct\": {\n        \"current\": 30.7,\n        \"previous\": 31.7,\n        \"delta_pct\": -3.2\n      }\n    },\n    \"scorecard\": {\n      \"composite\": 3.85,\n      \"band\": \"good\",\n      \"is_sufficient\": true,\n      \"so_count\": 421,\n      \"dimensions\": [\n        {\n          \"key\": \"gross_margin\",\n          \"label\": \"Gross Margin\",\n          \"raw\": 30.7,\n          \"raw_display\": \"30.7%\",\n          \"score\": 4,\n          \"weight\": 25\n        },\n        {\n          \"key\": \"gmroi\",\n          \"label\": \"GMROI\",\n          \"raw\": 3.2,\n          \"raw_display\": \"3.2\",\n          \"score\": 4,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"sales_growth\",\n          \"label\": \"Sales Growth\",\n          \"raw\": 12.4,\n          \"raw_display\": \"+12.4%\",\n          \"score\": 4,\n          \"weight\": 20\n        },\n        {\n          \"key\": \"sell_through\",\n          \"label\": \"Sell-Through\",\n          \"raw\": 78,\n          \"raw_display\": \"78%\",\n          \"score\": 4,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"stock_cover\",\n          \"label\": \"Stock Cover\",\n          \"raw\": 18.5,\n          \"raw_display\": \"18.5 days\",\n          \"score\": 3,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"return_rate\",\n          \"label\": \"Return Rate\",\n          \"raw\": 2.1,\n          \"raw_display\": \"2.1%\",\n          \"score\": 5,\n          \"weight\": 10\n        }\n      ]\n    },\n    \"trend\": {\n      \"columns\": [\n        \"2025-12\",\n        \"2026-01\",\n        \"2026-02\",\n        \"2026-03\",\n        \"2026-04\",\n        \"2026-05\"\n      ],\n      \"sales_volume\": [\n        48200,\n        51100,\n        49800,\n        53400,\n        55900,\n        54500\n      ],\n      \"units_sold\": [\n        1210,\n        1280,\n        1250,\n        1340,\n        1410,\n        1460\n      ]\n    },\n    \"hottest_channels\": [\n      {\n        \"sales_channel_id\": 3,\n        \"name\": \"Amazon US\",\n        \"value\": 142800,\n        \"delta_pct\": 24.1\n      }\n    ],\n    \"coolest_channels\": [\n      {\n        \"sales_channel_id\": 7,\n        \"name\": \"eBay\",\n        \"value\": 18400,\n        \"delta_pct\": -12.6\n      }\n    ],\n    \"hottest_customers\": [\n      {\n        \"customer_id\": 5521,\n        \"name\": \"Acme Distributors\",\n        \"value\": 48200,\n        \"delta_pct\": 31.7\n      }\n    ],\n    \"coolest_customers\": [\n      {\n        \"customer_id\": 4310,\n        \"name\": \"Bright Retail Co\",\n        \"value\": 9100,\n        \"delta_pct\": -18.4\n      }\n    ],\n    \"channel_heatmap\": {\n      \"columns\": [\n        \"2025-12\",\n        \"2026-01\",\n        \"2026-02\",\n        \"2026-03\",\n        \"2026-04\",\n        \"2026-05\"\n      ],\n      \"rows\": [\n        {\n          \"sales_channel_id\": 3,\n          \"name\": \"Amazon US\",\n          \"cells\": [\n            6.2,\n            -1.4,\n            8.1,\n            11,\n            4.5,\n            9.3\n          ]\n        }\n      ]\n    },\n    \"recent_orders\": [\n      {\n        \"sales_order_id\": 88213,\n        \"order_number\": \"SO-88213\",\n        \"sales_channel\": \"Amazon US\",\n        \"ordered_at\": \"2026-05-12\",\n        \"units\": 24,\n        \"sales_volume\": 940\n      }\n    ],\n    \"meta\": {\n      \"data_as_of\": \"2026-05-14T03:12:08+00:00\",\n      \"period_label\": \"YoY — May 2025 – May 2026 vs May 2024 – May 2025\",\n      \"current_range\": [\n        \"2025-05-16\",\n        \"2026-05-15\"\n      ],\n      \"previous_range\": [\n        \"2024-05-16\",\n        \"2025-05-15\"\n      ],\n      \"is_partial\": false,\n      \"is_live_query\": false\n    }\n  }\n}"}]},{"name":"Get Product Channel Breakdown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/products/{product}/channels?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","products",":product","channels"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"product","value":"","description":"The product ID."}]},"description":"Server-paginated, sortable per-sales-channel breakdown for one product — the Channels tab table. Each row carries the channel's units, sales volume, profit, order count, average order value, margin, return rate, share of the product's sales and period-over-period growth. Channel-grain data is sales-side only (purchasing is not channel-attributable).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameter:\n- product: the product ID\n\nAccepts the same period parameters as Get Product Overview, plus sort / page / per_page."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/products/{product}/channels?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","products",":product","channels"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"product","value":"","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"sales_channel_id\": 3,\n      \"name\": \"Amazon US\",\n      \"units_sold\": 4820,\n      \"sales_volume\": 142800,\n      \"profit\": 44100,\n      \"order_count\": 412,\n      \"avg_order_value\": 346.6,\n      \"margin_pct\": 30.9,\n      \"return_rate_pct\": 1.8,\n      \"sales_share_pct\": 45.6,\n      \"growth_pct\": 24.1\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 6,\n  \"from\": 1,\n  \"to\": 6,\n  \"path\": \"{{protocol}}{{domain}}/api/reporting/products/1042/channels\"\n}"}]},{"name":"Get Product Channel Drilldown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/products/{product}/channels/{salesChannel}?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","products",":product","channels",":salesChannel"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"product","value":"","description":"The product ID."},{"key":"salesChannel","value":"","description":"The sales channel ID."}]},"description":"Single-channel drill-down for one product: the channel's current and previous-period totals, the product's whole-period totals for a share comparison, and recent orders placed on that channel.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameters:\n- product: the product ID\n- salesChannel: the sales channel ID\n\nAccepts the same period parameters as Get Product Overview.\n\nResponse includes `sales_channel` (id, name), `kpis` (units_sold, sales_volume, profit, margin_pct, order_count, avg_order_value — each a current/previous/delta_pct triplet; margin_pct's delta is in percentage points), `units_chart` (monthly buckets + units) and `sales_chart` (monthly buckets + current and previous-period sales aligned month for month), alongside the raw `current`/`previous`/`product_current` totals."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/products/{product}/channels/{salesChannel}?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","products",":product","channels",":salesChannel"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"product","value":"","description":"The product ID."},{"key":"salesChannel","value":"","description":"The sales channel ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product\": {\n      \"id\": 1042,\n      \"sku\": \"WIDGET-RED-L\",\n      \"name\": \"Red Widget Large\"\n    },\n    \"sales_channel_id\": 3,\n    \"sales_channel\": {\n      \"id\": 3,\n      \"name\": \"Amazon US\"\n    },\n    \"kpis\": {\n      \"units_sold\": {\n        \"current\": 4820,\n        \"previous\": 3910,\n        \"delta_pct\": 23.3\n      },\n      \"sales_volume\": {\n        \"current\": 142800,\n        \"previous\": 115100,\n        \"delta_pct\": 24.1\n      },\n      \"profit\": {\n        \"current\": 44100,\n        \"previous\": 34900,\n        \"delta_pct\": 26.4\n      },\n      \"margin_pct\": {\n        \"current\": 30.9,\n        \"previous\": 30.3,\n        \"delta_pct\": 0.6\n      },\n      \"order_count\": {\n        \"current\": 198,\n        \"previous\": 162,\n        \"delta_pct\": 22.2\n      },\n      \"avg_order_value\": {\n        \"current\": 721.21,\n        \"previous\": 710.49,\n        \"delta_pct\": 1.5\n      }\n    },\n    \"units_chart\": {\n      \"buckets\": [\n        \"2026-07\",\n        \"2026-08\",\n        \"2026-09\"\n      ],\n      \"units\": [\n        1510,\n        1640,\n        1670\n      ]\n    },\n    \"sales_chart\": {\n      \"buckets\": [\n        \"2026-07\",\n        \"2026-08\",\n        \"2026-09\"\n      ],\n      \"current\": [\n        44700,\n        48600,\n        49500\n      ],\n      \"previous\": [\n        37200,\n        38400,\n        39500\n      ]\n    },\n    \"current\": {\n      \"units_sold\": 4820,\n      \"sales_volume\": 142800,\n      \"cogs\": 98700,\n      \"profit\": 44100,\n      \"margin_pct\": 30.9,\n      \"returned_units\": 87,\n      \"so_count\": 198\n    },\n    \"previous\": {\n      \"units_sold\": 3910,\n      \"sales_volume\": 115100,\n      \"cogs\": 80200,\n      \"profit\": 34900,\n      \"margin_pct\": 30.3,\n      \"returned_units\": 71,\n      \"so_count\": 162\n    },\n    \"product_current\": {\n      \"units_sold\": 7950,\n      \"sales_volume\": 312900,\n      \"profit\": 96100.25\n    },\n    \"recent_orders\": [\n      {\n        \"sales_order_id\": 88213,\n        \"order_number\": \"SO-88213\",\n        \"ordered_at\": \"2026-05-12\",\n        \"units\": 24,\n        \"sales_volume\": 940\n      }\n    ],\n    \"meta\": {\n      \"data_as_of\": \"2026-05-14T03:12:08+00:00\",\n      \"period_label\": \"YoY — May 2025 – May 2026 vs May 2024 – May 2025\",\n      \"current_range\": [\n        \"2025-05-16\",\n        \"2026-05-15\"\n      ],\n      \"previous_range\": [\n        \"2024-05-16\",\n        \"2025-05-15\"\n      ],\n      \"is_partial\": false,\n      \"is_live_query\": false\n    }\n  }\n}"}]},{"name":"Get Product Scorecard","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/products/{product}/scorecard?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","products",":product","scorecard"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"product","value":"","description":"The product ID."}]},"description":"Standalone computed scorecard for one product — the composite, its band and the per-dimension breakdown. Used by the Scorecard Settings live preview.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameter:\n- product: the product ID\n\nAccepts the same period parameters as Get Product Overview. When the optional preview_weights, preview_thresholds and preview_min_so_count parameters are supplied together, the score is computed against a transient, non-persisted settings configuration built from those values — nothing is written to the scorecard settings table. Supplying any one of the three without the others returns a 422."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/products/{product}/scorecard?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","products",":product","scorecard"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"product","value":"","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"composite\": 3.85,\n    \"band\": \"good\",\n    \"is_sufficient\": true,\n    \"so_count\": 421,\n    \"dimensions\": [\n      {\n        \"key\": \"gross_margin\",\n        \"label\": \"Gross Margin\",\n        \"raw\": 30.7,\n        \"raw_display\": \"30.7%\",\n        \"score\": 4,\n        \"weight\": 25\n      },\n      {\n        \"key\": \"gmroi\",\n        \"label\": \"GMROI\",\n        \"raw\": 3.2,\n        \"raw_display\": \"3.2\",\n        \"score\": 4,\n        \"weight\": 15\n      },\n      {\n        \"key\": \"sales_growth\",\n        \"label\": \"Sales Growth\",\n        \"raw\": 12.4,\n        \"raw_display\": \"+12.4%\",\n        \"score\": 4,\n        \"weight\": 20\n      },\n      {\n        \"key\": \"sell_through\",\n        \"label\": \"Sell-Through\",\n        \"raw\": 78,\n        \"raw_display\": \"78%\",\n        \"score\": 4,\n        \"weight\": 15\n      },\n      {\n        \"key\": \"stock_cover\",\n        \"label\": \"Stock Cover\",\n        \"raw\": 18.5,\n        \"raw_display\": \"18.5 days\",\n        \"score\": 3,\n        \"weight\": 15\n      },\n      {\n        \"key\": \"return_rate\",\n        \"label\": \"Return Rate\",\n        \"raw\": 2.1,\n        \"raw_display\": \"2.1%\",\n        \"score\": 5,\n        \"weight\": 10\n      }\n    ]\n  }\n}"}]},{"name":"Get Brand Overview","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/brands/overview?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","brands","overview"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}]},"description":"Macro brand dashboard for the resolved period: headline KPIs (purchase volume, sales volume, profit, active brands), the hottest/coldest movers, a 12-month growth heatmap, and the full leaderboard. KPIs and trends are read from monthly brand metric snapshots.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nRequired parameters:\n- period_mode: yoy | qoq | mom | custom\n\nWhen period_mode=custom, date_from and date_to (YYYY-MM-DD or m/d/Y) are both required and date_to must be on or after date_from.\n\nOptional:\n- growth_basis: spend | sales | profit | margin (default: sales) — the basis used to rank movers and the heatmap\n- min_volume: numeric — minimum purchase volume before a brand qualifies as a mover (default: 1000)\n- include_archived: boolean — include archived brands (default: false)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/brands/overview?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","brands","overview"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"kpis\": {\n      \"purchase_volume\": {\n        \"current\": 1842500.5,\n        \"previous\": 1640200,\n        \"delta_pct\": 12.3\n      },\n      \"sales_volume\": {\n        \"current\": 3120900,\n        \"previous\": 2890400,\n        \"delta_pct\": 8\n      },\n      \"profit\": {\n        \"current\": 961000.25,\n        \"previous\": 905100,\n        \"delta_pct\": 6.2\n      },\n      \"active_brands\": {\n        \"current\": 48,\n        \"previous\": 45,\n        \"delta_pct\": 6.7\n      }\n    },\n    \"hottest\": [\n      {\n        \"brand_id\": 42,\n        \"name\": \"Acme Housewares\",\n        \"value\": 184250.5,\n        \"delta_pct\": 31.5,\n        \"spark\": [\n          12000,\n          13400,\n          15600,\n          14200,\n          16800,\n          17100,\n          18900,\n          16400,\n          19200,\n          21000,\n          20400,\n          22300\n        ]\n      }\n    ],\n    \"coldest\": [\n      {\n        \"brand_id\": 71,\n        \"name\": \"Borealis Home\",\n        \"value\": 22400,\n        \"delta_pct\": -18.7,\n        \"spark\": [\n          9100,\n          8700,\n          8200,\n          7600,\n          6900,\n          6400,\n          5800,\n          5100,\n          4700,\n          4200,\n          3900,\n          3400\n        ]\n      }\n    ],\n    \"heatmap\": {\n      \"columns\": [\n        \"2025-06\",\n        \"2025-07\",\n        \"2025-08\",\n        \"2025-09\",\n        \"2025-10\",\n        \"2025-11\",\n        \"2025-12\",\n        \"2026-01\",\n        \"2026-02\",\n        \"2026-03\",\n        \"2026-04\",\n        \"2026-05\"\n      ],\n      \"rows\": [\n        {\n          \"brand_id\": 42,\n          \"name\": \"Acme Housewares\",\n          \"cells\": [\n            null,\n            8.2,\n            12.4,\n            -3.1,\n            6.7,\n            9,\n            14.2,\n            -2.4,\n            5.5,\n            11,\n            7.8,\n            12.4\n          ]\n        }\n      ]\n    },\n    \"leaderboard\": [\n      {\n        \"brand_id\": 42,\n        \"name\": \"Acme Housewares\",\n        \"purchase_volume\": 184250.5,\n        \"sales_volume\": 312900,\n        \"units_purchased\": 8400,\n        \"units_sold\": 7950,\n        \"profit\": 96100.25,\n        \"margin_pct\": 30.7,\n        \"growth_pct\": 12.4,\n        \"active_po_count\": 23,\n        \"po_count\": 21,\n        \"is_low_volume\": false\n      }\n    ],\n    \"meta\": {\n      \"data_as_of\": \"2026-05-14T03:12:08+00:00\",\n      \"period_label\": \"YoY — May 2025 – May 2026 vs May 2024 – May 2025\",\n      \"current_range\": [\n        \"2025-05-16\",\n        \"2026-05-15\"\n      ],\n      \"previous_range\": [\n        \"2024-05-16\",\n        \"2025-05-15\"\n      ],\n      \"is_partial\": false,\n      \"is_live_query\": false\n    }\n  }\n}"}]},{"name":"Get Brand Leaderboard","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/brands/leaderboard?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","brands","leaderboard"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}]},"description":"Leaderboard rows only — a lightweight endpoint for table page/sort changes on the macro brand view, without recomputing KPIs, movers, or the heatmap. Rows are computed in-memory from snapshots, then filtered, sorted and sliced; low-volume brands are flagged via is_low_volume.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nServer-paginated (PRD edge case 15: 500+ brands) — returns the standard Laravel pagination envelope (data, current_page, last_page, per_page, total, etc.).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nRequired parameters:\n- period_mode: yoy | qoq | mom | custom\n\nWhen period_mode=custom, date_from and date_to (YYYY-MM-DD or m/d/Y) are both required.\n\nOptional:\n- growth_basis: spend | sales | profit | margin (default: sales)\n- min_volume: numeric — low-volume threshold (default: 1000)\n- include_archived: boolean (default: false)\n- sort: sort key, prefix - for DESC. Allowed: name, purchase_volume, sales_volume, units_purchased, units_sold, profit, margin_pct, growth_pct, active_po_count (default: -purchase_volume)\n- page: page number (default: 1)\n- per_page: rows per page (default: 10)\n- filter[search]: substring match on brand name\n- filter[volume]: above | low — volume band filter"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/brands/leaderboard?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","brands","leaderboard"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"brand_id\": 42,\n      \"name\": \"Acme Housewares\",\n      \"purchase_volume\": 184250.5,\n      \"sales_volume\": 312900,\n      \"units_purchased\": 8400,\n      \"units_sold\": 7950,\n      \"profit\": 96100.25,\n      \"margin_pct\": 30.7,\n      \"growth_pct\": 12.4,\n      \"active_po_count\": 23,\n      \"po_count\": 21,\n      \"is_low_volume\": false\n    },\n    {\n      \"brand_id\": 71,\n      \"name\": \"Borealis Home\",\n      \"purchase_volume\": 22400,\n      \"sales_volume\": 41200,\n      \"units_purchased\": 980,\n      \"units_sold\": 910,\n      \"profit\": 9100.5,\n      \"margin_pct\": 22.1,\n      \"growth_pct\": -18.7,\n      \"active_po_count\": 4,\n      \"po_count\": 5,\n      \"is_low_volume\": false\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/reporting/brands/leaderboard?page=1\",\n  \"from\": 1,\n  \"last_page\": 5,\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/reporting/brands/leaderboard?page=5\",\n  \"next_page_url\": \"{{protocol}}{{domain}}/api/reporting/brands/leaderboard?page=2\",\n  \"path\": \"{{protocol}}{{domain}}/api/reporting/brands/leaderboard\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 10,\n  \"total\": 48\n}"}]},{"name":"Rebuild Brand Metric Snapshots","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/reporting/brands/rebuild-snapshots","host":["{{baseUrl}}"],"path":["api","reporting","brands","rebuild-snapshots"]},"description":"Dispatches the brand-metric snapshot rebuild as a tracked background job and returns its tracked job log ID for progress polling via the tracked job log endpoints. Rejects a second dispatch with 409 while a rebuild is already queued or running.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nThis endpoint takes no request body.\n\nNote: no request body is required.","body":{"mode":"raw","raw":"","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/reporting/brands/rebuild-snapshots","host":["{{baseUrl}}"],"path":["api","reporting","brands","rebuild-snapshots"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 90871\n  },\n  \"message\": \"Brand metric rebuild started.\"\n}"}]},{"name":"Get Brand Scorecard Settings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/brands/scorecard-settings","host":["{{baseUrl}}"],"path":["api","reporting","brands","scorecard-settings"]},"description":"Returns the tenant-global brand scorecard configuration: the six dimension weights (sum to 100), the ordered {min, score} threshold bands per dimension, and the minimum in-period PO count required before a brand is scored. A default row is created on first access if none exists.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/brands/scorecard-settings","host":["{{baseUrl}}"],"path":["api","reporting","brands","scorecard-settings"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"weights\": {\n      \"on_time_delivery\": 25,\n      \"lead_time_reliability\": 15,\n      \"fill_rate\": 15,\n      \"defect_rate\": 15,\n      \"cost_competitiveness\": 25,\n      \"price_stability\": 5\n    },\n    \"thresholds\": {\n      \"on_time_delivery\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"lead_time_reliability\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"fill_rate\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"defect_rate\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"cost_competitiveness\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"price_stability\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ]\n    },\n    \"min_po_count\": 3,\n    \"created_at\": \"2026-01-04T09:00:00.000000Z\",\n    \"updated_at\": \"2026-05-10T14:22:31.000000Z\"\n  }\n}"}]},{"name":"Update Brand Scorecard Settings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/reporting/brands/scorecard-settings","host":["{{baseUrl}}"],"path":["api","reporting","brands","scorecard-settings"]},"description":"Updates the tenant-global brand scorecard configuration. The six weights must each be present and sum to exactly 100; every threshold band needs a numeric min and an integer 1-5 score.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nRequest body fields:\n- weights: object (required) — must contain all six metric keys; values numeric 0-100, summing to exactly 100\n- weights.on_time_delivery: numeric, 0-100 (required)\n- weights.lead_time_reliability: numeric, 0-100 (required)\n- weights.fill_rate: numeric, 0-100 (required)\n- weights.defect_rate: numeric, 0-100 (required)\n- weights.cost_competitiveness: numeric, 0-100 (required)\n- weights.price_stability: numeric, 0-100 (required)\n- thresholds: object (required) — must contain all six metric keys\n- thresholds.{metric}: array, min 1 item (required) — ordered {min, score} bands\n- thresholds.{metric}.*.min: numeric (required) — lower bound of the band\n- thresholds.{metric}.*.score: integer 1-5 (required) — score awarded for the band\n- min_po_count: integer, min 1 (required)","body":{"mode":"raw","raw":"{\n  \"weights\": {\n    \"on_time_delivery\": 25,\n    \"lead_time_reliability\": 15,\n    \"fill_rate\": 15,\n    \"defect_rate\": 15,\n    \"cost_competitiveness\": 25,\n    \"price_stability\": 5\n  },\n  \"thresholds\": {\n    \"on_time_delivery\": [\n      {\n        \"min\": 97,\n        \"score\": 5\n      },\n      {\n        \"min\": 90,\n        \"score\": 4\n      },\n      {\n        \"min\": 80,\n        \"score\": 3\n      },\n      {\n        \"min\": 60,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ],\n    \"lead_time_reliability\": [\n      {\n        \"min\": 97,\n        \"score\": 5\n      },\n      {\n        \"min\": 90,\n        \"score\": 4\n      },\n      {\n        \"min\": 80,\n        \"score\": 3\n      },\n      {\n        \"min\": 60,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ],\n    \"fill_rate\": [\n      {\n        \"min\": 97,\n        \"score\": 5\n      },\n      {\n        \"min\": 90,\n        \"score\": 4\n      },\n      {\n        \"min\": 80,\n        \"score\": 3\n      },\n      {\n        \"min\": 60,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ],\n    \"defect_rate\": [\n      {\n        \"min\": 97,\n        \"score\": 5\n      },\n      {\n        \"min\": 90,\n        \"score\": 4\n      },\n      {\n        \"min\": 80,\n        \"score\": 3\n      },\n      {\n        \"min\": 60,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ],\n    \"cost_competitiveness\": [\n      {\n        \"min\": 97,\n        \"score\": 5\n      },\n      {\n        \"min\": 90,\n        \"score\": 4\n      },\n      {\n        \"min\": 80,\n        \"score\": 3\n      },\n      {\n        \"min\": 60,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ],\n    \"price_stability\": [\n      {\n        \"min\": 97,\n        \"score\": 5\n      },\n      {\n        \"min\": 90,\n        \"score\": 4\n      },\n      {\n        \"min\": 80,\n        \"score\": 3\n      },\n      {\n        \"min\": 60,\n        \"score\": 2\n      },\n      {\n        \"min\": 0,\n        \"score\": 1\n      }\n    ]\n  },\n  \"min_po_count\": 3\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/reporting/brands/scorecard-settings","host":["{{baseUrl}}"],"path":["api","reporting","brands","scorecard-settings"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"weights\": {\n      \"on_time_delivery\": 25,\n      \"lead_time_reliability\": 15,\n      \"fill_rate\": 15,\n      \"defect_rate\": 15,\n      \"cost_competitiveness\": 25,\n      \"price_stability\": 5\n    },\n    \"thresholds\": {\n      \"on_time_delivery\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"lead_time_reliability\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"fill_rate\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"defect_rate\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"cost_competitiveness\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ],\n      \"price_stability\": [\n        {\n          \"min\": 97,\n          \"score\": 5\n        },\n        {\n          \"min\": 90,\n          \"score\": 4\n        },\n        {\n          \"min\": 80,\n          \"score\": 3\n        },\n        {\n          \"min\": 60,\n          \"score\": 2\n        },\n        {\n          \"min\": 0,\n          \"score\": 1\n        }\n      ]\n    },\n    \"min_po_count\": 3,\n    \"created_at\": \"2026-01-04T09:00:00.000000Z\",\n    \"updated_at\": \"2026-05-10T14:22:31.000000Z\"\n  }\n}"}]},{"name":"Get Brand Scorecard Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/brands/{brand}/scorecard-summary","host":["{{baseUrl}}"],"path":["api","reporting","brands",":brand","scorecard-summary"],"variable":[{"key":"brand","value":"","description":"The brand ID."}]},"description":"Lightweight scorecard + volume sparklines for the brand Profile chip. Returns the composite scorecard with its dimension breakdown, a directional composite trend hint (up | down | flat), and trailing-12-month purchase/sales volume sparklines. The period is fixed to YoY.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameters:\n- brand: the Brand ID"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/brands/{brand}/scorecard-summary","host":["{{baseUrl}}"],"path":["api","reporting","brands",":brand","scorecard-summary"],"variable":[{"key":"brand","value":"","description":"The brand ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"brand\": {\n      \"id\": 42,\n      \"name\": \"Acme Housewares\"\n    },\n    \"scorecard\": {\n      \"composite\": 4.15,\n      \"band\": \"good\",\n      \"is_sufficient\": true,\n      \"po_count\": 18,\n      \"dimensions\": [\n        {\n          \"key\": \"on_time_delivery\",\n          \"label\": \"On-Time Delivery\",\n          \"raw\": 92.3,\n          \"raw_display\": \"92.3%\",\n          \"score\": 4,\n          \"weight\": 25\n        },\n        {\n          \"key\": \"lead_time_reliability\",\n          \"label\": \"Lead-Time Reliability\",\n          \"raw\": 88,\n          \"raw_display\": \"88.0%\",\n          \"score\": 3,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"fill_rate\",\n          \"label\": \"Fill Rate\",\n          \"raw\": 98.6,\n          \"raw_display\": \"98.6%\",\n          \"score\": 5,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"defect_rate\",\n          \"label\": \"Defect / RTV Rate\",\n          \"raw\": 1.2,\n          \"raw_display\": \"1.2%\",\n          \"score\": 5,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"cost_competitiveness\",\n          \"label\": \"Cost Competitiveness\",\n          \"raw\": 74.5,\n          \"raw_display\": \"74.5 / 100\",\n          \"score\": 3,\n          \"weight\": 25\n        },\n        {\n          \"key\": \"price_stability\",\n          \"label\": \"Price Stability\",\n          \"raw\": 0.18,\n          \"raw_display\": \"18% price increases\",\n          \"score\": 4,\n          \"weight\": 5\n        }\n      ]\n    },\n    \"composite_trend\": \"up\",\n    \"purchase_volume\": {\n      \"current\": 184250.5,\n      \"previous\": 158900,\n      \"delta_pct\": 16,\n      \"spark\": [\n        12000,\n        13400,\n        15600,\n        14200,\n        16800,\n        17100,\n        18900,\n        16400,\n        19200,\n        21000,\n        20400,\n        22300\n      ]\n    },\n    \"sales_volume\": {\n      \"current\": 312900,\n      \"previous\": 289400,\n      \"delta_pct\": 8.1,\n      \"spark\": [\n        22000,\n        23100,\n        24800,\n        25600,\n        26100,\n        27400,\n        26900,\n        28200,\n        29400,\n        30100,\n        31200,\n        32400\n      ]\n    },\n    \"meta\": {\n      \"data_as_of\": \"2026-05-14T03:12:08+00:00\",\n      \"period_label\": \"YoY — May 2025 – May 2026 vs May 2024 – May 2025\",\n      \"current_range\": [\n        \"2025-05-16\",\n        \"2026-05-15\"\n      ],\n      \"previous_range\": [\n        \"2024-05-16\",\n        \"2025-05-15\"\n      ],\n      \"is_partial\": false,\n      \"is_live_query\": false\n    }\n  }\n}"}]},{"name":"Get Brand Analytics","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/brands/{brand}/analytics?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","brands",":brand","analytics"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"brand","value":"","description":"The brand ID."}]},"description":"Full per-brand analytics for the Analytics tab: headline KPIs (purchase volume, sales volume, units purchased/sold, profit, margin %), the composite scorecard, a dual-line current-vs-previous trend chart, and the hottest/coolest products ranked by growth.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameters:\n- brand: the Brand ID\n\nRequired parameters:\n- period_mode: yoy | qoq | mom | custom\n\nWhen period_mode=custom, date_from and date_to (YYYY-MM-DD or m/d/Y) are both required.\n\nOptional:\n- growth_basis: spend | sales | profit | margin (default: sales) — drives the trend metric and product mover ranking\n- min_volume: numeric (default: 1000)\n- include_archived: boolean (default: false)\n\nThe response also includes `product_heatmap` (a period-aware per-product growth heatmap — the brand's top products by volume, each cell a period-over-period change that follows the `period_mode` selector) and `calendar_year_volume` (per calendar year for the trailing 5 years: non-draft PO spend with units/PO counts plus non-draft sales revenue with units/order counts; the current year is year-to-date and is independent of `period_mode`)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/brands/{brand}/analytics?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","brands",":brand","analytics"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"brand","value":"","description":"The brand ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"brand\": {\n      \"id\": 42,\n      \"name\": \"Acme Housewares\"\n    },\n    \"kpis\": {\n      \"purchase_volume\": {\n        \"current\": 184250.5,\n        \"previous\": 158900,\n        \"delta_pct\": 16\n      },\n      \"sales_volume\": {\n        \"current\": 312900,\n        \"previous\": 289400,\n        \"delta_pct\": 8.1\n      },\n      \"units_purchased\": {\n        \"current\": 8400,\n        \"previous\": 7600,\n        \"delta_pct\": 10.5\n      },\n      \"units_sold\": {\n        \"current\": 7950,\n        \"previous\": 7400,\n        \"delta_pct\": 7.4\n      },\n      \"profit\": {\n        \"current\": 96100.25,\n        \"previous\": 88200,\n        \"delta_pct\": 9\n      },\n      \"margin_pct\": {\n        \"current\": 30.7,\n        \"previous\": 30.5,\n        \"delta_pct\": 0.2\n      }\n    },\n    \"scorecard\": {\n      \"composite\": 4.15,\n      \"band\": \"good\",\n      \"is_sufficient\": true,\n      \"po_count\": 18,\n      \"dimensions\": [\n        {\n          \"key\": \"on_time_delivery\",\n          \"label\": \"On-Time Delivery\",\n          \"raw\": 92.3,\n          \"raw_display\": \"92.3%\",\n          \"score\": 4,\n          \"weight\": 25\n        },\n        {\n          \"key\": \"lead_time_reliability\",\n          \"label\": \"Lead-Time Reliability\",\n          \"raw\": 88,\n          \"raw_display\": \"88.0%\",\n          \"score\": 3,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"fill_rate\",\n          \"label\": \"Fill Rate\",\n          \"raw\": 98.6,\n          \"raw_display\": \"98.6%\",\n          \"score\": 5,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"defect_rate\",\n          \"label\": \"Defect / RTV Rate\",\n          \"raw\": 1.2,\n          \"raw_display\": \"1.2%\",\n          \"score\": 5,\n          \"weight\": 15\n        },\n        {\n          \"key\": \"cost_competitiveness\",\n          \"label\": \"Cost Competitiveness\",\n          \"raw\": 74.5,\n          \"raw_display\": \"74.5 / 100\",\n          \"score\": 3,\n          \"weight\": 25\n        },\n        {\n          \"key\": \"price_stability\",\n          \"label\": \"Price Stability\",\n          \"raw\": 0.18,\n          \"raw_display\": \"18% price increases\",\n          \"score\": 4,\n          \"weight\": 5\n        }\n      ]\n    },\n    \"trend\": {\n      \"metric\": \"sales\",\n      \"buckets\": [\n        \"2025-05\",\n        \"2025-06\",\n        \"2025-07\",\n        \"2025-08\",\n        \"2025-09\",\n        \"2025-10\",\n        \"2025-11\",\n        \"2025-12\",\n        \"2026-01\",\n        \"2026-02\",\n        \"2026-03\",\n        \"2026-04\"\n      ],\n      \"current\": [\n        14200,\n        15600,\n        16800,\n        17100,\n        18900,\n        16400,\n        19200,\n        21000,\n        20400,\n        22300,\n        21800,\n        23400\n      ],\n      \"previous\": [\n        12100,\n        13400,\n        14000,\n        14900,\n        15600,\n        14100,\n        16800,\n        18200,\n        17900,\n        19400,\n        18800,\n        20100\n      ]\n    },\n    \"hottest_products\": [\n      {\n        \"product_id\": 9001,\n        \"sku\": \"ACM-WIDGET-01\",\n        \"name\": \"Widget Assembly Type A\",\n        \"value\": 42100,\n        \"delta_pct\": 34.2\n      }\n    ],\n    \"coolest_products\": [\n      {\n        \"product_id\": 9044,\n        \"sku\": \"ACM-BRACKET-12\",\n        \"name\": \"Mounting Bracket XL\",\n        \"value\": 6200,\n        \"delta_pct\": -21.8\n      }\n    ],\n    \"product_heatmap\": {\n      \"columns\": [\n        \"2025-05\",\n        \"2025-06\",\n        \"2025-07\",\n        \"2025-08\",\n        \"2025-09\",\n        \"2025-10\",\n        \"2025-11\",\n        \"2025-12\",\n        \"2026-01\",\n        \"2026-02\",\n        \"2026-03\",\n        \"2026-04\"\n      ],\n      \"rows\": [\n        {\n          \"product_id\": 9001,\n          \"sku\": \"ACM-WIDGET-01\",\n          \"name\": \"Widget Assembly Type A\",\n          \"cells\": [\n            {\n              \"delta\": null,\n              \"current_value\": 11800,\n              \"previous_value\": null,\n              \"compare_period\": \"2024-05\"\n            },\n            {\n              \"delta\": 12.4,\n              \"current_value\": 13300,\n              \"previous_value\": 11830,\n              \"compare_period\": \"2024-06\"\n            },\n            {\n              \"delta\": 8.1,\n              \"current_value\": 14380,\n              \"previous_value\": 13300,\n              \"compare_period\": \"2024-07\"\n            },\n            {\n              \"delta\": -3.2,\n              \"current_value\": 13920,\n              \"previous_value\": 14380,\n              \"compare_period\": \"2024-08\"\n            },\n            {\n              \"delta\": 19.5,\n              \"current_value\": 16640,\n              \"previous_value\": 13920,\n              \"compare_period\": \"2024-09\"\n            },\n            {\n              \"delta\": -6.7,\n              \"current_value\": 15520,\n              \"previous_value\": 16640,\n              \"compare_period\": \"2024-10\"\n            },\n            {\n              \"delta\": 22,\n              \"current_value\": 18940,\n              \"previous_value\": 15520,\n              \"compare_period\": \"2024-11\"\n            },\n            {\n              \"delta\": 9.3,\n              \"current_value\": 20700,\n              \"previous_value\": 18940,\n              \"compare_period\": \"2024-12\"\n            },\n            {\n              \"delta\": -2.4,\n              \"current_value\": 20200,\n              \"previous_value\": 20700,\n              \"compare_period\": \"2025-01\"\n            },\n            {\n              \"delta\": 10.4,\n              \"current_value\": 22300,\n              \"previous_value\": 20200,\n              \"compare_period\": \"2025-02\"\n            },\n            {\n              \"delta\": -2.2,\n              \"current_value\": 21800,\n              \"previous_value\": 22300,\n              \"compare_period\": \"2025-03\"\n            },\n            {\n              \"delta\": 7.3,\n              \"current_value\": 23400,\n              \"previous_value\": 21800,\n              \"compare_period\": \"2025-04\"\n            }\n          ]\n        }\n      ]\n    },\n    \"calendar_year_volume\": [\n      {\n        \"year\": 2022,\n        \"purchase_volume\": 142300,\n        \"units_purchased\": 6100,\n        \"po_count\": 12,\n        \"sales_volume\": 238700,\n        \"units_sold\": 5840,\n        \"so_count\": 214\n      },\n      {\n        \"year\": 2023,\n        \"purchase_volume\": 168900,\n        \"units_purchased\": 7050,\n        \"po_count\": 14,\n        \"sales_volume\": 281400,\n        \"units_sold\": 6720,\n        \"so_count\": 248\n      },\n      {\n        \"year\": 2024,\n        \"purchase_volume\": 191450,\n        \"units_purchased\": 7780,\n        \"po_count\": 16,\n        \"sales_volume\": 309600,\n        \"units_sold\": 7410,\n        \"so_count\": 271\n      },\n      {\n        \"year\": 2025,\n        \"purchase_volume\": 224800,\n        \"units_purchased\": 8640,\n        \"po_count\": 19,\n        \"sales_volume\": 352100,\n        \"units_sold\": 8190,\n        \"so_count\": 305\n      },\n      {\n        \"year\": 2026,\n        \"purchase_volume\": 86200,\n        \"units_purchased\": 3320,\n        \"po_count\": 7,\n        \"sales_volume\": 131900,\n        \"units_sold\": 3040,\n        \"so_count\": 118\n      }\n    ],\n    \"meta\": {\n      \"data_as_of\": \"2026-05-14T03:12:08+00:00\",\n      \"period_label\": \"YoY — May 2025 – May 2026 vs May 2024 – May 2025\",\n      \"current_range\": [\n        \"2025-05-16\",\n        \"2026-05-15\"\n      ],\n      \"previous_range\": [\n        \"2024-05-16\",\n        \"2025-05-15\"\n      ],\n      \"is_partial\": false,\n      \"is_live_query\": false\n    }\n  }\n}"}]},{"name":"Get Brand Product Breakdown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/brands/{brand}/products?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","brands",":brand","products"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"brand","value":"","description":"The brand ID."}]},"description":"Paginated, sortable per-product breakdown for one brand (Analytics-tab product table). Each row carries units, purchase/sales volume, profit, margin %, sell-through %, landed unit cost, the landed-cost trend direction (cost_trend), and period-over-period growth %.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameters:\n- brand: the Brand ID\n\nRequired parameters:\n- period_mode: yoy | qoq | mom | custom\n\nWhen period_mode=custom, date_from and date_to (YYYY-MM-DD or m/d/Y) are both required.\n\nFilters (Spatie QueryBuilder):\n- filter[search]: partial match on product SKU or name\n- filter[cost_trend]: landed-unit-cost trend direction — up | down | flat\n- filter[margin]: margin sign — positive (margin > 0) | negative (margin < 0)\n\nSort (sort param, prefix - for DESC). Allowed: units_purchased, units_sold, purchase_volume, sales_volume, profit, margin_pct, sell_through_pct, growth_pct. Default: -purchase_volume\n\nPagination: page (default 1), per_page (default 10).\n\nOptional:\n- growth_basis: spend | sales | profit | margin (default: sales) — drives the growth_pct column\n- min_volume, include_archived\n\nResponse field cost_trend: up | down | flat — direction of the landed unit cost vs the prior period (always present, defaults to flat)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/brands/{brand}/products?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","brands",":brand","products"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"brand","value":"","description":"The brand ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 9001,\n      \"sku\": \"ACM-WIDGET-01\",\n      \"name\": \"Widget Assembly Type A\",\n      \"units_purchased\": 3200,\n      \"units_sold\": 3050,\n      \"purchase_volume\": 42100,\n      \"sales_volume\": 78900,\n      \"profit\": 24600,\n      \"margin_pct\": 31.2,\n      \"sell_through_pct\": 95.3,\n      \"landed_unit_cost\": 13.16,\n      \"cost_trend\": \"up\",\n      \"growth_pct\": 34.2\n    },\n    {\n      \"product_id\": 9044,\n      \"sku\": \"ACM-BRACKET-12\",\n      \"name\": \"Mounting Bracket XL\",\n      \"units_purchased\": 540,\n      \"units_sold\": 410,\n      \"purchase_volume\": 6200,\n      \"sales_volume\": 9800,\n      \"profit\": 2100,\n      \"margin_pct\": 21.4,\n      \"sell_through_pct\": 75.9,\n      \"landed_unit_cost\": 11.48,\n      \"cost_trend\": \"down\",\n      \"growth_pct\": -21.8\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/reporting/brands/42/products?page=1\",\n  \"from\": 1,\n  \"last_page\": 5,\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/reporting/brands/42/products?page=5\",\n  \"next_page_url\": \"{{protocol}}{{domain}}/api/reporting/brands/42/products?page=2\",\n  \"path\": \"{{protocol}}{{domain}}/api/reporting/brands/42/products\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 10,\n  \"total\": 47\n}"}]},{"name":"Get Brand Product Drilldown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/brands/{brand}/products/{product}?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","brands",":brand","products",":product"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"brand","value":"","description":"The brand ID."},{"key":"product","value":"","description":"The product ID."}]},"description":"Single-product drill-down for one brand: the KPI strip (current vs previous window), on-hand units, the default supplier's quoted lead time vs the actual average across all suppliers, landed-cost trend, monthly units purchased vs sold, every supplier carrying the SKU, and the most recent PO lines (any supplier).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameters:\n- brand: the ProductBrand ID\n- product: the Product ID\n\nRequired parameters:\n- period_mode: yoy | qoq | mom | custom\n\nWhen period_mode=custom, date_from and date_to (YYYY-MM-DD or m/d/Y) are both required.\n\nResponse fields:\n- product {id, sku, name}\n- kpis: units_purchased, units_sold, purchase_volume, sales_volume, profit, margin_pct, sell_through_pct, gmroi — each {current, previous, delta_pct}. Amounts and GMROI report % growth (null when previous <= 0); margin_pct / sell_through_pct report the percentage-point change (null when either side has no rate). GMROI = profit / inventory cost at window end.\n- inventory_on_hand: current on-hand units\n- lead_time: quoted lead time in days; avg_actual_lead_time: average days from PO date to full receipt\n- landed_cost_trend {dates, costs}: landed unit cost per PO in the current window\n- units_chart {buckets (Y-m), purchased, sold}: monthly units, zero-filled\n- recent_po_lines: last 10 non-draft PO lines, newest first\n- suppliers: [{supplier_id, name, landed_unit_cost, lead_time, is_default}]"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/brands/{brand}/products/{product}?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","brands",":brand","products",":product"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"brand","value":"","description":"The brand ID."},{"key":"product","value":"","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"brand\": {\n      \"id\": 42,\n      \"name\": \"Acme Housewares\"\n    },\n    \"product\": {\n      \"id\": 9001,\n      \"sku\": \"ACM-WIDGET-01\",\n      \"name\": \"Widget Assembly Type A\"\n    },\n    \"kpis\": {\n      \"units_purchased\": {\n        \"current\": 3200,\n        \"previous\": 2400,\n        \"delta_pct\": 33.3\n      },\n      \"units_sold\": {\n        \"current\": 3050,\n        \"previous\": 2280,\n        \"delta_pct\": 33.8\n      },\n      \"purchase_volume\": {\n        \"current\": 42100,\n        \"previous\": 31400,\n        \"delta_pct\": 34.1\n      },\n      \"sales_volume\": {\n        \"current\": 78900,\n        \"previous\": 58200,\n        \"delta_pct\": 35.6\n      },\n      \"profit\": {\n        \"current\": 24600,\n        \"previous\": 17900,\n        \"delta_pct\": 37.4\n      },\n      \"margin_pct\": {\n        \"current\": 31.2,\n        \"previous\": 30.8,\n        \"delta_pct\": 0.4\n      },\n      \"sell_through_pct\": {\n        \"current\": 95.3,\n        \"previous\": 95,\n        \"delta_pct\": 0.3\n      },\n      \"gmroi\": {\n        \"current\": 2.46,\n        \"previous\": 2.11,\n        \"delta_pct\": 16.6\n      }\n    },\n    \"inventory_on_hand\": 640,\n    \"lead_time\": 28,\n    \"avg_actual_lead_time\": 31.4,\n    \"landed_cost_trend\": {\n      \"dates\": [\n        \"2025-07-12\",\n        \"2025-11-03\",\n        \"2026-02-21\"\n      ],\n      \"costs\": [\n        13.4,\n        13.16,\n        12.95\n      ]\n    },\n    \"units_chart\": {\n      \"buckets\": [\n        \"2025-05\",\n        \"2025-06\",\n        \"2025-07\"\n      ],\n      \"purchased\": [\n        0,\n        0,\n        1200\n      ],\n      \"sold\": [\n        210,\n        240,\n        265\n      ]\n    },\n    \"suppliers\": [\n      {\n        \"supplier_id\": 42,\n        \"name\": \"Acme Components Ltd\",\n        \"landed_unit_cost\": 12.95,\n        \"lead_time\": 28,\n        \"is_default\": true\n      },\n      {\n        \"supplier_id\": 58,\n        \"name\": \"Globex Manufacturing\",\n        \"landed_unit_cost\": 12.1,\n        \"lead_time\": 21,\n        \"is_default\": false\n      }\n    ],\n    \"recent_po_lines\": [\n      {\n        \"po_id\": 5120,\n        \"po_number\": \"PO-103120\",\n        \"purchase_order_date\": \"2026-02-21\",\n        \"quantity\": 1000,\n        \"received_quantity\": 1000,\n        \"landed_unit_cost\": 12.95\n      },\n      {\n        \"po_id\": 4981,\n        \"po_number\": \"PO-101655\",\n        \"purchase_order_date\": \"2025-11-03\",\n        \"quantity\": 1000,\n        \"received_quantity\": 950,\n        \"landed_unit_cost\": 13.16\n      }\n    ],\n    \"meta\": {\n      \"data_as_of\": \"2026-05-14T03:12:08+00:00\",\n      \"period_label\": \"YoY — May 2025 – May 2026 vs May 2024 – May 2025\",\n      \"current_range\": [\n        \"2025-05-16\",\n        \"2026-05-15\"\n      ],\n      \"previous_range\": [\n        \"2024-05-16\",\n        \"2025-05-15\"\n      ],\n      \"is_partial\": false,\n      \"is_live_query\": false\n    }\n  }\n}"}]},{"name":"Get Brand PO History","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/brands/{brand}/po-history","host":["{{baseUrl}}"],"path":["api","reporting","brands",":brand","po-history"],"variable":[{"key":"brand","value":"42","description":"The Brand ID"}]},"description":"Recent purchase orders containing one brand's products — backs the \"Recent Purchase Orders\" mini-table on the brand Analytics tab. Returns up to the 10 most recent non-draft purchase orders ordered by purchase order date (DESC), each with its receipt status and an on-time flag.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameters:\n- brand: the Brand ID\n\nThis endpoint is period-independent — it always returns the most recent purchase orders and takes no query parameters. Draft purchase orders are excluded.\n\nResponse fields per row:\n- id: purchase order ID\n- po_number: human-readable purchase order number\n- purchase_order_date: order date (Y-m-d)\n- total_cost: total purchase order cost\n- receipt_status: receipt status of the PO (e.g. unreceived, partially_received, fully_received)\n- on_time: true if fully received on or before the estimated delivery date, false if late, null if not yet fully received"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/brands/{brand}/po-history","host":["{{baseUrl}}"],"path":["api","reporting","brands",":brand","po-history"],"variable":[{"key":"brand","value":"42","description":"The Brand ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 8821,\n      \"po_number\": \"PO-2025-0142\",\n      \"purchase_order_date\": \"2025-05-08\",\n      \"total_cost\": 42100,\n      \"receipt_status\": \"fully_received\",\n      \"on_time\": true\n    },\n    {\n      \"id\": 8790,\n      \"po_number\": \"PO-2025-0118\",\n      \"purchase_order_date\": \"2025-04-22\",\n      \"total_cost\": 6200.5,\n      \"receipt_status\": \"partially_received\",\n      \"on_time\": null\n    },\n    {\n      \"id\": 8755,\n      \"po_number\": \"PO-2025-0091\",\n      \"purchase_order_date\": \"2025-03-30\",\n      \"total_cost\": 18750,\n      \"receipt_status\": \"fully_received\",\n      \"on_time\": false\n    }\n  ]\n}"}]},{"name":"Get Brand Scorecard","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/brands/{brand}/scorecard?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","brands",":brand","scorecard"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"brand","value":"","description":"The brand ID."}]},"description":"Standalone composite scorecard for a brand over the resolved period — the composite, its band, and the per-dimension breakdown.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe band is one of: excellent, good, needs_attention, poor. When the brand has fewer received POs than the configured minimum (is_sufficient=false), the band is not_rated and composite is 0.\n\nLive preview: when the optional preview_weights, preview_thresholds and preview_min_po_count parameters are supplied (all three together), the score is computed against a transient, non-persisted settings instance built from those values. Nothing is written to the scorecard settings table. preview_weights must contain all six metric keys and sum to exactly 100.\n\nAuthentication: Requires Bearer token (scope: reports)\n\nPath parameters:\n- brand: the Brand ID\n\nRequired parameters:\n- period_mode: yoy | qoq | mom | custom\n\nWhen period_mode=custom, date_from and date_to (YYYY-MM-DD or m/d/Y) are both required.\n\nOptional period params:\n- growth_basis: spend | sales | profit | margin (default: sales)\n- min_volume, include_archived\n\nOptional live-preview params (all three required together when previewing):\n- preview_weights[{metric}]: numeric 0-100 — one per metric, summing to 100\n- preview_thresholds[{metric}][{i}][min]: numeric; preview_thresholds[{metric}][{i}][score]: integer 1-5\n- preview_min_po_count: integer, min 1\n\npreview_weights and preview_thresholds may be passed either as bracketed array params (shown above) or as a single URL-encoded JSON string — the live-preview UI sends them as JSON strings."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/brands/{brand}/scorecard?period_mode=yoy","host":["{{baseUrl}}"],"path":["api","reporting","brands",":brand","scorecard"],"query":[{"key":"period_mode","value":"yoy","description":"Required. Period comparison model: yoy | qoq | mom | custom","disabled":true}],"variable":[{"key":"brand","value":"","description":"The brand ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"composite\": 4.15,\n    \"band\": \"good\",\n    \"is_sufficient\": true,\n    \"po_count\": 18,\n    \"dimensions\": [\n      {\n        \"key\": \"on_time_delivery\",\n        \"label\": \"On-Time Delivery\",\n        \"raw\": 92.3,\n        \"raw_display\": \"92.3%\",\n        \"score\": 4,\n        \"weight\": 25\n      },\n      {\n        \"key\": \"lead_time_reliability\",\n        \"label\": \"Lead-Time Reliability\",\n        \"raw\": 88,\n        \"raw_display\": \"88.0%\",\n        \"score\": 3,\n        \"weight\": 15\n      },\n      {\n        \"key\": \"fill_rate\",\n        \"label\": \"Fill Rate\",\n        \"raw\": 98.6,\n        \"raw_display\": \"98.6%\",\n        \"score\": 5,\n        \"weight\": 15\n      },\n      {\n        \"key\": \"defect_rate\",\n        \"label\": \"Defect / RTV Rate\",\n        \"raw\": 1.2,\n        \"raw_display\": \"1.2%\",\n        \"score\": 5,\n        \"weight\": 15\n      },\n      {\n        \"key\": \"cost_competitiveness\",\n        \"label\": \"Cost Competitiveness\",\n        \"raw\": 74.5,\n        \"raw_display\": \"74.5 / 100\",\n        \"score\": 3,\n        \"weight\": 25\n      },\n      {\n        \"key\": \"price_stability\",\n        \"label\": \"Price Stability\",\n        \"raw\": 0.18,\n        \"raw_display\": \"18% price increases\",\n        \"score\": 4,\n        \"weight\": 5\n      }\n    ]\n  }\n}"}]},{"name":"Get Product Data Health Metrics","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/product-data-health/metrics","host":["{{baseUrl}}"],"path":["api","product-data-health","metrics"]},"description":"Aggregated product data-health metrics for the Product Data Health dashboard: product-type distribution, active/archived breakdown, unit-cost health (>0 / =0 / null), brand and supplier coverage, supplier-price health, kit/bundle component gaps, pricing coverage, image health, and sales-channel mapping coverage.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nImage health: `products_with_image` / `products_without_image` reflect image presence (a primary or any product image). `products_with_broken_image` counts products whose primary image was checked and found unresolvable by the on-demand image-health check (see POST /api/product-data-health/check-image-health). `image_health_last_checked_at` is the most recent time any product image was validated (UTC, ISO-8601), or null if no check has ever run.\n\nAuthentication: Requires Bearer token (scope: products read-write).\n\nOptional filters (all narrow every metric):\n- archived_status: active | archived — restrict to active or archived products\n- brand_id: integer — restrict to a single brand\n- default_supplier_id: integer — restrict to products whose default supplier matches"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/product-data-health/metrics","host":["{{baseUrl}}"],"path":["api","product-data-health","metrics"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"product_counts_by_type\": {\n    \"standard\": 1240,\n    \"kit\": 85,\n    \"bundle\": 42,\n    \"variation\": 30,\n    \"variation_child\": 180\n  },\n  \"total_products\": 1577,\n  \"active_products\": 1402,\n  \"archived_products\": 175,\n  \"active_products_by_type\": {\n    \"standard\": 1100,\n    \"kit\": 78,\n    \"bundle\": 38,\n    \"variation\": 28,\n    \"variation_child\": 158\n  },\n  \"archived_products_by_type\": {\n    \"standard\": 140,\n    \"kit\": 7,\n    \"bundle\": 4,\n    \"variation\": 2,\n    \"variation_child\": 22\n  },\n  \"products_with_unit_cost\": 1180,\n  \"products_with_zero_unit_cost\": 95,\n  \"products_with_null_unit_cost\": 302,\n  \"products_with_unit_cost_by_type\": {\n    \"standard\": 980,\n    \"kit\": 60,\n    \"bundle\": 30,\n    \"variation_child\": 110\n  },\n  \"products_with_zero_unit_cost_by_type\": {\n    \"standard\": 80,\n    \"kit\": 5,\n    \"bundle\": 4,\n    \"variation_child\": 6\n  },\n  \"products_with_null_unit_cost_by_type\": {\n    \"standard\": 180,\n    \"kit\": 20,\n    \"bundle\": 8,\n    \"variation_child\": 64\n  },\n  \"products_with_brand\": 1320,\n  \"products_without_brand\": 257,\n  \"brand_distribution\": [\n    {\n      \"name\": \"Acme\",\n      \"count\": 540\n    },\n    {\n      \"name\": \"Globex\",\n      \"count\": 320\n    },\n    {\n      \"name\": \"Unbranded\",\n      \"count\": 257\n    }\n  ],\n  \"products_with_supplier\": 1190,\n  \"products_without_supplier\": 387,\n  \"products_with_supplier_by_type\": {\n    \"standard\": 1000,\n    \"variation_child\": 190\n  },\n  \"products_without_supplier_by_type\": {\n    \"standard\": 240,\n    \"variation_child\": 147\n  },\n  \"supplier_count_distribution\": [\n    {\n      \"supplier_count\": 1,\n      \"products\": 900\n    },\n    {\n      \"supplier_count\": 2,\n      \"products\": 220\n    },\n    {\n      \"supplier_count\": 3,\n      \"products\": 70\n    }\n  ],\n  \"products_with_zero_supplier_price\": 64,\n  \"products_with_non_zero_supplier_price\": 1126,\n  \"default_supplier_pricing_tier_name\": \"Default Cost\",\n  \"kits_without_components\": 3,\n  \"bundles_without_components\": 1,\n  \"total_kits\": 85,\n  \"total_bundles\": 42,\n  \"products_with_pricing\": 1450,\n  \"products_without_pricing\": 127,\n  \"products_without_pricing_by_type\": {\n    \"standard\": 90,\n    \"kit\": 12,\n    \"bundle\": 5,\n    \"variation_child\": 20\n  },\n  \"default_pricing_tier_name\": \"Retail\",\n  \"products_with_image\": 1390,\n  \"products_without_image\": 187,\n  \"products_without_image_by_type\": {\n    \"standard\": 150,\n    \"kit\": 18,\n    \"bundle\": 9,\n    \"variation_child\": 10\n  },\n  \"products_with_broken_image\": 12,\n  \"image_health_last_checked_at\": \"2026-06-09T14:32:00.000000Z\",\n  \"mapped_product_listings\": 2104,\n  \"unmapped_sales_channel_products\": 96,\n  \"sales_channel_mapping_by_channel\": [\n    {\n      \"name\": \"Amazon US\",\n      \"mapped\": 980,\n      \"unmapped\": 40,\n      \"integration_instance_id\": 12,\n      \"channel_type\": \"amazon\"\n    },\n    {\n      \"name\": \"Shopify Storefront\",\n      \"mapped\": 1124,\n      \"unmapped\": 56,\n      \"integration_instance_id\": 7,\n      \"channel_type\": \"shopify\"\n    }\n  ]\n}"}]},{"name":"Check Image Health","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/product-data-health/check-image-health","host":["{{baseUrl}}"],"path":["api","product-data-health","check-image-health"]},"description":"Dispatch the on-demand image-health check job. The job re-validates every product image — local (disk) images are checked for existence on their storage disk, remote (URL) images via an HTTP request — records the result on each product image (resolvable + checked-at), and surfaces progress through the tracked job log endpoints.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the tracked job log id so the frontend can follow progress via the tracked job log endpoints. There is no request body.\n\nAuthentication: Requires Bearer token (scope: products read-write)."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-data-health/check-image-health","host":["{{baseUrl}}"],"path":["api","product-data-health","check-image-health"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 84213\n  },\n  \"message\": \"Image health check started\"\n}"}]},{"name":"List Supplier IRR Ranking","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/irr/suppliers","host":["{{baseUrl}}"],"path":["api","reporting","irr","suppliers"]},"description":"Rank every supplier by the pooled internal rate of return on the cash deployed with them.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach row's `xirr` is the annualized money-weighted return (0.549 = 54.9%), pooled across all the entity's non-draft purchase orders — deposit and balance payments out, then net sales proceeds in as the received units sell through, plus remaining stock valued at cost as of today. Also returns the cash multiple (`moic`), profit, capital deployed, days deployed, and units sold. Rows are ranked by IRR (unsolvable rows sort last with `is_rankable=false`). Amounts are in the account's base currency. Requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/irr/suppliers","host":["{{baseUrl}}"],"path":["api","reporting","irr","suppliers"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"currency_code\": \"USD\",\n    \"last_refreshed_at\": \"2026-07-18T04:30:11+00:00\",\n    \"eligible_po_count\": 47,\n    \"covered_po_count\": 47,\n    \"pending_po_count\": 0,\n    \"summary\": {\n      \"entity_count\": 3,\n      \"ranked_count\": 3,\n      \"total_capital_deployed\": 26750,\n      \"total_profit\": 8125.8,\n      \"blended_moic\": 1.3038,\n      \"best\": {\n        \"id\": 12,\n        \"name\": \"Rainier Roasters\",\n        \"sku\": null,\n        \"xirr\": 5.4903,\n        \"period_return\": 0.05,\n        \"moic\": 1.52,\n        \"profit\": 4653,\n        \"capital_deployed\": 9000,\n        \"total_cash_in\": 13653,\n        \"npv_at_hurdle\": 4091.55,\n        \"days_capital_deployed\": 150,\n        \"po_count\": 1,\n        \"units_bought\": 1000,\n        \"units_sold\": 188,\n        \"pct_realized\": 0.94,\n        \"selling_since\": \"2025-02-14\",\n        \"is_short_hold\": false,\n        \"is_provisional\": false,\n        \"is_rankable\": true\n      },\n      \"worst\": {\n        \"id\": 21,\n        \"name\": \"Umbrella Goods\",\n        \"sku\": null,\n        \"xirr\": -0.0312,\n        \"period_return\": 0.05,\n        \"moic\": 0.98,\n        \"profit\": -197.6,\n        \"capital_deployed\": 9750,\n        \"total_cash_in\": 9552.4,\n        \"npv_at_hurdle\": -410,\n        \"days_capital_deployed\": 280,\n        \"po_count\": 1,\n        \"units_bought\": 1000,\n        \"units_sold\": 38,\n        \"pct_realized\": 0.25,\n        \"selling_since\": \"2025-02-14\",\n        \"is_short_hold\": false,\n        \"is_provisional\": false,\n        \"is_rankable\": true\n      }\n    },\n    \"rows\": [\n      {\n        \"id\": 12,\n        \"name\": \"Rainier Roasters\",\n        \"sku\": null,\n        \"xirr\": 5.4903,\n        \"period_return\": 0.05,\n        \"moic\": 1.52,\n        \"profit\": 4653,\n        \"capital_deployed\": 9000,\n        \"total_cash_in\": 13653,\n        \"npv_at_hurdle\": 4091.55,\n        \"days_capital_deployed\": 150,\n        \"po_count\": 1,\n        \"units_bought\": 1000,\n        \"units_sold\": 188,\n        \"pct_realized\": 0.94,\n        \"selling_since\": \"2025-02-14\",\n        \"is_short_hold\": false,\n        \"is_provisional\": false,\n        \"is_rankable\": true\n      },\n      {\n        \"id\": 7,\n        \"name\": \"Acme Supplies\",\n        \"sku\": null,\n        \"xirr\": 1.6112,\n        \"period_return\": 0.05,\n        \"moic\": 1.46,\n        \"profit\": 3670.4,\n        \"capital_deployed\": 8000,\n        \"total_cash_in\": 11670.4,\n        \"npv_at_hurdle\": 3120.1,\n        \"days_capital_deployed\": 240,\n        \"po_count\": 1,\n        \"units_bought\": 1000,\n        \"units_sold\": 74,\n        \"pct_realized\": 0.74,\n        \"selling_since\": \"2025-02-14\",\n        \"is_short_hold\": false,\n        \"is_provisional\": false,\n        \"is_rankable\": true\n      },\n      {\n        \"id\": 21,\n        \"name\": \"Umbrella Goods\",\n        \"sku\": null,\n        \"xirr\": -0.0312,\n        \"period_return\": 0.05,\n        \"moic\": 0.98,\n        \"profit\": -197.6,\n        \"capital_deployed\": 9750,\n        \"total_cash_in\": 9552.4,\n        \"npv_at_hurdle\": -410,\n        \"days_capital_deployed\": 280,\n        \"po_count\": 1,\n        \"units_bought\": 1000,\n        \"units_sold\": 38,\n        \"pct_realized\": 0.25,\n        \"selling_since\": \"2025-02-14\",\n        \"is_short_hold\": false,\n        \"is_provisional\": false,\n        \"is_rankable\": true\n      }\n    ]\n  }\n}"}]},{"name":"List Product IRR Ranking","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/irr/products","host":["{{baseUrl}}"],"path":["api","reporting","irr","products"]},"description":"Rank every product by the internal rate of return on its purchasing cash flows (each order's flows prorated to the product by its cost share of the order).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach row's `xirr` is the annualized money-weighted return (0.549 = 54.9%), pooled across all the entity's non-draft purchase orders — deposit and balance payments out, then net sales proceeds in as the received units sell through, plus remaining stock valued at cost as of today. Also returns the cash multiple (`moic`), profit, capital deployed, days deployed, and units sold. Rows are ranked by IRR (unsolvable rows sort last with `is_rankable=false`). Amounts are in the account's base currency. Requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/irr/products","host":["{{baseUrl}}"],"path":["api","reporting","irr","products"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"currency_code\": \"USD\",\n    \"last_refreshed_at\": \"2026-07-18T04:30:11+00:00\",\n    \"eligible_po_count\": 47,\n    \"covered_po_count\": 47,\n    \"pending_po_count\": 0,\n    \"summary\": {\n      \"entity_count\": 2,\n      \"ranked_count\": 2,\n      \"total_capital_deployed\": 16200,\n      \"total_profit\": 8109,\n      \"blended_moic\": 1.5006,\n      \"best\": {\n        \"id\": 31,\n        \"name\": \"Cold Brew Concentrate 32oz\",\n        \"sku\": \"CB-32\",\n        \"xirr\": 5.4903,\n        \"period_return\": 0.05,\n        \"moic\": 1.52,\n        \"profit\": 4653,\n        \"capital_deployed\": 9000,\n        \"total_cash_in\": 13653,\n        \"npv_at_hurdle\": 4091.55,\n        \"days_capital_deployed\": 150,\n        \"po_count\": 1,\n        \"units_bought\": 1000,\n        \"units_sold\": 188,\n        \"pct_realized\": 0.94,\n        \"selling_since\": \"2025-02-14\",\n        \"is_short_hold\": false,\n        \"is_provisional\": false,\n        \"is_rankable\": true\n      },\n      \"worst\": {\n        \"id\": 44,\n        \"name\": \"Gooseneck Kettle 1.2L\",\n        \"sku\": \"KET-GN-12\",\n        \"xirr\": 1.5701,\n        \"period_return\": 0.05,\n        \"moic\": 1.48,\n        \"profit\": 3456,\n        \"capital_deployed\": 7200,\n        \"total_cash_in\": 10656,\n        \"npv_at_hurdle\": 2980.2,\n        \"days_capital_deployed\": 320,\n        \"po_count\": 1,\n        \"units_bought\": 1000,\n        \"units_sold\": 90,\n        \"pct_realized\": 1,\n        \"selling_since\": \"2025-02-14\",\n        \"is_short_hold\": false,\n        \"is_provisional\": false,\n        \"is_rankable\": true\n      }\n    },\n    \"rows\": [\n      {\n        \"id\": 31,\n        \"name\": \"Cold Brew Concentrate 32oz\",\n        \"sku\": \"CB-32\",\n        \"xirr\": 5.4903,\n        \"period_return\": 0.05,\n        \"moic\": 1.52,\n        \"profit\": 4653,\n        \"capital_deployed\": 9000,\n        \"total_cash_in\": 13653,\n        \"npv_at_hurdle\": 4091.55,\n        \"days_capital_deployed\": 150,\n        \"po_count\": 1,\n        \"units_bought\": 1000,\n        \"units_sold\": 188,\n        \"pct_realized\": 0.94,\n        \"selling_since\": \"2025-02-14\",\n        \"is_short_hold\": false,\n        \"is_provisional\": false,\n        \"is_rankable\": true\n      },\n      {\n        \"id\": 44,\n        \"name\": \"Gooseneck Kettle 1.2L\",\n        \"sku\": \"KET-GN-12\",\n        \"xirr\": 1.5701,\n        \"period_return\": 0.05,\n        \"moic\": 1.48,\n        \"profit\": 3456,\n        \"capital_deployed\": 7200,\n        \"total_cash_in\": 10656,\n        \"npv_at_hurdle\": 2980.2,\n        \"days_capital_deployed\": 320,\n        \"po_count\": 1,\n        \"units_bought\": 1000,\n        \"units_sold\": 90,\n        \"pct_realized\": 1,\n        \"selling_since\": \"2025-02-14\",\n        \"is_short_hold\": false,\n        \"is_provisional\": false,\n        \"is_rankable\": true\n      }\n    ]\n  }\n}"}]},{"name":"List Supplier Product IRR Breakdown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/irr/suppliers/{supplier}/products","host":["{{baseUrl}}"],"path":["api","reporting","irr","suppliers",":supplier","products"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]},"description":"For one supplier, return its own pooled IRR (in `supplier`) plus every product purchased from it, ranked by IRR.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach row's `xirr` is the annualized money-weighted return (0.549 = 54.9%), pooled across all the entity's non-draft purchase orders — deposit and balance payments out, then net sales proceeds in as the received units sell through, plus remaining stock valued at cost as of today. Also returns the cash multiple (`moic`), profit, capital deployed, days deployed, and units sold. Rows are ranked by IRR (unsolvable rows sort last with `is_rankable=false`). Amounts are in the account's base currency. Requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/irr/suppliers/{supplier}/products","host":["{{baseUrl}}"],"path":["api","reporting","irr","suppliers",":supplier","products"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"currency_code\": \"USD\",\n    \"last_refreshed_at\": \"2026-07-18T04:30:11+00:00\",\n    \"eligible_po_count\": 47,\n    \"covered_po_count\": 47,\n    \"pending_po_count\": 0,\n    \"summary\": {\n      \"entity_count\": 1,\n      \"ranked_count\": 1,\n      \"total_capital_deployed\": 9000,\n      \"total_profit\": 4653,\n      \"blended_moic\": 1.517,\n      \"best\": {\n        \"id\": 31,\n        \"name\": \"Cold Brew Concentrate 32oz\",\n        \"sku\": \"CB-32\",\n        \"xirr\": 5.4903,\n        \"period_return\": 0.05,\n        \"moic\": 1.52,\n        \"profit\": 4653,\n        \"capital_deployed\": 9000,\n        \"total_cash_in\": 13653,\n        \"npv_at_hurdle\": 4091.55,\n        \"days_capital_deployed\": 150,\n        \"po_count\": 1,\n        \"units_bought\": 1000,\n        \"units_sold\": 188,\n        \"pct_realized\": 0.94,\n        \"selling_since\": \"2025-02-14\",\n        \"is_short_hold\": false,\n        \"is_provisional\": false,\n        \"is_rankable\": true\n      },\n      \"worst\": {\n        \"id\": 31,\n        \"name\": \"Cold Brew Concentrate 32oz\",\n        \"sku\": \"CB-32\",\n        \"xirr\": 5.4903,\n        \"period_return\": 0.05,\n        \"moic\": 1.52,\n        \"profit\": 4653,\n        \"capital_deployed\": 9000,\n        \"total_cash_in\": 13653,\n        \"npv_at_hurdle\": 4091.55,\n        \"days_capital_deployed\": 150,\n        \"po_count\": 1,\n        \"units_bought\": 1000,\n        \"units_sold\": 188,\n        \"pct_realized\": 0.94,\n        \"selling_since\": \"2025-02-14\",\n        \"is_short_hold\": false,\n        \"is_provisional\": false,\n        \"is_rankable\": true\n      }\n    },\n    \"supplier\": {\n      \"id\": 12,\n      \"name\": \"Rainier Roasters\",\n      \"sku\": null,\n      \"xirr\": 5.4903,\n      \"period_return\": 0.05,\n      \"moic\": 1.52,\n      \"profit\": 4653,\n      \"capital_deployed\": 9000,\n      \"total_cash_in\": 13653,\n      \"npv_at_hurdle\": 4091.55,\n      \"days_capital_deployed\": 150,\n      \"po_count\": 1,\n      \"units_bought\": 1000,\n      \"units_sold\": 188,\n      \"pct_realized\": 0.94,\n      \"selling_since\": \"2025-02-14\",\n      \"is_short_hold\": false,\n      \"is_provisional\": false,\n      \"is_rankable\": true\n    },\n    \"rows\": [\n      {\n        \"id\": 31,\n        \"name\": \"Cold Brew Concentrate 32oz\",\n        \"sku\": \"CB-32\",\n        \"xirr\": 5.4903,\n        \"period_return\": 0.05,\n        \"moic\": 1.52,\n        \"profit\": 4653,\n        \"capital_deployed\": 9000,\n        \"total_cash_in\": 13653,\n        \"npv_at_hurdle\": 4091.55,\n        \"days_capital_deployed\": 150,\n        \"po_count\": 1,\n        \"units_bought\": 1000,\n        \"units_sold\": 188,\n        \"pct_realized\": 0.94,\n        \"selling_since\": \"2025-02-14\",\n        \"is_short_hold\": false,\n        \"is_provisional\": false,\n        \"is_rankable\": true\n      }\n    ]\n  }\n}"}]},{"name":"List Supplier Reconciliation Events","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/irr/suppliers/{supplier}/reconciliation-events","host":["{{baseUrl}}"],"path":["api","reporting","irr","suppliers",":supplier","reconciliation-events"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]},"description":"The bulk stock-take write-off events behind a supplier's reconciliation figure, grouped by stock-take (largest first) with per-product lines. Each event carries the stock-take date, warehouse, status, mode (full_count or adjustment), variance direction, units, and cost value, plus a link to the stock-take record. Use to see exactly which physical counts wrote off inventory.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: requires a Bearer token.\n\nQuery params: `since` (optional) restricts to purchase orders placed on/after the date."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/irr/suppliers/{supplier}/reconciliation-events","host":["{{baseUrl}}"],"path":["api","reporting","irr","suppliers",":supplier","reconciliation-events"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"entity\": {\n      \"type\": \"supplier\",\n      \"id\": 17,\n      \"name\": \"Example Supplier\",\n      \"sku\": null\n    },\n    \"currency_code\": \"USD\",\n    \"total_value\": 28416.43,\n    \"total_units\": 234,\n    \"event_count\": 2,\n    \"events\": [\n      {\n        \"stock_take_id\": 147,\n        \"reference\": \"Stock Take #147\",\n        \"link_route\": \"/inventory/stock-takes/147?tab=movements\",\n        \"date\": \"2026-06-11\",\n        \"warehouse\": \"Main Warehouse\",\n        \"status\": \"closed\",\n        \"mode\": \"full_count\",\n        \"variance_direction\": \"mixed\",\n        \"notes\": \"Stock take created from inventory discrepancies found during the fulfillment-provider sync.\",\n        \"value_change\": -45028.88,\n        \"units\": 233,\n        \"value\": 28305.58,\n        \"lines\": [\n          {\n            \"product_id\": 517,\n            \"product_name\": \"Diagnostic Scanner Pro\",\n            \"sku\": \"SCAN-PRO\",\n            \"product_route\": \"/products/517\",\n            \"units\": 28,\n            \"value\": 7154.7,\n            \"unit_cost\": 255.53,\n            \"variance_reason\": \"damage\",\n            \"variance_reason_label\": \"Damage\",\n            \"counted_before\": 75,\n            \"counted_after\": 47,\n            \"purchase_orders\": [\n              {\n                \"id\": 373,\n                \"number\": \"PO-0318\",\n                \"route\": \"/orders/purchase-orders/373\"\n              }\n            ]\n          }\n        ]\n      }\n    ]\n  }\n}"}]},{"name":"List Product Reconciliation Events","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/irr/products/{product}/reconciliation-events","host":["{{baseUrl}}"],"path":["api","reporting","irr","products",":product","reconciliation-events"],"variable":[{"key":"product","value":"34","description":"Product ID"}]},"description":"The bulk stock-take write-off events behind a product's reconciliation figure, grouped by stock-take (largest first). Each event carries the stock-take date, warehouse, status, mode, variance direction, units, and cost value, plus a link to the stock-take record.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: requires a Bearer token.\n\nQuery params: `since` (optional) restricts to purchase orders placed on/after the date."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/irr/products/{product}/reconciliation-events","host":["{{baseUrl}}"],"path":["api","reporting","irr","products",":product","reconciliation-events"],"variable":[{"key":"product","value":"34","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"entity\": {\n      \"type\": \"supplier\",\n      \"id\": 17,\n      \"name\": \"Example Supplier\",\n      \"sku\": null\n    },\n    \"currency_code\": \"USD\",\n    \"total_value\": 28416.43,\n    \"total_units\": 234,\n    \"event_count\": 2,\n    \"events\": [\n      {\n        \"stock_take_id\": 147,\n        \"reference\": \"Stock Take #147\",\n        \"link_route\": \"/inventory/stock-takes/147?tab=movements\",\n        \"date\": \"2026-06-11\",\n        \"warehouse\": \"Main Warehouse\",\n        \"status\": \"closed\",\n        \"mode\": \"full_count\",\n        \"variance_direction\": \"mixed\",\n        \"notes\": \"Stock take created from inventory discrepancies found during the fulfillment-provider sync.\",\n        \"value_change\": -45028.88,\n        \"units\": 233,\n        \"value\": 28305.58,\n        \"lines\": [\n          {\n            \"product_id\": 517,\n            \"product_name\": \"Diagnostic Scanner Pro\",\n            \"sku\": \"SCAN-PRO\",\n            \"product_route\": \"/products/517\",\n            \"units\": 28,\n            \"value\": 7154.7,\n            \"unit_cost\": 255.53,\n            \"variance_reason\": \"damage\",\n            \"variance_reason_label\": \"Damage\",\n            \"counted_before\": 75,\n            \"counted_after\": 47,\n            \"purchase_orders\": [\n              {\n                \"id\": 373,\n                \"number\": \"PO-0318\",\n                \"route\": \"/orders/purchase-orders/373\"\n              }\n            ]\n          }\n        ]\n      }\n    ]\n  }\n}"}]},{"name":"Get Supplier Cash-Flow Proof","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/irr/suppliers/{supplier}/cash-flows","host":["{{baseUrl}}"],"path":["api","reporting","irr","suppliers",":supplier","cash-flows"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]},"description":"The full proof behind a supplier's pooled return: the pooled metrics, a monthly cash-flow shape (money-in/out + running cumulative for charts), the dated cash-flow ledger (every flow the MIRR is solved on, each linked to its source document), and a per-purchase-order contribution breakdown.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/irr/suppliers/{supplier}/cash-flows","host":["{{baseUrl}}"],"path":["api","reporting","irr","suppliers",":supplier","cash-flows"],"variable":[{"key":"supplier","value":"12","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"entity\": {\n      \"type\": \"supplier\",\n      \"id\": 4,\n      \"name\": \"Example Supplier\",\n      \"sku\": null\n    },\n    \"currency_code\": \"USD\",\n    \"metrics\": {\n      \"id\": 4,\n      \"name\": \"Example Supplier\",\n      \"xirr\": 0.93,\n      \"xirr_exact\": null,\n      \"moic\": 1.89,\n      \"profit\": 67425,\n      \"capital_deployed\": 75480,\n      \"total_cash_in\": 142905,\n      \"days_capital_deployed\": 472,\n      \"po_count\": 2,\n      \"units_sold\": 1641,\n      \"pct_realized\": 0.66,\n      \"is_rankable\": true\n    },\n    \"monthly\": [\n      {\n        \"month\": \"2025-01\",\n        \"inflow\": 212.5,\n        \"outflow\": 0,\n        \"net\": 212.5,\n        \"cumulative\": 212.5\n      },\n      {\n        \"month\": \"2025-02\",\n        \"inflow\": 1078.31,\n        \"outflow\": -65480,\n        \"net\": -64401.69,\n        \"cumulative\": -64189.19\n      }\n    ],\n    \"flows\": [\n      {\n        \"date\": \"2025-02-19\",\n        \"amount\": -65480,\n        \"type\": \"deposit\",\n        \"label\": \"Vendor deposit\",\n        \"source_type\": \"App\\\\Models\\\\VendorDeposit\",\n        \"source_type_label\": \"Vendor Deposit\",\n        \"source_id\": 812,\n        \"source_route\": null\n      },\n      {\n        \"date\": \"2025-04-18\",\n        \"amount\": 9180,\n        \"type\": \"sale\",\n        \"label\": \"Sale of 42 unit(s)\",\n        \"source_type\": \"App\\\\Models\\\\SalesOrderLine\",\n        \"source_type_label\": \"Sales Order Line\",\n        \"source_id\": 10233,\n        \"source_route\": null\n      }\n    ],\n    \"flows_total\": 510,\n    \"purchase_orders\": [\n      {\n        \"id\": 312,\n        \"number\": \"PO-0269\",\n        \"route\": \"/orders/purchase-orders/312\",\n        \"date\": \"2025-02-19\",\n        \"date_from\": \"2025-02-19\",\n        \"date_to\": \"2026-08-17\",\n        \"capital\": 65480,\n        \"cash_in\": 142904.65,\n        \"profit\": 77424.65,\n        \"moic\": 2.1824,\n        \"xirr\": 1.12,\n        \"days_held\": 544,\n        \"pct_realized\": 0.66,\n        \"sold_on_terms\": false\n      }\n    ]\n  }\n}"}]},{"name":"Get Product Cash-Flow Proof","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/irr/products/{product}/cash-flows","host":["{{baseUrl}}"],"path":["api","reporting","irr","products",":product","cash-flows"],"variable":[{"key":"product","value":"34","description":"Product ID"}]},"description":"The full proof behind a product's pooled return (cash flows prorated to the product's cost share of each order): pooled metrics, monthly cash-flow shape, the dated ledger, and per-purchase-order breakdown.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/irr/products/{product}/cash-flows","host":["{{baseUrl}}"],"path":["api","reporting","irr","products",":product","cash-flows"],"variable":[{"key":"product","value":"34","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"entity\": {\n      \"type\": \"product\",\n      \"id\": 4,\n      \"name\": \"Example Product\",\n      \"sku\": null\n    },\n    \"currency_code\": \"USD\",\n    \"metrics\": {\n      \"id\": 4,\n      \"name\": \"Example Supplier\",\n      \"xirr\": 0.93,\n      \"xirr_exact\": null,\n      \"moic\": 1.89,\n      \"profit\": 67425,\n      \"capital_deployed\": 75480,\n      \"total_cash_in\": 142905,\n      \"days_capital_deployed\": 472,\n      \"po_count\": 2,\n      \"units_sold\": 1641,\n      \"pct_realized\": 0.66,\n      \"is_rankable\": true\n    },\n    \"monthly\": [\n      {\n        \"month\": \"2025-01\",\n        \"inflow\": 212.5,\n        \"outflow\": 0,\n        \"net\": 212.5,\n        \"cumulative\": 212.5\n      },\n      {\n        \"month\": \"2025-02\",\n        \"inflow\": 1078.31,\n        \"outflow\": -65480,\n        \"net\": -64401.69,\n        \"cumulative\": -64189.19\n      }\n    ],\n    \"flows\": [\n      {\n        \"date\": \"2025-02-19\",\n        \"amount\": -65480,\n        \"type\": \"deposit\",\n        \"label\": \"Vendor deposit\",\n        \"source_type\": \"App\\\\Models\\\\VendorDeposit\",\n        \"source_type_label\": \"Vendor Deposit\",\n        \"source_id\": 812,\n        \"source_route\": null\n      },\n      {\n        \"date\": \"2025-04-18\",\n        \"amount\": 9180,\n        \"type\": \"sale\",\n        \"label\": \"Sale of 42 unit(s)\",\n        \"source_type\": \"App\\\\Models\\\\SalesOrderLine\",\n        \"source_type_label\": \"Sales Order Line\",\n        \"source_id\": 10233,\n        \"source_route\": null\n      }\n    ],\n    \"flows_total\": 510,\n    \"purchase_orders\": [\n      {\n        \"id\": 312,\n        \"number\": \"PO-0269\",\n        \"route\": \"/orders/purchase-orders/312\",\n        \"date\": \"2025-02-19\",\n        \"date_from\": \"2025-02-19\",\n        \"date_to\": \"2026-08-17\",\n        \"capital\": 65480,\n        \"cash_in\": 142904.65,\n        \"profit\": 77424.65,\n        \"moic\": 2.1824,\n        \"xirr\": 1.12,\n        \"days_held\": 544,\n        \"pct_realized\": 0.66,\n        \"sold_on_terms\": false\n      }\n    ]\n  }\n}"}]},{"name":"Refresh IRR Metrics","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/irr/refresh","host":["{{baseUrl}}"],"path":["api","reporting","irr","refresh"]},"description":"Recompute every eligible purchase order's return metric in the background so the supplier and product IRR leaderboards reflect fresh figures. The leaderboards read these persisted metrics, so a refresh is what updates `last_refreshed_at` and clears any pending (not-yet-computed) purchase orders.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRuns as a background job and returns immediately. Idempotent: if a refresh is already running, `started` is `false` and no second run is queued. Requires a Bearer token with reports write access."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/reporting/irr/refresh","host":["{{baseUrl}}"],"path":["api","reporting","irr","refresh"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"started\": true,\n    \"tracked_job_log_id\": 4821\n  }\n}"}]},{"name":"List Supplier Pickup Orders","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reports/supplier-pickup-orders","host":["{{baseUrl}}"],"path":["api","reports","supplier-pickup-orders"]},"description":"Paginated Supplier Pickup Orders report — one row per drop-ship purchase-order line whose requested shipping method is a supplier pickup (vendor will-call), so a customer collects the goods directly from the merchant's supplier. Each row carries COGS (from the purchase-order line), revenue (from the matching sales-order line), margin, the customer, the supplier, the pickup location, and the derived pickup status.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFilters (Spatie `filter[...]`): search (SKU, product name, PO number, SO number, customer name, supplier name, or row id), status (picked_up | awaiting_pickup | all), customer_id (comma-separated customer ids), supplier_id (comma-separated), warehouse_id (comma-separated pickup-location / supplier-warehouse ids), customer_tag (comma-separated tag names on the customer).\n\nAdvanced grouped filters are also accepted via a `filter_groups` tree over the columns expected_pickup_at, order_date, quantity, customer, supplier, product, sku.\n\nSorting (`sort`, prefix `-` for descending): id, order_date, expected_pickup_at, days_open, customer, supplier. Default order is newest order date first.\n\nPagination: page, per_page (default 10).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reports/supplier-pickup-orders","host":["{{baseUrl}}"],"path":["api","reports","supplier-pickup-orders"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 98,\n      \"sales_order\": {\n        \"id\": 512,\n        \"number\": \"SO-9236\",\n        \"route\": \"/orders/sales-orders/512\"\n      },\n      \"purchase_order\": {\n        \"id\": 28,\n        \"number\": \"PO-0028\",\n        \"route\": \"/orders/purchase-orders/28\"\n      },\n      \"customer\": {\n        \"id\": 44,\n        \"name\": \"Harbor Freight Traders\",\n        \"route\": \"/crm/customers/44\"\n      },\n      \"supplier\": {\n        \"id\": 7,\n        \"name\": \"Shenzhen Helmet Co.\",\n        \"route\": \"/crm/suppliers/7\"\n      },\n      \"pickup_location\": \"Shenzhen Supplier DC\",\n      \"product\": {\n        \"id\": 61,\n        \"sku\": \"5884265723882\",\n        \"name\": \"Carbon Track Helmet\",\n        \"route\": \"/products/61/overview\"\n      },\n      \"quantity\": 180,\n      \"order_date\": \"2026-06-27T00:00:00+00:00\",\n      \"expected_pickup_at\": \"2026-09-07T00:00:00+00:00\",\n      \"days_open\": 27,\n      \"status\": \"awaiting_pickup\",\n      \"unit_cost\": 14,\n      \"cogs\": 2520,\n      \"revenue\": 5670,\n      \"margin\": 3150,\n      \"margin_pct\": 0.5556,\n      \"currency\": \"USD\"\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/reports/supplier-pickup-orders?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/reports/supplier-pickup-orders?page=1\",\n  \"next_page_url\": null,\n  \"path\": \"{{protocol}}{{domain}}/api/reports/supplier-pickup-orders\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 3,\n  \"total\": 3\n}"}]},{"name":"Get Supplier Pickup Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reports/supplier-pickup-orders/summary","host":["{{baseUrl}}"],"path":["api","reports","supplier-pickup-orders","summary"]},"description":"KPI summary totals for the Supplier Pickup Orders report header cards. Honours the same filters as the list endpoint (search, customer_id, supplier_id, warehouse_id, customer_tag, and the advanced filter_groups) EXCEPT status, which is ignored so both the awaiting and picked-up buckets are always represented.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns: open_count (distinct orders awaiting pickup), units_awaiting, value_awaiting (COGS of awaiting lines), picked_up_count (distinct picked-up orders), revenue_period and margin_period (revenue / margin of picked-up lines).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reports/supplier-pickup-orders/summary","host":["{{baseUrl}}"],"path":["api","reports","supplier-pickup-orders","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"open_count\": 1,\n    \"units_awaiting\": 180,\n    \"value_awaiting\": 2520,\n    \"picked_up_count\": 2,\n    \"revenue_period\": 13230,\n    \"margin_period\": 7350\n  }\n}"}]},{"name":"Get Inventory COGS","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-cogs?group_by[]=sales_channel&filter[date_range_from]=2026-01-01T00:00:00&filter[date_range_to]=2026-05-29T23:59:59","host":["{{baseUrl}}"],"path":["api","reporting","inventory-cogs"],"query":[{"key":"group_by[]","value":"sales_channel","description":"Grouping dimensions in nesting order. Allowed: sales_channel, country, brand, sku. Repeat the parameter to nest (e.g. group_by[]=sales_channel&group_by[]=sku).","disabled":true},{"key":"filter[date_range_from]","value":"2026-01-01T00:00:00","description":"Start of the reporting date range. Required.","disabled":true},{"key":"filter[date_range_to]","value":"2026-05-29T23:59:59","description":"End of the reporting date range. Required.","disabled":true}]},"description":"Returns the movement-basis COGS report: a grouped tree (by sales channel, country, brand, and/or SKU) of average cost, quantities sold/returned/adjusted, and COGS totals (gross, returned, adjusted, net), computed from inventory movements and Amazon FBA ledger events. Costs are the as-booked per-movement unit cost; FBA events are costed through the FBA FIFO subledger.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`zero_cost_count` reports how many rows in range carry quantity but no recorded cost, so missing-cost data is surfaced rather than silently understating COGS.\n\nAuthentication: Requires Bearer token. Scope: reports."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/inventory-cogs?group_by[]=sales_channel&filter[date_range_from]=2026-01-01T00:00:00&filter[date_range_to]=2026-05-29T23:59:59","host":["{{baseUrl}}"],"path":["api","reporting","inventory-cogs"],"query":[{"key":"group_by[]","value":"sales_channel","description":"Grouping dimensions in nesting order. Allowed: sales_channel, country, brand, sku. Repeat the parameter to nest (e.g. group_by[]=sales_channel&group_by[]=sku).","disabled":true},{"key":"filter[date_range_from]","value":"2026-01-01T00:00:00","description":"Start of the reporting date range. Required.","disabled":true},{"key":"filter[date_range_to]","value":"2026-05-29T23:59:59","description":"End of the reporting date range. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"generated_at\": \"2026-05-29T18:12:03.000000Z\",\n    \"groupBy\": [\n      \"sales_channel\"\n    ],\n    \"groups\": {\n      \"Shopify US\": {\n        \"key\": \"Shopify US\",\n        \"totals\": {\n          \"qty_sold\": 450,\n          \"qty_returned\": 12,\n          \"qty_adjusted\": -5,\n          \"net_qty\": -443,\n          \"gross_cogs\": 4162.5,\n          \"returned_cogs\": 111,\n          \"adjusted_cogs\": 46.25,\n          \"net_cogs\": 4097.75,\n          \"avg_cost\": 9.25\n        },\n        \"children\": {}\n      }\n    },\n    \"grand_totals\": {\n      \"qty_sold\": 450,\n      \"qty_returned\": 12,\n      \"qty_adjusted\": -5,\n      \"net_qty\": -443,\n      \"gross_cogs\": 4162.5,\n      \"returned_cogs\": 111,\n      \"adjusted_cogs\": 46.25,\n      \"net_cogs\": 4097.75,\n      \"avg_cost\": 9.25\n    },\n    \"zero_cost_count\": 0\n  }\n}"}]},{"name":"Get Inventory COGS Drilldown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-cogs/drilldown?filter[date_range_from]=2026-01-01T00:00:00&filter[date_range_to]=2026-05-29T23:59:59","host":["{{baseUrl}}"],"path":["api","reporting","inventory-cogs","drilldown"],"query":[{"key":"filter[date_range_from]","value":"2026-01-01T00:00:00","description":"Start of the report's date range (matches the generated report). Required.","disabled":true},{"key":"filter[date_range_to]","value":"2026-05-29T23:59:59","description":"End of the report's date range. Required.","disabled":true}]},"description":"Underlying movements behind one Inventory COGS report row — the individual inventory movements (sales fulfillments, customer returns, adjustments, stock takes) and Amazon FBA ledger events that compose the clicked group/SKU aggregate. Paginated, sortable, searchable, with a `totals` block that reconciles to the report row that opened it (same 9 metrics).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nIdentify the clicked row with `dimensions[<group_by>]=<value>` for each active group-by dimension down to that row's level (e.g. sales_channel, then sku). Pass the same `filter[date_range_from]`/`filter[date_range_to]` the report was generated with. Each row includes a `product_link` to the product detail page, and a cross-link to the underlying business document via `link_route` (the sales order fulfillment, return receipt, inventory adjustment, stock take, or FBA ledger event that produced it), plus `link_type_label`, `link_id`, and `link_reference`. `link_route` is null when the movement's source cannot be resolved.\n\nAuthentication: Requires Bearer token. Scope: reports."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/inventory-cogs/drilldown?filter[date_range_from]=2026-01-01T00:00:00&filter[date_range_to]=2026-05-29T23:59:59","host":["{{baseUrl}}"],"path":["api","reporting","inventory-cogs","drilldown"],"query":[{"key":"filter[date_range_from]","value":"2026-01-01T00:00:00","description":"Start of the report's date range (matches the generated report). Required.","disabled":true},{"key":"filter[date_range_to]","value":"2026-05-29T23:59:59","description":"End of the report's date range. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"transaction_date\": \"2026-05-20T14:03:00.000000Z\",\n      \"type\": \"sale\",\n      \"type_label\": \"Sale\",\n      \"category\": \"sold\",\n      \"sales_channel_name\": \"Shopify US\",\n      \"brand\": \"Acme\",\n      \"country\": \"US\",\n      \"sku\": \"ACM-001\",\n      \"product_id\": 42,\n      \"product_link\": \"/products/42\",\n      \"raw_qty\": 10,\n      \"sign\": -1,\n      \"unit_cost\": 9.25,\n      \"ext_cost\": 92.5,\n      \"cogs_impact\": 92.5,\n      \"src_id\": 276415,\n      \"src_type\": \"App\\\\Models\\\\InventoryMovement\",\n      \"source_label\": \"Inventory Movement\",\n      \"link_route\": \"/orders/sales-orders/fulfillments/512\",\n      \"link_type_label\": \"Sales Order Fulfillment\",\n      \"link_id\": 512,\n      \"link_reference\": \"F-1042\"\n    }\n  ],\n  \"current_page\": 1,\n  \"per_page\": 15,\n  \"total\": 136,\n  \"last_page\": 10,\n  \"totals\": {\n    \"qty_sold\": 450,\n    \"qty_returned\": 12,\n    \"qty_adjusted\": -5,\n    \"net_qty\": -443,\n    \"gross_cogs\": 4162.5,\n    \"returned_cogs\": 111,\n    \"adjusted_cogs\": 46.25,\n    \"net_cogs\": 4097.75,\n    \"avg_cost\": 9.25,\n    \"count\": 136\n  }\n}"}]},{"name":"Export Inventory COGS Drilldown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-cogs/drilldown/export?filter[date_range_from]=2026-01-01T00:00:00&filter[date_range_to]=2026-05-29T23:59:59","host":["{{baseUrl}}"],"path":["api","reporting","inventory-cogs","drilldown","export"],"query":[{"key":"filter[date_range_from]","value":"2026-01-01T00:00:00","description":"Start of the report's date range (matches the generated report). Required.","disabled":true},{"key":"filter[date_range_to]","value":"2026-05-29T23:59:59","description":"End of the report's date range. Required.","disabled":true}]},"description":"All underlying movements behind one Inventory COGS report row, unpaginated, for exporting the full drilled-into set. Capped at 5,000 rows; `truncated` is true when the group had more rows than were returned (narrow the date range for the full set). Same row shape as the drilldown endpoint.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Scope: reports."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/inventory-cogs/drilldown/export?filter[date_range_from]=2026-01-01T00:00:00&filter[date_range_to]=2026-05-29T23:59:59","host":["{{baseUrl}}"],"path":["api","reporting","inventory-cogs","drilldown","export"],"query":[{"key":"filter[date_range_from]","value":"2026-01-01T00:00:00","description":"Start of the report's date range (matches the generated report). Required.","disabled":true},{"key":"filter[date_range_to]","value":"2026-05-29T23:59:59","description":"End of the report's date range. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"transaction_date\": \"2026-05-20T14:03:00.000000Z\",\n      \"type\": \"sale\",\n      \"type_label\": \"Sale\",\n      \"category\": \"sold\",\n      \"sales_channel_name\": \"Shopify US\",\n      \"brand\": \"Acme\",\n      \"country\": \"US\",\n      \"sku\": \"ACM-001\",\n      \"product_id\": 42,\n      \"product_link\": \"/products/42\",\n      \"raw_qty\": 10,\n      \"sign\": -1,\n      \"unit_cost\": 9.25,\n      \"ext_cost\": 92.5,\n      \"cogs_impact\": 92.5,\n      \"src_id\": 276415,\n      \"src_type\": \"App\\\\Models\\\\InventoryMovement\",\n      \"source_label\": \"Inventory Movement\",\n      \"link_route\": \"/orders/sales-orders/fulfillments/512\",\n      \"link_type_label\": \"Sales Order Fulfillment\",\n      \"link_id\": 512,\n      \"link_reference\": \"F-1042\"\n    }\n  ],\n  \"total\": 1,\n  \"truncated\": false\n}"}]},{"name":"Export Inventory COGS Report","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-cogs/export?group_by[]=sales_channel&filter[date_range_from]=2026-01-01T00:00:00&filter[date_range_to]=2026-05-29T23:59:59","host":["{{baseUrl}}"],"path":["api","reporting","inventory-cogs","export"],"query":[{"key":"group_by[]","value":"sales_channel","description":"Grouping dimensions in nesting order. Allowed: sales_channel, country, brand, sku. Repeat the parameter to nest (e.g. group_by[]=sales_channel&group_by[]=sku).","disabled":true},{"key":"filter[date_range_from]","value":"2026-01-01T00:00:00","description":"Start of the reporting date range. Required.","disabled":true},{"key":"filter[date_range_to]","value":"2026-05-29T23:59:59","description":"End of the reporting date range. Required.","disabled":true}]},"description":"Streams the flattened Inventory COGS report as a CSV file (`inventory-cogs.csv`): one row per deepest group, prefixed with the active group-by dimension columns, followed by Avg Cost, Qty Sold, Qty Returned, Qty Adjusted, Net Qty, Gross COGS, Returned COGS, Adjusted COGS, and Net COGS.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe response is a file download (`text/csv`), not JSON.\n\nAuthentication: Requires Bearer token. Scope: reports."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/inventory-cogs/export?group_by[]=sales_channel&filter[date_range_from]=2026-01-01T00:00:00&filter[date_range_to]=2026-05-29T23:59:59","host":["{{baseUrl}}"],"path":["api","reporting","inventory-cogs","export"],"query":[{"key":"group_by[]","value":"sales_channel","description":"Grouping dimensions in nesting order. Allowed: sales_channel, country, brand, sku. Repeat the parameter to nest (e.g. group_by[]=sales_channel&group_by[]=sku).","disabled":true},{"key":"filter[date_range_from]","value":"2026-01-01T00:00:00","description":"Start of the reporting date range. Required.","disabled":true},{"key":"filter[date_range_to]","value":"2026-05-29T23:59:59","description":"End of the reporting date range. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"Sales Channel,Avg Cost,Qty Sold,Qty Returned,Qty Adjusted,Net Qty,Gross COGS,Returned COGS,Adjusted COGS,Net COGS\nShopify US,9.25,450,12,-5,-443,4162.50,111.00,46.25,4097.75"}]},{"name":"Get Inventory Value Change","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-value-change?start_date=2026-01-31&end_date=2026-02-28","host":["{{baseUrl}}"],"path":["api","reporting","inventory-value-change"],"query":[{"key":"start_date","value":"2026-01-31","description":"Opening anchor date (Y-m-d). The report values on-hand as of this day's end, in the account timezone. Required.","disabled":true},{"key":"end_date","value":"2026-02-28","description":"Closing anchor date (Y-m-d). Must be on or after start_date; the period may span at most 12 months. Required.","disabled":true}]},"description":"Returns the Inventory Value Change statement: on-hand quantity and value at the opening and closing dates (on the same basis as the Inventory Valuation report), the change between them, and that change broken into categories — sales, receipts, adjustments, returns, transfers, manufacturing, and revaluations — each with sub-rows carrying a drilldown descriptor whose fields can be passed to the transactions endpoint.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe lenses object adds two more views over the same period. in_transit re-states the change in the warehouse-transfer in-transit balance, split into dispatches (into transit), arrivals (physical receipts and Amazon check-ins, out of transit), transit losses (declared shortages and close-out write-offs — value that left transit without arriving anywhere), and the two derived Amazon positions (FC-to-FC, AWD-to-FBA relay; positions have a null drilldown since no documents exist behind them). total is the conservation audit: on-hand plus in-transit, where internal transfers net to approximately zero by construction and the only movers are external flows and named losses. lenses is null when a warehouse filter is applied, because the in-transit balance is a whole-tenant figure.\n\nThe residual line quantifies rounding on Amazon FIFO layers fully consumed inside the period; it is flagged when its absolute value reaches $1. total_check confirms that opening + categories + residual equals closing exactly, per lens.\n\nAuthentication: Requires Bearer token.\n\nDates are calendar dates interpreted in the account timezone; each anchor is that day's end.\n\nAmazon arrivals whose inbound shipment is matched to a warehouse transfer are classified under the Transfers category (event keys `WtReceipts` for FBA and `WtAwdReceipts` for AWD) rather than Receipts — they are the on-hand side of an internal move, not an external receipt.\n\nOn the `total` lens, the `internal_transfers` category carries a `pairing` array: each row pairs an on-hand movement group with the in-transit leg it mirrors (`on_hand_value`, `in_transit_value`, `net_value`, `note`), and the rows' nets sum to the category value exactly — so a non-zero internal-transfers net decomposes into named effects (cost capitalized at receipt, check-in repricing, period-boundary timing) instead of reading as missing stock.\n\n`in_period_revaluations` is a MEMO: it is already inside the category values (which is why `residual` ties to 0.00) and must never be added to them. It names the part of this period's movement values that is dated cost revision rather than trade — the exact gap between a roll-forward built from as-booked movement values and this statement, split into the part capitalised into stock still on hand and the part restating the cost of units already sold."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/inventory-value-change?start_date=2026-01-31&end_date=2026-02-28","host":["{{baseUrl}}"],"path":["api","reporting","inventory-value-change"],"query":[{"key":"start_date","value":"2026-01-31","description":"Opening anchor date (Y-m-d). The report values on-hand as of this day's end, in the account timezone. Required.","disabled":true},{"key":"end_date","value":"2026-02-28","description":"Closing anchor date (Y-m-d). Must be on or after start_date; the period may span at most 12 months. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"period\": {\n      \"start_date\": \"2026-01-31\",\n      \"end_date\": \"2026-02-28\",\n      \"timezone\": \"America/Los_Angeles\",\n      \"window_after_utc\": \"2026-02-01 07:59:59\",\n      \"window_through_utc\": \"2026-03-01 07:59:59\"\n    },\n    \"opening\": {\n      \"quantity\": 29125,\n      \"value\": 267197.05,\n      \"in_transit_value\": 108056.19,\n      \"as_of\": \"2026-01-31\"\n    },\n    \"closing\": {\n      \"quantity\": 19765,\n      \"value\": 195553.39,\n      \"in_transit_value\": 293867.49,\n      \"as_of\": \"2026-02-28\"\n    },\n    \"change\": {\n      \"quantity\": -9360,\n      \"value\": -71643.66\n    },\n    \"categories\": [\n      {\n        \"key\": \"sales\",\n        \"label\": \"Sales\",\n        \"quantity\": -3244,\n        \"value\": -41771.4,\n        \"transaction_count\": 3026,\n        \"sub_rows\": [\n          {\n            \"key\": \"fba_shipments\",\n            \"label\": \"FBA customer shipments\",\n            \"pool\": \"fba\",\n            \"quantity\": -3225,\n            \"value\": -41643.58,\n            \"transaction_count\": 3010,\n            \"drilldown\": {\n              \"source\": \"amazon\",\n              \"pools\": [\n                \"fba\"\n              ],\n              \"event_types\": [\n                \"Shipments\"\n              ],\n              \"direction\": \"both\"\n            }\n          },\n          {\n            \"key\": \"warehouse_sales\",\n            \"label\": \"Warehouse sales\",\n            \"pool\": \"movements\",\n            \"quantity\": -19,\n            \"value\": -127.82,\n            \"transaction_count\": 16,\n            \"drilldown\": {\n              \"source\": \"inventory_movements\",\n              \"types\": [\n                \"sale\"\n              ]\n            }\n          }\n        ]\n      },\n      {\n        \"key\": \"receipts\",\n        \"label\": \"Receipts\",\n        \"quantity\": 4902,\n        \"value\": 65809.39,\n        \"transaction_count\": 1801,\n        \"sub_rows\": [\n          {\n            \"key\": \"po_receipts\",\n            \"label\": \"PO receipts (warehouses)\",\n            \"pool\": \"movements\",\n            \"quantity\": 1282,\n            \"value\": 17501.74,\n            \"transaction_count\": 15,\n            \"drilldown\": {\n              \"source\": \"inventory_movements\",\n              \"types\": [\n                \"purchase_receipt\"\n              ]\n            }\n          }\n        ]\n      }\n    ],\n    \"residual\": {\n      \"quantity\": 2,\n      \"value\": 59.67,\n      \"flagged\": false,\n      \"explanation\": \"FIFO layer clamp / cent rounding on Amazon layers fully consumed within the period — no single transaction carries this amount.\"\n    },\n    \"in_period_revaluations\": {\n      \"label\": \"Cost revisions recognised on this period's movements\",\n      \"on_hand\": -4310.64,\n      \"cogs\": 4026.05,\n      \"value\": -284.59,\n      \"transaction_count\": 11598,\n      \"is_memo\": true,\n      \"explanation\": \"Already included in the category values above — this names the part of them that is dated cost revision rather than trade.\"\n    },\n    \"total_check\": {\n      \"sum_of_categories_plus_residual\": -71643.66,\n      \"ties\": true\n    },\n    \"lenses\": {\n      \"in_transit\": {\n        \"opening\": {\n          \"quantity\": 9966,\n          \"value\": 108056.19\n        },\n        \"closing\": {\n          \"quantity\": 32384,\n          \"value\": 293867.49\n        },\n        \"change\": {\n          \"quantity\": 22418,\n          \"value\": 185811.3\n        },\n        \"categories\": [\n          {\n            \"key\": \"dispatches\",\n            \"label\": \"Dispatches (into transit)\",\n            \"quantity\": 24512,\n            \"value\": 201322.4,\n            \"transaction_count\": 118,\n            \"sub_rows\": [\n              {\n                \"key\": \"wt_dispatches\",\n                \"label\": \"Transfer dispatches (into transit)\",\n                \"pool\": \"transit\",\n                \"quantity\": 24512,\n                \"value\": 201322.4,\n                \"transaction_count\": 118,\n                \"drilldown\": {\n                  \"source\": \"transit_documents\",\n                  \"event_types\": [\n                    \"dispatch\"\n                  ]\n                }\n              }\n            ]\n          },\n          {\n            \"key\": \"arrivals\",\n            \"label\": \"Arrivals (out of transit)\",\n            \"quantity\": -2044,\n            \"value\": -15286.3,\n            \"transaction_count\": 41,\n            \"sub_rows\": [\n              {\n                \"key\": \"wt_receipts\",\n                \"label\": \"Physical receipts at destination\",\n                \"pool\": \"transit\",\n                \"quantity\": -1500,\n                \"value\": -11100,\n                \"transaction_count\": 24,\n                \"drilldown\": {\n                  \"source\": \"transit_documents\",\n                  \"event_types\": [\n                    \"receipt\"\n                  ]\n                }\n              },\n              {\n                \"key\": \"wt_amazon_check_ins\",\n                \"label\": \"Amazon check-ins (FBA / AWD arrivals)\",\n                \"pool\": \"transit\",\n                \"quantity\": -544,\n                \"value\": -4186.3,\n                \"transaction_count\": 17,\n                \"drilldown\": {\n                  \"source\": \"transit_documents\",\n                  \"event_types\": [\n                    \"fba_check_in\",\n                    \"awd_check_in\"\n                  ]\n                }\n              }\n            ]\n          },\n          {\n            \"key\": \"transit_losses\",\n            \"label\": \"Transit losses\",\n            \"quantity\": -50,\n            \"value\": -224.8,\n            \"transaction_count\": 3,\n            \"sub_rows\": [\n              {\n                \"key\": \"wt_shortages\",\n                \"label\": \"Declared inbound shortages\",\n                \"pool\": \"transit\",\n                \"quantity\": -20,\n                \"value\": -96,\n                \"transaction_count\": 2,\n                \"drilldown\": {\n                  \"source\": \"transit_documents\",\n                  \"event_types\": [\n                    \"shortage\"\n                  ]\n                }\n              },\n              {\n                \"key\": \"wt_write_offs\",\n                \"label\": \"Close-out write-offs\",\n                \"pool\": \"transit\",\n                \"quantity\": -30,\n                \"value\": -128.8,\n                \"transaction_count\": 1,\n                \"drilldown\": {\n                  \"source\": \"transit_documents\",\n                  \"event_types\": [\n                    \"write_off\"\n                  ]\n                }\n              }\n            ]\n          },\n          {\n            \"key\": \"amazon_positions\",\n            \"label\": \"Amazon in-transit positions (derived)\",\n            \"quantity\": 0,\n            \"value\": 0,\n            \"transaction_count\": 0,\n            \"sub_rows\": [\n              {\n                \"key\": \"fc_to_fc_position\",\n                \"label\": \"Amazon FC-to-FC position change\",\n                \"pool\": \"derived\",\n                \"quantity\": 0,\n                \"value\": 0,\n                \"transaction_count\": 0,\n                \"drilldown\": null\n              },\n              {\n                \"key\": \"awd_relay_position\",\n                \"label\": \"AWD → FBA relay position change\",\n                \"pool\": \"derived\",\n                \"quantity\": 0,\n                \"value\": 0,\n                \"transaction_count\": 0,\n                \"drilldown\": null\n              }\n            ]\n          }\n        ],\n        \"residual\": {\n          \"quantity\": 0,\n          \"value\": 0,\n          \"flagged\": false,\n          \"explanation\": \"Cap-chain lines whose in-window components do not fully explain their value change (e.g. a dispatch-weighted unit cost that moved).\"\n        },\n        \"total_check\": {\n          \"sum_of_categories_plus_residual\": 185811.3,\n          \"ties\": true\n        }\n      },\n      \"total\": {\n        \"opening\": {\n          \"quantity\": 39091,\n          \"value\": 375253.24\n        },\n        \"closing\": {\n          \"quantity\": 52149,\n          \"value\": 489420.88\n        },\n        \"change\": {\n          \"quantity\": 13058,\n          \"value\": 114167.64\n        },\n        \"categories\": [\n          {\n            \"key\": \"sales\",\n            \"label\": \"Sales\",\n            \"quantity\": -3244,\n            \"value\": -41771.4,\n            \"transaction_count\": 3026,\n            \"sub_rows\": []\n          },\n          {\n            \"key\": \"internal_transfers\",\n            \"label\": \"Internal transfers (net)\",\n            \"quantity\": 0,\n            \"value\": 0,\n            \"transaction_count\": 3911,\n            \"sub_rows\": [],\n            \"pairing\": [\n              {\n                \"key\": \"dispatches\",\n                \"label\": \"Dispatches\",\n                \"on_hand_quantity\": -230,\n                \"on_hand_value\": -2306,\n                \"in_transit_quantity\": 230,\n                \"in_transit_value\": 2306,\n                \"net_value\": 0,\n                \"note\": \"Stock leaves on-hand and enters transit at the same dispatched cost — this pair nets to zero when bookings agree.\"\n              },\n              {\n                \"key\": \"physical_receipts\",\n                \"label\": \"Physical receipts\",\n                \"on_hand_quantity\": 80,\n                \"on_hand_value\": 934,\n                \"in_transit_quantity\": -80,\n                \"in_transit_value\": -812,\n                \"net_value\": 122,\n                \"note\": \"Destination layers capitalize allocated bill/freight cost on top of the dispatched cost — a positive net is value added to inventory at receipt, not leakage.\"\n              },\n              {\n                \"key\": \"amazon_check_ins\",\n                \"label\": \"Amazon check-ins\",\n                \"on_hand_quantity\": 150,\n                \"on_hand_value\": 1650,\n                \"in_transit_quantity\": -150,\n                \"in_transit_value\": -1494,\n                \"net_value\": 156,\n                \"note\": \"Transfer-matched FBA/AWD arrivals at Amazon layer COGS vs transit relieved at dispatched cost — the net is repricing plus arrivals whose dispatch or check-in fell outside the period.\"\n              },\n              {\n                \"key\": \"fc_to_fc\",\n                \"label\": \"FBA FC-to-FC\",\n                \"on_hand_quantity\": 0,\n                \"on_hand_value\": 12.4,\n                \"in_transit_quantity\": 0,\n                \"in_transit_value\": 0,\n                \"net_value\": 12.4,\n                \"note\": \"FC-to-FC ledger events vs the derived FC-to-FC transit position — the net is timing across the period boundary.\"\n              },\n              {\n                \"key\": \"awd_relay\",\n                \"label\": \"AWD → FBA relay\",\n                \"on_hand_quantity\": -40,\n                \"on_hand_value\": -520,\n                \"in_transit_quantity\": 40,\n                \"in_transit_value\": 505,\n                \"net_value\": -15,\n                \"note\": \"AWD departures vs the derived relay position — the net is stock mid-relay at a period edge.\"\n              },\n              {\n                \"key\": \"unpaired_movements\",\n                \"label\": \"Other on-hand transfer movements\",\n                \"on_hand_quantity\": 0,\n                \"on_hand_value\": 0,\n                \"in_transit_quantity\": 0,\n                \"in_transit_value\": 0,\n                \"net_value\": 0,\n                \"note\": \"Transfer-typed movements with no transit pairing: reclassifications, corrections and legacy bookings.\"\n              }\n            ]\n          },\n          {\n            \"key\": \"transit_losses\",\n            \"label\": \"Transit losses\",\n            \"quantity\": -50,\n            \"value\": -224.8,\n            \"transaction_count\": 3,\n            \"sub_rows\": []\n          }\n        ],\n        \"residual\": {\n          \"quantity\": 2,\n          \"value\": 59.67,\n          \"flagged\": false,\n          \"explanation\": \"FIFO layer clamp / cent rounding on Amazon layers fully consumed within the period — no single transaction carries this amount. Plus any in-transit residue.\"\n        },\n        \"total_check\": {\n          \"sum_of_categories_plus_residual\": 114167.64,\n          \"ties\": true\n        }\n      }\n    }\n  }\n}"}]},{"name":"List Inventory Value Change Transactions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-value-change/transactions?start_date=2026-01-31&end_date=2026-02-28&source=amazon","host":["{{baseUrl}}"],"path":["api","reporting","inventory-value-change","transactions"],"query":[{"key":"start_date","value":"2026-01-31","description":"Opening anchor date (Y-m-d). Required.","disabled":true},{"key":"end_date","value":"2026-02-28","description":"Closing anchor date (Y-m-d). Required.","disabled":true},{"key":"source","value":"amazon","description":"Which transaction population to list: inventory_movements (warehouse movements), amazon (FBA/AWD FIFO layer arrivals and ledger consumptions, signed), revaluations (cost true-ups recognised in the period on pre-period stock), or transit_documents (warehouse-transfer cap-chain legs for the in-transit and total lenses). Required.","disabled":true}]},"description":"Lists the individual transactions behind one Inventory Value Change statement line, in a uniform row shape: date, SKU, warehouse, type or ledger event, quantity, unit cost, and signed value. Amazon rows also carry FNSKU, disposition, fulfillment center and the FIFO layer that was created or consumed; revaluation rows carry the old and new unit cost and the movement they re-price; transit_documents rows carry the warehouse transfer reference, its route (from/to warehouses), and the cap-chain leg (dispatch, receipt, fba_check_in, awd_check_in, shortage, write_off) — quantities and values are signed as their effect on the in-transit balance, dispatch rows carry the shipment date, and relief legs return a null date (they are cumulative in-period deltas).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe summary object always totals the FULL filtered set (not just the current page), so it restates the statement line the drill-down explains.\n\nAuthentication: Requires Bearer token.\n\nPagination: page / per_page (default 25, max 100). Allowed sorts: date, -date, value, -value.\n\nSynthetic event keys: `WtReceipts` (FBA arrivals matched to a warehouse transfer) and `WtAwdReceipts` (AWD arrivals matched to a warehouse transfer) select the transfer-fed subset of Amazon arrivals; plain `Receipts` covers the rest. `brand_ids[]` / `supplier_ids[]` scope the rows to the same product subset the report was generated with."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/inventory-value-change/transactions?start_date=2026-01-31&end_date=2026-02-28&source=amazon","host":["{{baseUrl}}"],"path":["api","reporting","inventory-value-change","transactions"],"query":[{"key":"start_date","value":"2026-01-31","description":"Opening anchor date (Y-m-d). Required.","disabled":true},{"key":"end_date","value":"2026-02-28","description":"Closing anchor date (Y-m-d). Required.","disabled":true},{"key":"source","value":"amazon","description":"Which transaction population to list: inventory_movements (warehouse movements), amazon (FBA/AWD FIFO layer arrivals and ledger consumptions, signed), revaluations (cost true-ups recognised in the period on pre-period stock), or transit_documents (warehouse-transfer cap-chain legs for the in-transit and total lenses). Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 151632,\n      \"source\": \"amazon\",\n      \"direction\": \"out\",\n      \"pool\": \"fba\",\n      \"date\": \"2026-02-01T18:24:00+00:00\",\n      \"sku\": \"BKG-010\",\n      \"product_id\": 913,\n      \"warehouse\": \"FBA US (BKG - North America)\",\n      \"warehouse_id\": 41,\n      \"type_label\": \"Consumption\",\n      \"event_type\": \"Shipments\",\n      \"quantity\": -1,\n      \"unit_cost\": 4.35,\n      \"value\": -4.35,\n      \"fnsku\": \"B0BMW99RZ5\",\n      \"disposition\": \"SELLABLE\",\n      \"reference\": null,\n      \"fulfillment_center\": \"MDT4\",\n      \"layer_id\": 48211\n    }\n  ],\n  \"current_page\": 1,\n  \"per_page\": 25,\n  \"total\": 3010,\n  \"summary\": {\n    \"quantity\": -3225,\n    \"value\": -41643.58,\n    \"row_count\": 3010\n  }\n}"}]},{"name":"Export Inventory Value Change Transactions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/inventory-value-change/transactions/export?start_date=2026-01-31&end_date=2026-02-28&source=amazon","host":["{{baseUrl}}"],"path":["api","reporting","inventory-value-change","transactions","export"],"query":[{"key":"start_date","value":"2026-01-31","description":"Opening anchor date (Y-m-d). Required.","disabled":true},{"key":"end_date","value":"2026-02-28","description":"Closing anchor date (Y-m-d). Required.","disabled":true},{"key":"source","value":"amazon","description":"Which transaction population to list: inventory_movements (warehouse movements), amazon (FBA/AWD FIFO layer arrivals and ledger consumptions, signed), revaluations (cost true-ups recognised in the period on pre-period stock), or transit_documents (warehouse-transfer cap-chain legs for the in-transit and total lenses). Required.","disabled":true}]},"description":"Streams EVERY transaction row of one drill-down line as a CSV file — the same uniform rows the transactions endpoint pages through, unpaginated (hard cap 100,000 rows). Accepts the same filters: source, types, pools, event_types, direction, warehouse_ids, brand_ids, supplier_ids and search. Columns: Date, Source, Type / Event, SKU, FNSKU, Warehouse, From Warehouse, To Warehouse, Reference, Quantity, Unit Cost, Value, Old Unit Cost, New Unit Cost, Disposition, Linked Document.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe response is a text/csv file download, not JSON."}},{"name":"List Drop-ship Order Money","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reports/dropship-order-money","host":["{{baseUrl}}"],"path":["api","reports","dropship-order-money"]},"description":"Paginated Drop-ship Order Money report — one row per drop-ship purchase order (a purchase order linked to a sales order), pairing the money owed on both sides of the same trade.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach row carries the customer side (what the sales order was billed, what has been received against it, and the balance still due) and the supplier side (the purchase order total, the vendor deposits actually paid, and the balance still owed). `customer_settled` is true once receipts cover the billed amount — for businesses that release goods from the factory only after the customer's balance lands, that is the column to watch.\n\nOnly vendor deposits in a committed state (paid, partially paid, partially applied, fully applied) count toward `supplier_paid`; drafts and deposits awaiting approval are intent, not spend, and are excluded so the paid figure cannot overstate what has left the bank. All money is expressed in the tenant's base currency.\n\nFilters: `filter[search]`, `filter[supplier_id]`, `filter[customer_id]` inline; everything else through the `filter_groups` advanced tree (see the parameter description).\n\nSorting (`sort`): prefix with - for DESC. Allowed: purchase_order_number, purchase_order_date, order_status, id. Default: -purchase_order_date.\n\nPagination: page (default 1), per_page (default 10).\n\nThe money columns (billed, received, customer due, PO total, deposits paid, supplier due) are computed per row from the underlying orders and deposits — they are returned on every row but are deliberately NOT filterable or sortable, because they are not database columns."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reports/dropship-order-money","host":["{{baseUrl}}"],"path":["api","reports","dropship-order-money"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"purchase_order_id\": 3,\n      \"purchase_order_number\": \"PO-0003\",\n      \"purchase_order_date\": \"2026-09-04\",\n      \"order_status\": \"open\",\n      \"shipment_status\": \"unshipped\",\n      \"receipt_status\": \"dropship\",\n      \"supplier_id\": 40,\n      \"supplier_name\": \"ZipSupplier\",\n      \"sales_order_id\": 1,\n      \"sales_order_number\": \"SO-0001\",\n      \"customer_id\": 12,\n      \"customer_name\": \"Wholesale Customer\",\n      \"customer_billed\": 2500,\n      \"customer_received\": 750,\n      \"customer_outstanding\": 1750,\n      \"customer_settled\": false,\n      \"supplier_committed\": 500,\n      \"supplier_paid\": 150,\n      \"supplier_outstanding\": 350,\n      \"deposit_count\": 1,\n      \"currency_code\": \"USD\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1,\n  \"path\": \"https://sku7.test/api/reports/dropship-order-money\",\n  \"first_page_url\": \"https://sku7.test/api/reports/dropship-order-money?page=1\",\n  \"last_page_url\": \"https://sku7.test/api/reports/dropship-order-money?page=1\",\n  \"next_page_url\": null,\n  \"prev_page_url\": null,\n  \"links\": []\n}"}]},{"name":"Get Drop-ship Order Money Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reports/dropship-order-money/summary","host":["{{baseUrl}}"],"path":["api","reports","dropship-order-money","summary"]},"description":"KPI totals for the Drop-ship Order Money report, computed over the same filtered set as the list endpoint so the header cards never disagree with the rows beneath them.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the number of drop-ship orders, the customer side (billed, received, outstanding) and the supplier side (committed, paid, outstanding). Accepts the same filters as the list endpoint (inline plus `filter_groups`), so the cards always agree with the rows; pagination and sort parameters are ignored.\n\nThe money columns (billed, received, customer due, PO total, deposits paid, supplier due) are computed per row from the underlying orders and deposits — they are returned on every row but are deliberately NOT filterable or sortable, because they are not database columns."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reports/dropship-order-money/summary","host":["{{baseUrl}}"],"path":["api","reports","dropship-order-money","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"order_count\": 2,\n    \"customer_billed\": 1500,\n    \"customer_received\": 800,\n    \"customer_outstanding\": 700,\n    \"supplier_committed\": 1500,\n    \"supplier_paid\": 350,\n    \"supplier_outstanding\": 1150\n  }\n}"}]},{"name":"Export Drop-ship Order Money","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reports/dropship-order-money/export","host":["{{baseUrl}}"],"path":["api","reports","dropship-order-money","export"]},"description":"Download the Drop-ship Order Money report as an Excel or CSV file.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nExports **every row matching the current filters**, not just the page you are looking at, so the file always agrees with the table it was triggered from. Pass the same `filter[...]` and `filter_groups` parameters you used on the list endpoint.\n\nColumn selection mirrors the columns visible in the table — pass them in `columns` to control which fields appear and in what order.\n\nMoney columns are written as fixed 2-decimal numbers, so a settled balance exports as 0 rather than an empty cell, and `customer_settled` exports as the words shown in the table (Paid in full / Balance due).\n\nReturns a file download, not JSON: `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` for xlsx, `text/csv` for csv."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reports/dropship-order-money/export","host":["{{baseUrl}}"],"path":["api","reports","dropship-order-money","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"\"PO #\",\"PO Date\",\"Supplier\",\"SO #\",\"Customer\",\"Billed\",\"Received\",\"Customer Due\",\"Release\",\"PO Total\",\"Deposits Paid\",\"Supplier Due\",\"Deposits\",\"PO Status\",\"Shipment\",\"Currency\"\n\"4612\",\"2026-07-08\",\"Shenzhen Cord Works\",\"SO-0735\",\"Nordic Toy Distribution AB\",\"2400.00\",\"2400.00\",\"0.00\",\"Paid in full\",\"1008.00\",\"300.00\",\"708.00\",\"2\",\"open\",\"unshipped\",\"USD\""}]},{"name":"List Duty Reconciliation Rows","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/reports/duty-reconciliation","host":["{{baseUrl}}"],"path":["api","v2","reports","duty-reconciliation"]},"description":"Duty Reconciliation report: estimated customs duty booked at receipt against the actual Customs Duty bills allocated to the same purchase order lines, grouped by inbound shipment (default), product, supplier or month.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe estimate is the duty frozen when the goods were received. The actual is each purchase order line's Customs Duty bill allocations, pro-rated to the received quantity. Variance = actual - estimate on the cleared receipts; variance_percent is that variance over the estimate booked on those receipts.\n\nRow status: awaiting_bill (estimate booked, no duty bill yet), partially_cleared (some receipts billed), cleared (every estimated receipt billed), no_estimate (no estimable duty and no bill), actual_without_estimate (a duty bill allocated where no estimate was booked). `uncleared` is the estimate still awaiting a bill. `is_aging` / `is_big_variance` flag rows past the aging and variance thresholds.\n\nAmounts are in the tenant's base currency. Requires the reports scope (read) and access to the Duty Reconciliation report."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/reports/duty-reconciliation","host":["{{baseUrl}}"],"path":["api","v2","reports","duty-reconciliation"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"key\": \"shipment:1042\",\n      \"level\": \"shipment\",\n      \"id\": 1042,\n      \"label\": \"IS-1042\",\n      \"entry_number\": \"123-4567890-1\",\n      \"inbound_shipment_id\": 1042,\n      \"product_id\": null,\n      \"product_name\": null,\n      \"purchase_order_id\": null,\n      \"purchase_order_number\": null,\n      \"tariff_code\": null,\n      \"supplier_id\": 12,\n      \"supplier_name\": \"Shenzhen Precision Electronics\",\n      \"destination_country_code\": \"US\",\n      \"received_at\": \"2026-08-02T15:20:00Z\",\n      \"last_received_at\": \"2026-08-02T15:20:00Z\",\n      \"quantity\": 1200,\n      \"shipments_count\": 1,\n      \"receipt_lines_count\": 2,\n      \"booked_estimate\": 2852.8,\n      \"actual_duty\": 2620,\n      \"variance\": 72.86,\n      \"variance_percent\": 2.86,\n      \"uncleared\": 305.66,\n      \"status\": \"partially_cleared\",\n      \"status_label\": \"Partially cleared\",\n      \"days_since_receipt\": 68,\n      \"days_awaiting\": 68,\n      \"is_aging\": true,\n      \"is_big_variance\": false,\n      \"bills\": [\n        {\n          \"id\": 88213,\n          \"invoice_number\": \"BRK-88213\"\n        }\n      ],\n      \"children\": [\n        {\n          \"key\": \"shipment:1042|line:5521\",\n          \"level\": \"line\",\n          \"id\": 5521,\n          \"label\": \"WIDGET-01\",\n          \"entry_number\": \"123-4567890-1\",\n          \"inbound_shipment_id\": 1042,\n          \"product_id\": 301,\n          \"product_name\": \"Laptop Dock\",\n          \"purchase_order_id\": 4421,\n          \"purchase_order_number\": \"PO-4421\",\n          \"tariff_code\": \"8471300100\",\n          \"supplier_id\": 12,\n          \"supplier_name\": \"Shenzhen Precision Electronics\",\n          \"destination_country_code\": \"US\",\n          \"received_at\": \"2026-08-02T15:20:00Z\",\n          \"last_received_at\": \"2026-08-02T15:20:00Z\",\n          \"quantity\": 1000,\n          \"shipments_count\": 1,\n          \"receipt_lines_count\": 1,\n          \"booked_estimate\": 2547.14,\n          \"actual_duty\": 2620,\n          \"variance\": 72.86,\n          \"variance_percent\": 2.86,\n          \"uncleared\": 0,\n          \"status\": \"cleared\",\n          \"status_label\": \"Cleared\",\n          \"days_since_receipt\": 68,\n          \"days_awaiting\": null,\n          \"is_aging\": false,\n          \"is_big_variance\": false,\n          \"bills\": [\n            {\n              \"id\": 88213,\n              \"invoice_number\": \"BRK-88213\"\n            }\n          ]\n        }\n      ]\n    }\n  ],\n  \"first_page_url\": \"https://acme.sku.io/api/v2/reports/duty-reconciliation?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://acme.sku.io/api/v2/reports/duty-reconciliation?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://acme.sku.io/api/v2/reports/duty-reconciliation\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"Get Duty Reconciliation Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/reports/duty-reconciliation/summary","host":["{{baseUrl}}"],"path":["api","v2","reports","duty-reconciliation","summary"]},"description":"Header totals for the Duty Reconciliation report over the same filters as the list (status filters do not narrow them): total estimated, total actual, net variance, uncleared balance, shipment counts, per-status row counts at the current grouping, and the destination countries available to filter on.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nGL tie-out: when an Accrued Customs Duty (or shared Accrued Landed Cost) account is mapped, `gl_accrued_balance` is that account's credit balance and `gl_uncleared_balance` the duty capitalised on every receipt still awaiting a bill (all receipts, whatever the filters). `gl_tie_out_ok` is true when they match to the cent; `gl_account.shared_with_landed_cost` marks an account that also holds the freight accrual, where some difference is expected. All GL fields are null with `gl_unavailable_reason: no_accrual_account` when no account is mapped."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/reports/duty-reconciliation/summary","host":["{{baseUrl}}"],"path":["api","v2","reports","duty-reconciliation","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_estimated\": 48210,\n    \"total_actual\": 49880,\n    \"net_variance\": 1670,\n    \"net_variance_percent\": 3.5,\n    \"uncleared_balance\": 6120,\n    \"uncleared_capitalised\": 6120,\n    \"shipments_count\": 14,\n    \"cleared_or_partial_count\": 11,\n    \"aging_count\": 1,\n    \"received_from\": \"2026-07-11T15:31:00Z\",\n    \"received_to\": \"2026-10-09T15:31:00Z\",\n    \"variance_threshold\": 10,\n    \"aging_days\": 60,\n    \"group_by\": \"shipment\",\n    \"tenant_currency_code\": \"USD\",\n    \"status_counts\": {\n      \"all\": 14,\n      \"awaiting_bill\": 3,\n      \"partially_cleared\": 2,\n      \"cleared\": 9,\n      \"no_estimate\": 0,\n      \"actual_without_estimate\": 0,\n      \"big_variance\": 1,\n      \"aging\": 1\n    },\n    \"destination_options\": [\n      \"US\"\n    ],\n    \"unclearable_duty\": null,\n    \"gl_accrued_balance\": 6295,\n    \"gl_tie_out_ok\": false,\n    \"gl_uncleared_balance\": 6120,\n    \"gl_difference\": 175,\n    \"gl_account\": {\n      \"id\": 88,\n      \"code\": \"2155\",\n      \"name\": \"Accrued Customs Duty\",\n      \"shared_with_landed_cost\": false\n    },\n    \"gl_unavailable_reason\": null,\n    \"capitalisation_enabled\": true\n  }\n}"}]},{"name":"Export Duty Reconciliation","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/reports/duty-reconciliation/export","host":["{{baseUrl}}"],"path":["api","v2","reports","duty-reconciliation","export"]},"description":"Export the Duty Reconciliation report (all rows matching the filters, at the chosen grouping and sort) as XLSX or CSV. Columns: group, entry no., supplier, destination, first and last received, received qty, estimated duty, actual duty, variance, variance %, uncleared, status, days since receipt and bill numbers.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nUp to 1,000 rows the file downloads directly. Larger exports run in the background: the response is 202 with a tracked_job_log_id, and the finished CSV is available from the tracked job's results (download_url)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/reports/duty-reconciliation/export","host":["{{baseUrl}}"],"path":["api","v2","reports","duty-reconciliation","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":""}]},{"name":"Get Commissions by Group","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/sales-rep-commissions/group-commissions?date_from=2026-06-01&date_to=2026-06-30","host":["{{baseUrl}}"],"path":["api","reporting","sales-rep-commissions","group-commissions"],"query":[{"key":"date_from","value":"2026-06-01","description":"Required. Start of the reporting period (YYYY-MM-DD), interpreted in your account's timezone.","disabled":true},{"key":"date_to","value":"2026-06-30","description":"Required. End of the reporting period (YYYY-MM-DD), on or after `date_from`.","disabled":true}]},"description":"Total orders, sales and commission for the period, grouped by sales rep group.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReps who belong to no group are returned as a single row with a null `group_id` and the name `Unassigned`. `rep_count` is the number of distinct reps in that group who had at least one qualifying order, so a group whose reps were all idle does not appear.\n\nThe `summary` object totals every returned row and echoes back the requested period.\n\nHow commission is earned is an account-level setting:\n- **Sale basis** — commission is earned when the order is placed, and the period is measured on the order date.\n- **Fulfillment basis** — commission is earned when the order ships, and the period is measured on the fulfillment date. Fully and over-fulfilled orders count in full. Partially fulfilled orders are only included if the account enables them, and then contribute only the value of the units that actually shipped.\n\nCancelled orders are excluded unless the account opts to include them, and an account may further restrict the report to a list of qualifying order statuses.\n\nSales figures are the product subtotal of each order's warehoused lines, converted to your account currency at the rate stored on the order. Commission is that value multiplied by the rep's commission rate.\n\nDates are interpreted in your account's timezone.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/sales-rep-commissions/group-commissions?date_from=2026-06-01&date_to=2026-06-30","host":["{{baseUrl}}"],"path":["api","reporting","sales-rep-commissions","group-commissions"],"query":[{"key":"date_from","value":"2026-06-01","description":"Required. Start of the reporting period (YYYY-MM-DD), interpreted in your account's timezone.","disabled":true},{"key":"date_to","value":"2026-06-30","description":"Required. End of the reporting period (YYYY-MM-DD), on or after `date_from`.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"data\": [\n      {\n        \"group_id\": 3,\n        \"group_name\": \"East Coast\",\n        \"total_orders\": 128,\n        \"total_sales\": 412500,\n        \"total_commission\": 20625,\n        \"rep_count\": 4\n      },\n      {\n        \"group_id\": 5,\n        \"group_name\": \"West Coast\",\n        \"total_orders\": 96,\n        \"total_sales\": 298400,\n        \"total_commission\": 22380,\n        \"rep_count\": 3\n      },\n      {\n        \"group_id\": null,\n        \"group_name\": \"Unassigned\",\n        \"total_orders\": 12,\n        \"total_sales\": 31200,\n        \"total_commission\": 0,\n        \"rep_count\": 1\n      }\n    ],\n    \"summary\": {\n      \"total_groups\": 3,\n      \"total_orders\": 236,\n      \"total_sales\": 742100,\n      \"total_commission\": 43005,\n      \"date_from\": \"2026-06-01\",\n      \"date_to\": \"2026-06-30\"\n    }\n  }\n}"}]},{"name":"List Sales Rep Performance","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/sales-rep-commissions/performance?date_from=2026-06-01&date_to=2026-06-30","host":["{{baseUrl}}"],"path":["api","reporting","sales-rep-commissions","performance"],"query":[{"key":"date_from","value":"2026-06-01","description":"Required. Start of the reporting period (YYYY-MM-DD), interpreted in your account's timezone.","disabled":true},{"key":"date_to","value":"2026-06-30","description":"Required. End of the reporting period (YYYY-MM-DD), on or after `date_from`.","disabled":true}]},"description":"One row per sales rep, paginated.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEvery rep matching the filters is returned, including those with no qualifying orders in the period — they come back with zero orders, sales and commission, which is a meaningful reading of the report rather than an omission. `average_order_value` is zero for those reps rather than undefined.\n\nSorting is applied across the whole result set, not just the returned page, so sorting by `commission_earned` descending gives the highest earners overall.\n\nHow commission is earned is an account-level setting:\n- **Sale basis** — commission is earned when the order is placed, and the period is measured on the order date.\n- **Fulfillment basis** — commission is earned when the order ships, and the period is measured on the fulfillment date. Fully and over-fulfilled orders count in full. Partially fulfilled orders are only included if the account enables them, and then contribute only the value of the units that actually shipped.\n\nCancelled orders are excluded unless the account opts to include them, and an account may further restrict the report to a list of qualifying order statuses.\n\nSales figures are the product subtotal of each order's warehoused lines, converted to your account currency at the rate stored on the order. Commission is that value multiplied by the rep's commission rate.\n\nDates are interpreted in your account's timezone.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/sales-rep-commissions/performance?date_from=2026-06-01&date_to=2026-06-30","host":["{{baseUrl}}"],"path":["api","reporting","sales-rep-commissions","performance"],"query":[{"key":"date_from","value":"2026-06-01","description":"Required. Start of the reporting period (YYYY-MM-DD), interpreted in your account's timezone.","disabled":true},{"key":"date_to","value":"2026-06-30","description":"Required. End of the reporting period (YYYY-MM-DD), on or after `date_from`.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"sales_rep_id\": 18,\n      \"sales_rep_name\": \"Dana Whitfield\",\n      \"group_id\": 5,\n      \"group_name\": \"West Coast\",\n      \"commission_rate\": 7.5,\n      \"total_orders\": 41,\n      \"total_sales\": 164800,\n      \"commission_earned\": 12360,\n      \"average_order_value\": 4019.51\n    },\n    {\n      \"sales_rep_id\": 22,\n      \"sales_rep_name\": \"Marcus Lee\",\n      \"group_id\": 3,\n      \"group_name\": \"East Coast\",\n      \"commission_rate\": 5,\n      \"total_orders\": 37,\n      \"total_sales\": 121300,\n      \"commission_earned\": 6065,\n      \"average_order_value\": 3278.38\n    },\n    {\n      \"sales_rep_id\": 31,\n      \"sales_rep_name\": \"Priya Raman\",\n      \"group_id\": null,\n      \"group_name\": \"Unassigned\",\n      \"commission_rate\": 0,\n      \"total_orders\": 0,\n      \"total_sales\": 0,\n      \"commission_earned\": 0,\n      \"average_order_value\": 0\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 2,\n  \"per_page\": 15,\n  \"total\": 23,\n  \"from\": 1,\n  \"to\": 15,\n  \"first_page_url\": \"https://your-account.sku.io/api/reporting/sales-rep-commissions/performance?page=1\",\n  \"last_page_url\": \"https://your-account.sku.io/api/reporting/sales-rep-commissions/performance?page=2\",\n  \"next_page_url\": \"https://your-account.sku.io/api/reporting/sales-rep-commissions/performance?page=2\",\n  \"prev_page_url\": null,\n  \"path\": \"https://your-account.sku.io/api/reporting/sales-rep-commissions/performance\"\n}"}]},{"name":"Get Performance Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/sales-rep-commissions/performance-summary?date_from=2026-06-01&date_to=2026-06-30","host":["{{baseUrl}}"],"path":["api","reporting","sales-rep-commissions","performance-summary"],"query":[{"key":"date_from","value":"2026-06-01","description":"Required. Start of the reporting period (YYYY-MM-DD), interpreted in your account's timezone.","disabled":true},{"key":"date_to","value":"2026-06-30","description":"Required. End of the reporting period (YYYY-MM-DD), on or after `date_from`.","disabled":true}]},"description":"Headline totals across every rep matching the filters: how many reps earned commission, and the combined order count, sales and commission.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`total_reps` counts only reps with at least one qualifying order in the period, so it is normally lower than the number of rows returned by the performance list.\n\nHow commission is earned is an account-level setting:\n- **Sale basis** — commission is earned when the order is placed, and the period is measured on the order date.\n- **Fulfillment basis** — commission is earned when the order ships, and the period is measured on the fulfillment date. Fully and over-fulfilled orders count in full. Partially fulfilled orders are only included if the account enables them, and then contribute only the value of the units that actually shipped.\n\nCancelled orders are excluded unless the account opts to include them, and an account may further restrict the report to a list of qualifying order statuses.\n\nSales figures are the product subtotal of each order's warehoused lines, converted to your account currency at the rate stored on the order. Commission is that value multiplied by the rep's commission rate.\n\nDates are interpreted in your account's timezone.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/sales-rep-commissions/performance-summary?date_from=2026-06-01&date_to=2026-06-30","host":["{{baseUrl}}"],"path":["api","reporting","sales-rep-commissions","performance-summary"],"query":[{"key":"date_from","value":"2026-06-01","description":"Required. Start of the reporting period (YYYY-MM-DD), interpreted in your account's timezone.","disabled":true},{"key":"date_to","value":"2026-06-30","description":"Required. End of the reporting period (YYYY-MM-DD), on or after `date_from`.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_reps\": 8,\n    \"total_orders\": 236,\n    \"total_sales\": 742100,\n    \"total_commission\": 43005\n  }\n}"}]},{"name":"Get Monthly Breakdown by Rep","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/sales-rep-commissions/monthly-breakdown-by-rep?date_from=2026-06-01&date_to=2026-06-30","host":["{{baseUrl}}"],"path":["api","reporting","sales-rep-commissions","monthly-breakdown-by-rep"],"query":[{"key":"date_from","value":"2026-06-01","description":"Required. Start of the reporting period (YYYY-MM-DD), interpreted in your account's timezone.","disabled":true},{"key":"date_to","value":"2026-06-30","description":"Required. End of the reporting period (YYYY-MM-DD), on or after `date_from`.","disabled":true}]},"description":"Month-by-month commission for each sales rep, ready to render as a grid.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`months` lists every `YYYY-MM` bucket the requested period touches, in order, including months with no activity. Each rep's `monthly_data` is keyed by those same values, so every rep has an entry for every month — zeroed where there were no orders.\n\nRows are ordered by total commission, highest first. Reps with no qualifying orders in the period are omitted entirely.\n\nHow commission is earned is an account-level setting:\n- **Sale basis** — commission is earned when the order is placed, and the period is measured on the order date.\n- **Fulfillment basis** — commission is earned when the order ships, and the period is measured on the fulfillment date. Fully and over-fulfilled orders count in full. Partially fulfilled orders are only included if the account enables them, and then contribute only the value of the units that actually shipped.\n\nCancelled orders are excluded unless the account opts to include them, and an account may further restrict the report to a list of qualifying order statuses.\n\nSales figures are the product subtotal of each order's warehoused lines, converted to your account currency at the rate stored on the order. Commission is that value multiplied by the rep's commission rate.\n\nDates are interpreted in your account's timezone.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/sales-rep-commissions/monthly-breakdown-by-rep?date_from=2026-06-01&date_to=2026-06-30","host":["{{baseUrl}}"],"path":["api","reporting","sales-rep-commissions","monthly-breakdown-by-rep"],"query":[{"key":"date_from","value":"2026-06-01","description":"Required. Start of the reporting period (YYYY-MM-DD), interpreted in your account's timezone.","disabled":true},{"key":"date_to","value":"2026-06-30","description":"Required. End of the reporting period (YYYY-MM-DD), on or after `date_from`.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"months\": [\n      \"2026-04\",\n      \"2026-05\",\n      \"2026-06\"\n    ],\n    \"data\": [\n      {\n        \"sales_rep_id\": 18,\n        \"sales_rep_name\": \"Dana Whitfield\",\n        \"group_name\": \"West Coast\",\n        \"monthly_data\": {\n          \"2026-04\": {\n            \"orders\": 12,\n            \"sales\": 48200,\n            \"commission\": 3615\n          },\n          \"2026-05\": {\n            \"orders\": 15,\n            \"sales\": 61300,\n            \"commission\": 4597.5\n          },\n          \"2026-06\": {\n            \"orders\": 14,\n            \"sales\": 55300,\n            \"commission\": 4147.5\n          }\n        },\n        \"total_orders\": 41,\n        \"total_sales\": 164800,\n        \"total_commission\": 12360\n      }\n    ]\n  }\n}"}]},{"name":"Get Monthly Breakdown by Group","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/sales-rep-commissions/monthly-breakdown-by-group?date_from=2026-06-01&date_to=2026-06-30","host":["{{baseUrl}}"],"path":["api","reporting","sales-rep-commissions","monthly-breakdown-by-group"],"query":[{"key":"date_from","value":"2026-06-01","description":"Required. Start of the reporting period (YYYY-MM-DD), interpreted in your account's timezone.","disabled":true},{"key":"date_to","value":"2026-06-30","description":"Required. End of the reporting period (YYYY-MM-DD), on or after `date_from`.","disabled":true}]},"description":"Month-by-month commission for each sales rep group, in the same shape as the per-rep breakdown.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`months` lists every `YYYY-MM` bucket the requested period touches, including empty ones, and each group's `monthly_data` is keyed by those values. Reps who belong to no group are aggregated into a single row with a null `group_id` and the name `Unassigned`.\n\nRows are ordered by total commission, highest first.\n\nHow commission is earned is an account-level setting:\n- **Sale basis** — commission is earned when the order is placed, and the period is measured on the order date.\n- **Fulfillment basis** — commission is earned when the order ships, and the period is measured on the fulfillment date. Fully and over-fulfilled orders count in full. Partially fulfilled orders are only included if the account enables them, and then contribute only the value of the units that actually shipped.\n\nCancelled orders are excluded unless the account opts to include them, and an account may further restrict the report to a list of qualifying order statuses.\n\nSales figures are the product subtotal of each order's warehoused lines, converted to your account currency at the rate stored on the order. Commission is that value multiplied by the rep's commission rate.\n\nDates are interpreted in your account's timezone.\n\nAuthentication: Requires Bearer token. Scope: `reports` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/sales-rep-commissions/monthly-breakdown-by-group?date_from=2026-06-01&date_to=2026-06-30","host":["{{baseUrl}}"],"path":["api","reporting","sales-rep-commissions","monthly-breakdown-by-group"],"query":[{"key":"date_from","value":"2026-06-01","description":"Required. Start of the reporting period (YYYY-MM-DD), interpreted in your account's timezone.","disabled":true},{"key":"date_to","value":"2026-06-30","description":"Required. End of the reporting period (YYYY-MM-DD), on or after `date_from`.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"months\": [\n      \"2026-04\",\n      \"2026-05\",\n      \"2026-06\"\n    ],\n    \"data\": [\n      {\n        \"group_id\": 5,\n        \"group_name\": \"West Coast\",\n        \"monthly_data\": {\n          \"2026-04\": {\n            \"orders\": 28,\n            \"sales\": 96400,\n            \"commission\": 7230\n          },\n          \"2026-05\": {\n            \"orders\": 34,\n            \"sales\": 108700,\n            \"commission\": 8152.5\n          },\n          \"2026-06\": {\n            \"orders\": 34,\n            \"sales\": 93300,\n            \"commission\": 6997.5\n          }\n        },\n        \"total_orders\": 96,\n        \"total_sales\": 298400,\n        \"total_commission\": 22380\n      }\n    ]\n  }\n}"}]},{"name":"Sales by Source Type (POS vs Online)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/sales-by-source-type","host":["{{baseUrl}}"],"path":["api","reporting","sales-by-source-type"]},"description":"Omnichannel sales segmented by sub-sales-channel source type (in-store / Point of Sale vs online vs marketplace). Integration-agnostic — aggregates Shopify POS, Square, and any classified source. Returns per-segment order_count, units, line revenue, plus point_of_sale_total and online_total. Optional date_from/date_to (Y-m-d). Authenticate with a Personal Access Token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/sales-by-source-type","host":["{{baseUrl}}"],"path":["api","reporting","sales-by-source-type"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"source_type\": \"point_of_sale\",\n      \"label\": \"Point of Sale\",\n      \"order_count\": 42,\n      \"units\": 118,\n      \"revenue\": 3840.5\n    },\n    {\n      \"source_type\": \"online\",\n      \"label\": \"Online\",\n      \"order_count\": 97,\n      \"units\": 210,\n      \"revenue\": 8120\n    }\n  ],\n  \"point_of_sale_total\": 3840.5,\n  \"online_total\": 8120\n}"}]},{"name":"List Brand Reporting Profiles","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brand-management/profiles","host":["{{baseUrl}}"],"path":["api","v2","brand-management","profiles"]},"description":"Paginated list of brands with reporting settings, with their channels and core product/ASIN counts.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.index).\n\nFilters:\n- filter[search] — brand name, or exact ID\n- filter[enabled] — true | false\n- filter[integration_instance_id] — comma-separated integration instance IDs (profiles with a channel on any of them)\n- filter_groups — advanced filter tree; columns: id, brand, enabled, updated_at\n\nSorts: id, enabled, next_weekly_run_at, next_monthly_run_at, updated_at (prefix with - for descending; default id).\n\nPagination: page, per_page (default 10, max 100).\n\n`next_weekly_run_at` / `next_monthly_run_at` are always the next upcoming slot."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brand-management/profiles","host":["{{baseUrl}}"],"path":["api","v2","brand-management","profiles"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 1,\n      \"exists\": true,\n      \"brand_id\": 36,\n      \"brand\": {\n        \"id\": 36,\n        \"name\": \"Summit Flask\"\n      },\n      \"enabled\": true,\n      \"timezone\": \"America/Los_Angeles\",\n      \"weekly\": {\n        \"enabled\": true,\n        \"day_of_week\": 2,\n        \"time\": \"06:00\"\n      },\n      \"monthly\": {\n        \"enabled\": true,\n        \"day_of_month\": 2,\n        \"time\": \"06:00\"\n      },\n      \"review_required\": true,\n      \"map_policy\": {\n        \"mode\": \"pricing_tier\",\n        \"pricing_tier_id\": 5,\n        \"pricing_tier_name\": null,\n        \"tolerance_pct\": 0\n      },\n      \"watch_mode\": \"auto_plus_manual\",\n      \"auto_top_n\": 30,\n      \"brand_aliases\": [],\n      \"sections\": {\n        \"listing_quality\": true,\n        \"fba_inventory\": true,\n        \"products_by_channel\": true\n      },\n      \"delivery\": {\n        \"attach_xlsx\": true,\n        \"attach_pdf\": true,\n        \"share_link\": true,\n        \"share_link_days\": 90,\n        \"cc\": [\n          \"brands@northwind.example\"\n        ]\n      },\n      \"sales_drop_threshold_pct\": 40,\n      \"sales_drop_min_prior\": 200,\n      \"brand_store_url\": \"https://www.amazon.com/stores/SummitFlask/page/7B2F1E40\",\n      \"brand_store_confirmed_at\": \"2026-08-12\",\n      \"next_weekly_run_at\": \"2026-10-06T13:00:00+00:00\",\n      \"next_monthly_run_at\": \"2026-11-02T14:00:00+00:00\",\n      \"channels\": [\n        {\n          \"id\": 1,\n          \"sales_channel_id\": 6,\n          \"name\": \"Amazon\",\n          \"integration_name\": \"Amazon\",\n          \"integration_instance_id\": 4,\n          \"is_amazon\": true,\n          \"include_in_report\": true,\n          \"competitive_monitoring\": true,\n          \"competitive_monitoring_since\": \"2026-07-27T16:00:00+00:00\",\n          \"marketplace_id\": \"ATVPDKIKX0DER\",\n          \"sort_order\": 0\n        }\n      ],\n      \"core_products_count\": 8,\n      \"core_asins_count\": 9,\n      \"watchlist_counts\": null,\n      \"competition_suggestions\": [],\n      \"report_recipient_count\": null,\n      \"watchlist_tracked_job_log_id\": null,\n      \"sales_traffic_refusals\": [],\n      \"updated_at\": \"2026-10-03T19:01:52+00:00\"\n    }\n  ],\n  \"first_page_url\": \"https://acme.sku.io/api/v2/brand-management/profiles?per_page=2&page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://acme.sku.io/api/v2/brand-management/profiles?per_page=2&page=1\",\n  \"links\": [\n    {\n      \"url\": null,\n      \"label\": \"&laquo; Previous\",\n      \"page\": null,\n      \"active\": false\n    }\n  ],\n  \"next_page_url\": null,\n  \"path\": \"https://acme.sku.io/api/v2/brand-management/profiles\",\n  \"per_page\": 2,\n  \"prev_page_url\": null,\n  \"to\": 2,\n  \"total\": 2\n}"}]},{"name":"Get Brand Reporting Settings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/management-profile","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","management-profile"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]},"description":"Returns a brand's reporting settings. When the brand has never been configured, returns an unsaved draft with defaults (`exists: false`).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAlso returns watchlist counts, `competition_suggestions` (per Amazon channel: how many core ASINs had sellers other than you and Amazon in the last 14 days, and whether turning on Competition monitoring is suggested) and the number of contacts with the Report Recipient role.\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.index)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/management-profile","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","management-profile"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"exists\": true,\n    \"brand_id\": 36,\n    \"brand\": {\n      \"id\": 36,\n      \"name\": \"Summit Flask\"\n    },\n    \"enabled\": true,\n    \"timezone\": \"America/Los_Angeles\",\n    \"weekly\": {\n      \"enabled\": true,\n      \"day_of_week\": 2,\n      \"time\": \"06:00\"\n    },\n    \"monthly\": {\n      \"enabled\": true,\n      \"day_of_month\": 2,\n      \"time\": \"06:00\"\n    },\n    \"review_required\": true,\n    \"map_policy\": {\n      \"mode\": \"pricing_tier\",\n      \"pricing_tier_id\": 5,\n      \"pricing_tier_name\": \"MAP\",\n      \"tolerance_pct\": 0\n    },\n    \"watch_mode\": \"auto_plus_manual\",\n    \"auto_top_n\": 30,\n    \"brand_aliases\": [],\n    \"sections\": {\n      \"listing_quality\": true,\n      \"fba_inventory\": true,\n      \"products_by_channel\": true\n    },\n    \"delivery\": {\n      \"attach_xlsx\": true,\n      \"attach_pdf\": true,\n      \"share_link\": true,\n      \"share_link_days\": 90,\n      \"cc\": [\n        \"brands@northwind.example\"\n      ]\n    },\n    \"sales_drop_threshold_pct\": 40,\n    \"sales_drop_min_prior\": 200,\n    \"brand_store_url\": \"https://www.amazon.com/stores/SummitFlask/page/7B2F1E40\",\n    \"brand_store_confirmed_at\": \"2026-08-12\",\n    \"next_weekly_run_at\": \"2026-10-06T13:00:00+00:00\",\n    \"next_monthly_run_at\": \"2026-11-02T14:00:00+00:00\",\n    \"channels\": [\n      {\n        \"id\": 1,\n        \"sales_channel_id\": 6,\n        \"name\": \"Amazon\",\n        \"integration_name\": \"Amazon\",\n        \"integration_instance_id\": 4,\n        \"is_amazon\": true,\n        \"include_in_report\": true,\n        \"competitive_monitoring\": true,\n        \"competitive_monitoring_since\": \"2026-07-27T16:00:00+00:00\",\n        \"marketplace_id\": \"ATVPDKIKX0DER\",\n        \"sort_order\": 0\n      },\n      {\n        \"id\": 2,\n        \"sales_channel_id\": 8,\n        \"name\": \"Shopify Storefront\",\n        \"integration_name\": \"Shopify\",\n        \"integration_instance_id\": 6,\n        \"is_amazon\": false,\n        \"include_in_report\": true,\n        \"competitive_monitoring\": false,\n        \"competitive_monitoring_since\": null,\n        \"marketplace_id\": null,\n        \"sort_order\": 1\n      }\n    ],\n    \"watchlist_counts\": {\n      \"products\": 8,\n      \"core_products\": 8,\n      \"asins\": 9,\n      \"core_asins\": 9\n    },\n    \"competition_suggestions\": [\n      {\n        \"sales_channel_id\": 6,\n        \"core_asins\": 9,\n        \"other_sellers_asins\": 9,\n        \"suggest\": false\n      }\n    ],\n    \"report_recipient_count\": 2,\n    \"watchlist_tracked_job_log_id\": null,\n    \"sales_traffic_refusals\": [],\n    \"updated_at\": \"2026-10-03T19:01:52+00:00\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Update Brand Reporting Settings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/management-profile","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","management-profile"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]},"description":"Creates or updates a brand's reporting settings. Every top-level key is optional — omitted keys keep their stored value — so one section can be saved on its own. `channels`, when sent, replaces the brand's whole channel set.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage).\n\nBody fields:\n- enabled (boolean) — turn scheduled reporting on. Enabling needs at least one report channel and a weekly or monthly cadence.\n- channels[] (array, max 50) — one row per sales channel: sales_channel_id (required, distinct), include_in_report (boolean, default true), competitive_monitoring (boolean, Amazon channels only, default false), marketplace_id (string, optional).\n- competitive_monitoring — \"Competition monitoring\". Turn it on when other sellers also sell this brand's ASINs: it adds seller/FBA/Prime count history, exact offer totals and an estimated total market per ASIN. Leave it off when you own the brand or distribute it exclusively; MAP, our offer, Amazon retail, Buy Box and sales rank are reported either way.\n- timezone (IANA name) — schedule and week boundaries (Monday–Sunday) use this timezone.\n- weekly.enabled / weekly.day_of_week (1 = Monday … 7 = Sunday) / weekly.time (HH:MM)\n- monthly.enabled / monthly.day_of_month (1–28) / monthly.time (HH:MM)\n- review_required (boolean) — hold each report for review before sending; false sends automatically when the report has no data warnings.\n- map_policy.mode — none | pricing_tier. map_policy.pricing_tier_id is required for pricing_tier. map_policy.tolerance_pct 0–50.\n- watch_mode — auto_top_n | manual | auto_plus_manual. auto_top_n 1–200.\n- brand_aliases[] (max 20) — Amazon brand names that roll into this brand (sub-brands, old names).\n- sections.listing_quality / sections.fba_inventory / sections.products_by_channel (boolean) — optional report sections.\n- delivery.attach_xlsx / attach_pdf / share_link (boolean), delivery.share_link_days (1–365), delivery.cc[] (emails, max 20).\n- sales_drop_threshold_pct (1–100) and sales_drop_min_prior (≥ 0) — when to raise a weekly sales-drop alert.\n- brand_store_url (URL) / brand_store_confirmed_at (date) — manual Brand Store confirmation used by the listing-quality audit.\n\nSaving may start a background watchlist rebuild; its id is returned as `watchlist_tracked_job_log_id`.","body":{"mode":"raw","raw":"{\n  \"enabled\": true,\n  \"channels\": [\n    {\n      \"sales_channel_id\": 6,\n      \"include_in_report\": true,\n      \"competitive_monitoring\": true,\n      \"marketplace_id\": \"ATVPDKIKX0DER\"\n    },\n    {\n      \"sales_channel_id\": 8,\n      \"include_in_report\": true\n    },\n    {\n      \"sales_channel_id\": 10,\n      \"include_in_report\": true\n    }\n  ],\n  \"timezone\": \"America/Los_Angeles\",\n  \"weekly\": {\n    \"enabled\": true,\n    \"day_of_week\": 2,\n    \"time\": \"06:00\"\n  },\n  \"monthly\": {\n    \"enabled\": true,\n    \"day_of_month\": 2,\n    \"time\": \"06:00\"\n  },\n  \"review_required\": true,\n  \"map_policy\": {\n    \"mode\": \"pricing_tier\",\n    \"pricing_tier_id\": 7,\n    \"tolerance_pct\": 0\n  },\n  \"watch_mode\": \"auto_plus_manual\",\n  \"auto_top_n\": 30,\n  \"brand_aliases\": [\n    \"Summit\"\n  ],\n  \"sections\": {\n    \"listing_quality\": true,\n    \"fba_inventory\": true,\n    \"products_by_channel\": true\n  },\n  \"delivery\": {\n    \"attach_xlsx\": true,\n    \"attach_pdf\": true,\n    \"share_link\": true,\n    \"share_link_days\": 90,\n    \"cc\": [\n      \"brands@acme.example\"\n    ]\n  },\n  \"sales_drop_threshold_pct\": 40,\n  \"sales_drop_min_prior\": 200,\n  \"brand_store_url\": \"https://www.amazon.com/stores/SummitFlask/page/1A2B3C4D\",\n  \"brand_store_confirmed_at\": \"2026-09-01\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/management-profile","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","management-profile"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Brand reporting settings saved.\",\n  \"data\": {\n    \"id\": 1,\n    \"exists\": true,\n    \"brand_id\": 36,\n    \"brand\": {\n      \"id\": 36,\n      \"name\": \"Summit Flask\"\n    },\n    \"enabled\": true,\n    \"timezone\": \"America/Los_Angeles\",\n    \"weekly\": {\n      \"enabled\": true,\n      \"day_of_week\": 2,\n      \"time\": \"06:00\"\n    },\n    \"monthly\": {\n      \"enabled\": true,\n      \"day_of_month\": 2,\n      \"time\": \"06:00\"\n    },\n    \"review_required\": true,\n    \"map_policy\": {\n      \"mode\": \"pricing_tier\",\n      \"pricing_tier_id\": 5,\n      \"pricing_tier_name\": \"MAP\",\n      \"tolerance_pct\": 0\n    },\n    \"watch_mode\": \"auto_plus_manual\",\n    \"auto_top_n\": 30,\n    \"brand_aliases\": [],\n    \"sections\": {\n      \"listing_quality\": true,\n      \"fba_inventory\": true,\n      \"products_by_channel\": true\n    },\n    \"delivery\": {\n      \"attach_xlsx\": true,\n      \"attach_pdf\": true,\n      \"share_link\": true,\n      \"share_link_days\": 90,\n      \"cc\": [\n        \"brands@northwind.example\"\n      ]\n    },\n    \"sales_drop_threshold_pct\": 40,\n    \"sales_drop_min_prior\": 200,\n    \"brand_store_url\": \"https://www.amazon.com/stores/SummitFlask/page/7B2F1E40\",\n    \"brand_store_confirmed_at\": \"2026-08-12\",\n    \"next_weekly_run_at\": \"2026-10-06T13:00:00+00:00\",\n    \"next_monthly_run_at\": \"2026-11-02T14:00:00+00:00\",\n    \"channels\": [\n      {\n        \"id\": 1,\n        \"sales_channel_id\": 6,\n        \"name\": \"Amazon\",\n        \"integration_name\": \"Amazon\",\n        \"integration_instance_id\": 4,\n        \"is_amazon\": true,\n        \"include_in_report\": true,\n        \"competitive_monitoring\": true,\n        \"competitive_monitoring_since\": \"2026-07-27T16:00:00+00:00\",\n        \"marketplace_id\": \"ATVPDKIKX0DER\",\n        \"sort_order\": 0\n      },\n      {\n        \"id\": 2,\n        \"sales_channel_id\": 8,\n        \"name\": \"Shopify Storefront\",\n        \"integration_name\": \"Shopify\",\n        \"integration_instance_id\": 6,\n        \"is_amazon\": false,\n        \"include_in_report\": true,\n        \"competitive_monitoring\": false,\n        \"competitive_monitoring_since\": null,\n        \"marketplace_id\": null,\n        \"sort_order\": 1\n      }\n    ],\n    \"watchlist_counts\": {\n      \"products\": 8,\n      \"core_products\": 8,\n      \"asins\": 9,\n      \"core_asins\": 9\n    },\n    \"competition_suggestions\": [\n      {\n        \"sales_channel_id\": 6,\n        \"core_asins\": 9,\n        \"other_sellers_asins\": 9,\n        \"suggest\": false\n      }\n    ],\n    \"report_recipient_count\": 2,\n    \"watchlist_tracked_job_log_id\": null,\n    \"sales_traffic_refusals\": [],\n    \"updated_at\": \"2026-10-03T19:01:52+00:00\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Enable Brand Reporting","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brand-management/profiles/bulk-enable","host":["{{baseUrl}}"],"path":["api","v2","brand-management","profiles","bulk-enable"]},"description":"Turns scheduled reporting on or off for several brands at once — the given profile IDs, or every profile matching the list filters. Each brand goes through the normal save rules; a brand that cannot be enabled (for example, no report channel) is left unchanged and listed under `skipped`.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage).\n\nBody:\n- enabled (boolean, required)\n- ids[] (profile IDs, max 500) — required unless apply_to_all is true\n- apply_to_all (boolean) — use `filters` instead of ids\n- filters (object) — the same filters as List Brand Reporting Profiles","body":{"mode":"raw","raw":"{\n  \"enabled\": true,\n  \"ids\": [\n    1,\n    2\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brand-management/profiles/bulk-enable","host":["{{baseUrl}}"],"path":["api","v2","brand-management","profiles","bulk-enable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Reporting turned on for 1 brand. 1 could not be changed — see the details.\",\n  \"data\": {\n    \"processed\": 1,\n    \"skipped\": [\n      {\n        \"id\": 2,\n        \"brand\": \"EverHydrate\",\n        \"reason\": \"Add at least one report channel before turning reporting on.\"\n      }\n    ]\n  }\n}"}]},{"name":"List Watched Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/watch-products","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","watch-products"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]},"description":"Paginated list of the brand's watched products, with the channel listing identifiers of each product.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.index).\n\nFilters:\n- filter[search] — exact ID, ASIN or SKU, product SKU or name\n- filter[archived] — omitted or empty: active rows only; `only`: archived only; `all`: both\n- filter[is_core] — true | false\n- filter[source] — auto | manual (comma-separated)\n- filter_groups — advanced filter tree; columns: id, is_core, source, created_at\n\nSorts: id, sku, name, trailing_90d_sales, is_core, source, created_at (prefix - for descending).\n\nPagination: page, per_page (default 10).\n\nReturns 404 until the brand's reporting settings have been saved once."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/watch-products","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","watch-products"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 1,\n      \"product_id\": 188,\n      \"product\": {\n        \"id\": 188,\n        \"sku\": \"SF-INS-32\",\n        \"name\": \"Summit Flask Insulated Bottle 32 oz — Glacier\"\n      },\n      \"is_core\": true,\n      \"source\": \"auto\",\n      \"sticky_note_reason\": null,\n      \"sticky_note_text\": null,\n      \"trailing_90d_sales\": 22891.35,\n      \"archived_at\": null,\n      \"listings\": [\n        {\n          \"sales_channel_id\": 6,\n          \"product_listing_id\": 292,\n          \"listing_id\": \"SF-INS-32\",\n          \"listing_sku\": \"SF-INS-32\",\n          \"asin\": \"B0D7SF3201\",\n          \"is_fba\": false,\n          \"archived\": false\n        }\n      ],\n      \"created_at\": \"2026-07-27T16:00:00+00:00\"\n    }\n  ],\n  \"first_page_url\": \"https://acme.sku.io/api/v2/brands/36/watch-products?per_page=2&page=1\",\n  \"from\": 1,\n  \"last_page\": 4,\n  \"last_page_url\": \"https://acme.sku.io/api/v2/brands/36/watch-products?per_page=2&page=4\",\n  \"links\": [\n    {\n      \"url\": null,\n      \"label\": \"&laquo; Previous\",\n      \"page\": null,\n      \"active\": false\n    }\n  ],\n  \"next_page_url\": \"https://acme.sku.io/api/v2/brands/36/watch-products?per_page=2&page=2\",\n  \"path\": \"https://acme.sku.io/api/v2/brands/36/watch-products\",\n  \"per_page\": 2,\n  \"prev_page_url\": null,\n  \"to\": 2,\n  \"total\": 8\n}"}]},{"name":"Add Watched Products","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/watch-products","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","watch-products"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]},"description":"Adds products to the brand's watchlist by hand (source `manual`). Rejected when the brand's watch mode is `auto_top_n`; at most 200 products per brand.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage).\n\nBody:\n- product_ids[] (required, 1–200 distinct product IDs)","body":{"mode":"raw","raw":"{\n  \"product_ids\": [\n    188,\n    192\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/watch-products","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","watch-products"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 product(s) added to the watchlist.\",\n  \"data\": [\n    {\n      \"id\": 1,\n      \"product_id\": 188,\n      \"product\": {\n        \"id\": 188,\n        \"sku\": \"SF-INS-32\",\n        \"name\": \"Summit Flask Insulated Bottle 32 oz — Glacier\"\n      },\n      \"is_core\": true,\n      \"source\": \"auto\",\n      \"sticky_note_reason\": null,\n      \"sticky_note_text\": null,\n      \"trailing_90d_sales\": 22891.35,\n      \"archived_at\": null,\n      \"listings\": [\n        {\n          \"sales_channel_id\": 6,\n          \"product_listing_id\": 292,\n          \"listing_id\": \"SF-INS-32\",\n          \"listing_sku\": \"SF-INS-32\",\n          \"asin\": \"B0D7SF3201\",\n          \"is_fba\": false,\n          \"archived\": false\n        }\n      ],\n      \"created_at\": \"2026-07-27T16:00:00+00:00\"\n    }\n  ]\n}"}]},{"name":"Update Watched Product","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/watch-products/{watchProduct}","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","watch-products",":watchProduct"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"},{"key":"watchProduct","value":"1","description":"Watched product ID"}]},"description":"Updates one watched product: mark it core or not, archive/restore it, or set a remembered report note that overrides the suggested note until cleared.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage).\n\nBody (all optional):\n- is_core (boolean)\n- archived (boolean)\n- sticky_note_reason (string|null) — one of: disco, merged, duplicate_asin, late_inbound, stock_out, not_listed, backorder, amazon_retail, competitor_buy_box, map_break, we_hold_buy_box, ltsf_risk\n- sticky_note_text (string|null, max 255)","body":{"mode":"raw","raw":"{\n  \"is_core\": true,\n  \"sticky_note_reason\": \"backorder\",\n  \"sticky_note_text\": \"Backorder, ETA mid-October\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/watch-products/{watchProduct}","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","watch-products",":watchProduct"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"},{"key":"watchProduct","value":"1","description":"Watched product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"product_id\": 188,\n    \"product\": {\n      \"id\": 188,\n      \"sku\": \"SF-INS-32\",\n      \"name\": \"Summit Flask Insulated Bottle 32 oz — Glacier\"\n    },\n    \"is_core\": true,\n    \"source\": \"auto\",\n    \"sticky_note_reason\": null,\n    \"sticky_note_text\": null,\n    \"trailing_90d_sales\": 22891.35,\n    \"archived_at\": null,\n    \"listings\": [\n      {\n        \"sales_channel_id\": 6,\n        \"product_listing_id\": 292,\n        \"listing_id\": \"SF-INS-32\",\n        \"listing_sku\": \"SF-INS-32\",\n        \"asin\": \"B0D7SF3201\",\n        \"is_fba\": false,\n        \"archived\": false\n      }\n    ],\n    \"created_at\": \"2026-07-27T16:00:00+00:00\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"List Watched ASINs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/watch-asins","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","watch-asins"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]},"description":"Paginated list of the brand's watched Amazon ASINs with live facts: the latest Buy Box sample, the offer summary (MAP status, our offer, Amazon retail, seller counts when Competition monitoring is on) and the latest listing-quality score.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.index).\n\nFilters:\n- filter[search] — exact ID, ASIN or SKU\n- filter[archived] — omitted or empty: active rows only; `only`: archived only; `all`: both\n- filter[is_core] — true | false\n- filter[source] — auto | manual (comma-separated)\n- filter[sales_channel_id] — comma-separated sales channel IDs\n- filter_groups — advanced filter tree; columns: id, is_core, source, created_at\n\nSorts: id, asin, sku, trailing_90d_sales, is_core, source, created_at (prefix - for descending).\n\nPagination: page, per_page (default 10).\n\nReturns 404 until the brand's reporting settings have been saved once."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/watch-asins","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","watch-asins"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 1,\n      \"asin\": \"B0D7SF3201\",\n      \"sales_channel_id\": 6,\n      \"sales_channel_name\": \"Amazon\",\n      \"product_id\": 188,\n      \"product\": {\n        \"id\": 188,\n        \"sku\": \"SF-INS-32\",\n        \"name\": \"Summit Flask Insulated Bottle 32 oz — Glacier\"\n      },\n      \"sku\": \"SF-INS-32\",\n      \"is_core\": true,\n      \"source\": \"auto\",\n      \"sticky_note_reason\": null,\n      \"sticky_note_text\": null,\n      \"map_price_override\": null,\n      \"aplus_confirmed_at\": \"2026-10-03\",\n      \"trailing_90d_sales\": 22611.7,\n      \"archived_at\": null,\n      \"buy_box\": {\n        \"status\": \"lost\",\n        \"price\": 41.2,\n        \"currency\": \"USD\",\n        \"seller_id\": \"A1KQ7ZRV3M2T8B\",\n        \"our_offer_count\": 1,\n        \"competing_offer_count\": 7,\n        \"observed_at\": \"2026-10-03T07:05:00+00:00\"\n      },\n      \"offers\": {\n        \"offer_count_returned\": 7,\n        \"offer_count_capped\": false,\n        \"offer_count_total\": null,\n        \"prime_offer_count\": 5,\n        \"fba_offer_count\": 5,\n        \"our_offer_present\": true,\n        \"amazon_retail_present\": false,\n        \"map_price\": 39.95,\n        \"map_violation\": true,\n        \"lowest_price\": 36.99,\n        \"observed_at\": \"2026-10-03T07:05:00+00:00\"\n      },\n      \"listing_quality_score\": 88,\n      \"listing_quality_audited_on\": \"2026-10-01\"\n    }\n  ],\n  \"first_page_url\": \"https://acme.sku.io/api/v2/brands/36/watch-asins?per_page=2&page=1\",\n  \"from\": 1,\n  \"last_page\": 5,\n  \"last_page_url\": \"https://acme.sku.io/api/v2/brands/36/watch-asins?per_page=2&page=5\",\n  \"links\": [\n    {\n      \"url\": null,\n      \"label\": \"&laquo; Previous\",\n      \"page\": null,\n      \"active\": false\n    }\n  ],\n  \"next_page_url\": \"https://acme.sku.io/api/v2/brands/36/watch-asins?per_page=2&page=2\",\n  \"path\": \"https://acme.sku.io/api/v2/brands/36/watch-asins\",\n  \"per_page\": 2,\n  \"prev_page_url\": null,\n  \"to\": 2,\n  \"total\": 9\n}"}]},{"name":"Add Watched ASINs","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/watch-asins","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","watch-asins"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]},"description":"Adds Amazon ASINs to the brand's watchlist on one Amazon sales channel — including brand ASINs you do not carry, so MAP and offer monitoring still cover them. Rejected when the watch mode is `auto_top_n`; at most 200 ASINs per channel.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage).\n\nBody:\n- sales_channel_id (required) — an Amazon sales channel on the brand's profile\n- asins[] (required, 1–200, 10 alphanumeric characters each)\n- is_core (boolean, default true)","body":{"mode":"raw","raw":"{\n  \"sales_channel_id\": 6,\n  \"asins\": [\n    \"B0D7SF3209\"\n  ],\n  \"is_core\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/watch-asins","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","watch-asins"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"1 ASIN(s) added to the watchlist.\",\n  \"data\": [\n    {\n      \"id\": 1,\n      \"asin\": \"B0D7SF3201\",\n      \"sales_channel_id\": 6,\n      \"sales_channel_name\": \"Amazon\",\n      \"product_id\": 188,\n      \"product\": {\n        \"id\": 188,\n        \"sku\": \"SF-INS-32\",\n        \"name\": \"Summit Flask Insulated Bottle 32 oz — Glacier\"\n      },\n      \"sku\": \"SF-INS-32\",\n      \"is_core\": true,\n      \"source\": \"auto\",\n      \"sticky_note_reason\": null,\n      \"sticky_note_text\": null,\n      \"map_price_override\": null,\n      \"aplus_confirmed_at\": \"2026-10-03\",\n      \"trailing_90d_sales\": 22611.7,\n      \"archived_at\": null,\n      \"buy_box\": {\n        \"status\": \"lost\",\n        \"price\": 41.2,\n        \"currency\": \"USD\",\n        \"seller_id\": \"A1KQ7ZRV3M2T8B\",\n        \"our_offer_count\": 1,\n        \"competing_offer_count\": 7,\n        \"observed_at\": \"2026-10-03T07:05:00+00:00\"\n      },\n      \"offers\": {\n        \"offer_count_returned\": 7,\n        \"offer_count_capped\": false,\n        \"offer_count_total\": null,\n        \"prime_offer_count\": 5,\n        \"fba_offer_count\": 5,\n        \"our_offer_present\": true,\n        \"amazon_retail_present\": false,\n        \"map_price\": 39.95,\n        \"map_violation\": true,\n        \"lowest_price\": 36.99,\n        \"observed_at\": \"2026-10-03T07:05:00+00:00\"\n      },\n      \"listing_quality_score\": 88,\n      \"listing_quality_audited_on\": \"2026-10-01\"\n    }\n  ]\n}"}]},{"name":"Update Watched ASIN","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/watch-asins/{watchAsin}","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","watch-asins",":watchAsin"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"},{"key":"watchAsin","value":"1","description":"Watched ASIN ID"}]},"description":"Updates one watched ASIN.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage).\n\nBody (all optional):\n- is_core (boolean)\n- archived (boolean)\n- sticky_note_reason (string|null) — see Update Watched Product for values\n- sticky_note_text (string|null, max 255)\n- map_price_override (number|null, 0–9999999) — MAP price for an ASIN you do not carry (no pricing-tier price)\n- aplus_confirmed (boolean) — record that A+ content is present (used by the listing-quality audit)","body":{"mode":"raw","raw":"{\n  \"is_core\": true,\n  \"map_price_override\": 44.95,\n  \"aplus_confirmed\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/watch-asins/{watchAsin}","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","watch-asins",":watchAsin"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"},{"key":"watchAsin","value":"1","description":"Watched ASIN ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"asin\": \"B0D7SF3201\",\n    \"sales_channel_id\": 6,\n    \"sales_channel_name\": \"Amazon\",\n    \"product_id\": 188,\n    \"product\": {\n      \"id\": 188,\n      \"sku\": \"SF-INS-32\",\n      \"name\": \"Summit Flask Insulated Bottle 32 oz — Glacier\"\n    },\n    \"sku\": \"SF-INS-32\",\n    \"is_core\": true,\n    \"source\": \"auto\",\n    \"sticky_note_reason\": null,\n    \"sticky_note_text\": null,\n    \"map_price_override\": null,\n    \"aplus_confirmed_at\": \"2026-10-03\",\n    \"trailing_90d_sales\": 22611.7,\n    \"archived_at\": null,\n    \"buy_box\": {\n      \"status\": \"lost\",\n      \"price\": 41.2,\n      \"currency\": \"USD\",\n      \"seller_id\": \"A1KQ7ZRV3M2T8B\",\n      \"our_offer_count\": 1,\n      \"competing_offer_count\": 7,\n      \"observed_at\": \"2026-10-03T07:05:00+00:00\"\n    },\n    \"offers\": {\n      \"offer_count_returned\": 7,\n      \"offer_count_capped\": false,\n      \"offer_count_total\": null,\n      \"prime_offer_count\": 5,\n      \"fba_offer_count\": 5,\n      \"our_offer_present\": true,\n      \"amazon_retail_present\": false,\n      \"map_price\": 39.95,\n      \"map_violation\": true,\n      \"lowest_price\": 36.99,\n      \"observed_at\": \"2026-10-03T07:05:00+00:00\"\n    },\n    \"listing_quality_score\": 88,\n    \"listing_quality_audited_on\": \"2026-10-01\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Update Watched ASINs","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/watch-asins/bulk","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","watch-asins","bulk"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]},"description":"Marks several watched ASINs core / not core, or archives / restores them — the given IDs, or every ASIN matching the list filters. Only this brand's rows are changed.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage).\n\nBody:\n- ids[] (max 500) — required unless apply_to_all is true\n- apply_to_all (boolean) + filters (object, same as List Watched ASINs; a missing archived filter means active rows)\n- is_core (boolean) and/or archived (boolean) — at least one is required","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ],\n  \"is_core\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/watch-asins/bulk","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","watch-asins","bulk"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 ASINs updated.\",\n  \"data\": {\n    \"updated\": 3\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Rebuild Brand Watchlist","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/watch-asins/rebuild","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","watch-asins","rebuild"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]},"description":"Re-runs the automatic top-N selection now (it also runs nightly). Manual rows, pinned rows and user overrides are kept; discontinued products stay until archived. Runs in the background — the response returns the tracked job id.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage)."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/watch-asins/rebuild","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","watch-asins","rebuild"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Watchlist rebuild started.\",\n  \"data\": {\n    \"tracked_job_log_id\": 5821\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Upload MAP Prices","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/map-prices","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","map-prices"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]},"description":"Applies a brand MAP price list to watched ASINs as `map_price_override` — for ASINs you do not carry, which have no pricing-tier price. Each row's key is matched against ASIN, SKU, MPN or part number.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage).\n\nSend either:\n- file — CSV (csv or txt, max 2 MB) with two columns: key, price (multipart/form-data), or\n- rows[] — JSON array of {key, map_price} (max 5000 rows)\n\nThe response counts matched rows and lists keys that matched nothing.","body":{"mode":"raw","raw":"{\n  \"rows\": [\n    {\n      \"key\": \"B0D7SF3209\",\n      \"map_price\": 44.95\n    },\n    {\n      \"key\": \"SF-LID-STR\",\n      \"map_price\": 12.99\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/map-prices","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","map-prices"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"MAP prices applied to 2 watched ASIN(s).\",\n  \"data\": {\n    \"rows\": 3,\n    \"updated\": 2,\n    \"unmatched_keys\": [\n      \"SF-OLD-01\"\n    ]\n  },\n  \"status\": \"success\"\n}"}]},{"name":"List Brand Contacts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/contacts","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","contacts"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]},"description":"List all contacts attached to a brand, paginated. Supports search (title/notes), role/primary/has-email filters, advanced per-column filters, and sorting.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBrand contacts use the role `report_recipient` for people who receive the brand's reports (the primary contact is addressed in the email greeting). Requires permission brand_reports.index to read and brand_reports.manage to change."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/contacts","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","contacts"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Dana Whitfield\",\n      \"email\": \"dana.whitfield@summitflask.example\",\n      \"phone\": null,\n      \"role\": \"report_recipient\",\n      \"role_label\": \"Report Recipient\",\n      \"title\": \"Head of Marketplaces\",\n      \"is_primary\": true,\n      \"is_messaging\": false,\n      \"notes\": null,\n      \"link_type\": \"brand\",\n      \"link_id\": 36,\n      \"created_at\": \"2026-10-03T18:27:33+00:00\",\n      \"updated_at\": \"2026-10-03T18:27:33+00:00\"\n    }\n  ],\n  \"first_page_url\": \"https://acme.sku.io/api/v2/brands/36/contacts?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://acme.sku.io/api/v2/brands/36/contacts?page=1\",\n  \"links\": [\n    {\n      \"url\": null,\n      \"label\": \"&laquo; Previous\",\n      \"page\": null,\n      \"active\": false\n    }\n  ],\n  \"next_page_url\": null,\n  \"path\": \"https://acme.sku.io/api/v2/brands/36/contacts\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 2,\n  \"total\": 2\n}"}]},{"name":"Create Brand Contact","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/contacts","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","contacts"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]},"description":"Add a new contact to a brand. Fields: name (required), email, phone, role (default general), title, is_primary, notes (max 500). The first contact added to a brand automatically becomes primary; setting is_primary demotes the previous primary.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRole options: general, sales_rep, account_manager, customer_service, accounts_payable, accounts_receivable, purchasing, shipping_receiving, warehouse_manager, operations, billing, owner, other.\n\nAuthentication: Requires Bearer token.\n\nBrand contacts use the role `report_recipient` for people who receive the brand's reports (the primary contact is addressed in the email greeting). Requires permission brand_reports.index to read and brand_reports.manage to change.","body":{"mode":"raw","raw":"{\n  \"name\": \"Jane Buyer\",\n  \"email\": \"jane.buyer@example.com\",\n  \"phone\": \"+1 415-555-0100\",\n  \"role\": \"report_recipient\",\n  \"title\": \"AP Specialist\",\n  \"is_primary\": true,\n  \"notes\": \"Prefers email over phone.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/contacts","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","contacts"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Dana Whitfield\",\n    \"email\": \"dana.whitfield@summitflask.example\",\n    \"phone\": null,\n    \"role\": \"report_recipient\",\n    \"role_label\": \"Report Recipient\",\n    \"title\": \"Head of Marketplaces\",\n    \"is_primary\": true,\n    \"is_messaging\": false,\n    \"notes\": null,\n    \"link_type\": \"brand\",\n    \"link_id\": 36,\n    \"created_at\": \"2026-10-03T18:27:33+00:00\",\n    \"updated_at\": \"2026-10-03T18:27:33+00:00\"\n  }\n}"}]},{"name":"Export Brand Contacts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/contacts/export?format=csv","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","contacts","export"],"query":[{"key":"format","value":"csv","description":"Export format: csv (default) or xlsx","disabled":true}],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]},"description":"Download all contacts belonging to the brand as a CSV or XLSX file. Columns: Contact ID, Name, Email, Phone, Role, Title, Primary, Notes, Created At, Updated At. The file mirrors the contacts bulk-import template, so an exported file can be edited and re-imported (rows with a Contact ID or a matching email update the existing contact; others are created).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nOptional comma-separated column keys (`name,role,title,email,phone,notes,created_at`) to narrow the exported fields; `Contact ID` is always included so the file can round-trip as an update. Omit for the full field set.\n\nOptional. `filtered` applies the request's filter/sort parameters (same syntax as the list endpoint) instead of exporting every contact.\n\nBrand contacts use the role `report_recipient` for people who receive the brand's reports (the primary contact is addressed in the email greeting). Requires permission brand_reports.manage."}},{"name":"Get Brand Contact","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/contacts/{contact}","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","contacts",":contact"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"},{"key":"contact","value":"42","description":"Contact ID"}]},"description":"Get a single contact belonging to the brand. A contact id that belongs to a different brand returns 404.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBrand contacts use the role `report_recipient` for people who receive the brand's reports (the primary contact is addressed in the email greeting). Requires permission brand_reports.index to read and brand_reports.manage to change."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/contacts/{contact}","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","contacts",":contact"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"},{"key":"contact","value":"42","description":"Contact ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Dana Whitfield\",\n    \"email\": \"dana.whitfield@summitflask.example\",\n    \"phone\": null,\n    \"role\": \"report_recipient\",\n    \"role_label\": \"Report Recipient\",\n    \"title\": \"Head of Marketplaces\",\n    \"is_primary\": true,\n    \"is_messaging\": false,\n    \"notes\": null,\n    \"link_type\": \"brand\",\n    \"link_id\": 36,\n    \"created_at\": \"2026-10-03T18:27:33+00:00\",\n    \"updated_at\": \"2026-10-03T18:27:33+00:00\"\n  }\n}"}]},{"name":"Update Brand Contact","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/contacts/{contact}","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","contacts",":contact"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"},{"key":"contact","value":"42","description":"Contact ID"}]},"description":"Update a contact belonging to the brand. Full update — name is required. Setting is_primary promotes this contact and demotes the previous primary.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRole options: general, sales_rep, account_manager, customer_service, accounts_payable, accounts_receivable, purchasing, shipping_receiving, warehouse_manager, operations, billing, owner, other.\n\nAuthentication: Requires Bearer token.\n\nBrand contacts use the role `report_recipient` for people who receive the brand's reports (the primary contact is addressed in the email greeting). Requires permission brand_reports.index to read and brand_reports.manage to change.","body":{"mode":"raw","raw":"{\n  \"name\": \"Jane Buyer\",\n  \"email\": \"jane.buyer@example.com\",\n  \"phone\": \"+1 415-555-0100\",\n  \"role\": \"report_recipient\",\n  \"title\": \"AP Specialist\",\n  \"is_primary\": true,\n  \"notes\": \"Prefers email over phone.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/contacts/{contact}","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","contacts",":contact"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"},{"key":"contact","value":"42","description":"Contact ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Dana Whitfield\",\n    \"email\": \"dana.whitfield@summitflask.example\",\n    \"phone\": null,\n    \"role\": \"report_recipient\",\n    \"role_label\": \"Report Recipient\",\n    \"title\": \"Head of Marketplaces\",\n    \"is_primary\": true,\n    \"is_messaging\": false,\n    \"notes\": null,\n    \"link_type\": \"brand\",\n    \"link_id\": 36,\n    \"created_at\": \"2026-10-03T18:27:33+00:00\",\n    \"updated_at\": \"2026-10-03T18:27:33+00:00\"\n  }\n}"}]},{"name":"Delete Brand Contact","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/contacts/{contact}","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","contacts",":contact"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"},{"key":"contact","value":"42","description":"Contact ID"}]},"description":"Delete a contact from the brand. If the deleted contact was primary and other contacts remain, the oldest remaining contact is promoted to primary.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBrand contacts use the role `report_recipient` for people who receive the brand's reports (the primary contact is addressed in the email greeting). Requires permission brand_reports.index to read and brand_reports.manage to change."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/contacts/{contact}","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","contacts",":contact"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"},{"key":"contact","value":"42","description":"Contact ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"message\": \"Contact deleted.\"\n}"}]},{"name":"Set Brand Contact as Primary","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/contacts/{contact}/make-primary","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","contacts",":contact","make-primary"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"},{"key":"contact","value":"42","description":"Contact ID"}]},"description":"Promote a contact to be the brand's primary contact, demoting the previous primary.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBrand contacts use the role `report_recipient` for people who receive the brand's reports (the primary contact is addressed in the email greeting). Requires permission brand_reports.index to read and brand_reports.manage to change."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/contacts/{contact}/make-primary","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","contacts",":contact","make-primary"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"},{"key":"contact","value":"42","description":"Contact ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Dana Whitfield\",\n    \"email\": \"dana.whitfield@summitflask.example\",\n    \"phone\": null,\n    \"role\": \"report_recipient\",\n    \"role_label\": \"Report Recipient\",\n    \"title\": \"Head of Marketplaces\",\n    \"is_primary\": true,\n    \"is_messaging\": false,\n    \"notes\": null,\n    \"link_type\": \"brand\",\n    \"link_id\": 36,\n    \"created_at\": \"2026-10-03T18:27:33+00:00\",\n    \"updated_at\": \"2026-10-03T18:27:33+00:00\"\n  }\n}"}]},{"name":"List Brand Reports","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports","host":["{{baseUrl}}"],"path":["api","v2","brand-reports"]},"description":"Paginated list of brand report runs. By default only the latest version of each report (brand + period) is listed; send filter[latest_only]=0 to list every version. `latest_version` and `has_sent_prior_version` tell you when a newer version supersedes a report the brand already received.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.index).\n\nFilters:\n- filter[search] — exact ID, brand name or summary text\n- filter[brand_id] — comma-separated brand IDs\n- filter[period_type] — weekly | monthly\n- filter[status] — scheduled | generating | in_review | approved | sent | sent_partial | skipped | failed (comma-separated)\n- filter[period_from] / filter[period_to] — YYYY-MM-DD (period start on/after, period end on/before)\n- filter[latest_only] — 1 (default) | 0\n- filter_groups — advanced filter tree; columns: id, brand_name, status, period_type, version, period_start, period_end, generated_at, sent_at, created_at\n\nSorts: id, period_start, period_end, status, version, generated_at, sent_at, created_at, updated_at (default -period_start, -id).\n\nPagination: page, per_page (default 10)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brand-reports","host":["{{baseUrl}}"],"path":["api","v2","brand-reports"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 16,\n      \"brand\": {\n        \"id\": 37,\n        \"name\": \"EverHydrate\"\n      },\n      \"profile_id\": 2,\n      \"period_type\": \"weekly\",\n      \"period_start\": \"2026-09-21\",\n      \"period_end\": \"2026-09-27\",\n      \"period_label\": \"Sep 21 – 27, 2026\",\n      \"version\": 1,\n      \"latest_version\": 1,\n      \"has_sent_prior_version\": false,\n      \"status\": \"approved\",\n      \"status_label\": \"Approved\",\n      \"generated_at\": \"2026-09-29T13:06:00+00:00\",\n      \"brand_sales\": {\n        \"current\": 3984.56,\n        \"prior\": 4096.49,\n        \"change_pct\": -2.73,\n        \"units\": 144,\n        \"prior_units\": 151\n      },\n      \"core_sales\": {\n        \"current\": 3984.56,\n        \"prior\": 4096.49,\n        \"change_pct\": -2.73,\n        \"units\": 144,\n        \"prior_units\": 151,\n        \"change_units_pct\": -4.64\n      },\n      \"flags\": {\n        \"map\": 1,\n        \"amazon_retail\": 0,\n        \"buy_box_lost\": 0,\n        \"disco\": 0,\n        \"needs_decision\": 0,\n        \"suggested\": 0,\n        \"zero_sales\": 2,\n        \"low_share\": 0,\n        \"unit_swing\": 3,\n        \"seller_count_change\": 0\n      },\n      \"warning_count\": 0,\n      \"reviewed_by\": 1,\n      \"reviewed_by_name\": \"Administrator\",\n      \"sent_by\": null,\n      \"sent_by_name\": null,\n      \"reviewed_at\": \"2026-09-29T13:46:00+00:00\",\n      \"scheduled_send_at\": \"2026-10-05T09:00:00+00:00\",\n      \"sent_at\": null,\n      \"sent_to\": null,\n      \"skipped_reason\": null,\n      \"failure_reason\": null,\n      \"tracked_job_log_id\": 55,\n      \"updated_at\": \"2026-09-29T13:46:00+00:00\"\n    }\n  ],\n  \"first_page_url\": \"https://acme.sku.io/api/v2/brand-reports?per_page=2&page=1\",\n  \"from\": 1,\n  \"last_page\": 3,\n  \"last_page_url\": \"https://acme.sku.io/api/v2/brand-reports?per_page=2&page=3\",\n  \"links\": [\n    {\n      \"url\": null,\n      \"label\": \"&laquo; Previous\",\n      \"page\": null,\n      \"active\": false\n    }\n  ],\n  \"next_page_url\": \"https://acme.sku.io/api/v2/brand-reports?per_page=2&page=2\",\n  \"path\": \"https://acme.sku.io/api/v2/brand-reports\",\n  \"per_page\": 2,\n  \"prev_page_url\": null,\n  \"to\": 2,\n  \"total\": 6\n}"}]},{"name":"Generate Brand Report","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports","host":["{{baseUrl}}"],"path":["api","v2","brand-reports"]},"description":"Starts generating a report for a brand now, in the background. Without period_start, the most recent complete period is used.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage).\n\nBody:\n- brand_id (required) — a brand with saved reporting settings\n- period_type (required) — weekly | monthly\n- period_start (optional, YYYY-MM-DD) — must be a Monday for weekly or the 1st for monthly","body":{"mode":"raw","raw":"{\n  \"brand_id\": 36,\n  \"period_type\": \"weekly\",\n  \"period_start\": \"2026-09-21\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brand-reports","host":["{{baseUrl}}"],"path":["api","v2","brand-reports"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Report generation started — track progress in the job tray.\",\n  \"data\": {\n    \"run\": {\n      \"id\": 16,\n      \"brand\": {\n        \"id\": 37,\n        \"name\": \"EverHydrate\"\n      },\n      \"profile_id\": 2,\n      \"period_type\": \"weekly\",\n      \"period_start\": \"2026-09-21\",\n      \"period_end\": \"2026-09-27\",\n      \"period_label\": \"Sep 21 – 27, 2026\",\n      \"version\": 1,\n      \"latest_version\": 1,\n      \"has_sent_prior_version\": false,\n      \"status\": \"approved\",\n      \"status_label\": \"Approved\",\n      \"generated_at\": \"2026-09-29T13:06:00+00:00\",\n      \"brand_sales\": {\n        \"current\": 3984.56,\n        \"prior\": 4096.49,\n        \"change_pct\": -2.73,\n        \"units\": 144,\n        \"prior_units\": 151\n      },\n      \"core_sales\": {\n        \"current\": 3984.56,\n        \"prior\": 4096.49,\n        \"change_pct\": -2.73,\n        \"units\": 144,\n        \"prior_units\": 151,\n        \"change_units_pct\": -4.64\n      },\n      \"flags\": {\n        \"map\": 1,\n        \"amazon_retail\": 0,\n        \"buy_box_lost\": 0,\n        \"disco\": 0,\n        \"needs_decision\": 0,\n        \"suggested\": 0,\n        \"zero_sales\": 2,\n        \"low_share\": 0,\n        \"unit_swing\": 3,\n        \"seller_count_change\": 0\n      },\n      \"warning_count\": 0,\n      \"reviewed_by\": 1,\n      \"reviewed_by_name\": \"Administrator\",\n      \"sent_by\": null,\n      \"sent_by_name\": null,\n      \"reviewed_at\": \"2026-09-29T13:46:00+00:00\",\n      \"scheduled_send_at\": \"2026-10-05T09:00:00+00:00\",\n      \"sent_at\": null,\n      \"sent_to\": null,\n      \"skipped_reason\": null,\n      \"failure_reason\": null,\n      \"tracked_job_log_id\": 55,\n      \"updated_at\": \"2026-09-29T13:46:00+00:00\"\n    },\n    \"tracked_job_log_id\": 5830\n  }\n}"}]},{"name":"Get Brand Report Summary Counts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/summary","host":["{{baseUrl}}"],"path":["api","v2","brand-reports","summary"]},"description":"Headline counts for the reports overview: reports due this week, reports awaiting review (latest versions only), reports sent in the last 30 days, and open MAP violations across watched ASINs.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.index)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/summary","host":["{{baseUrl}}"],"path":["api","v2","brand-reports","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"due_this_week\": 0,\n    \"awaiting_review\": 3,\n    \"sent_last_30_days\": 2,\n    \"open_map_violations\": 5\n  }\n}"}]},{"name":"Get Brand Report","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]},"description":"Returns a report run in full: totals, flags, warnings, data-as-of timestamps, channels, narrative (insights and actions), versions and every line.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nLine sections: `channel` (one per sales channel), `product` (one per core product, with `channel_breakdown` keyed by sales_channel_id), `amazon` (one per watched ASIN per Amazon channel).\n\nOn Amazon lines, seller/FBA/Prime counts and the market estimate (`market_units`, `market_sales_amount`, `market_share_pct`, `competitor_units`, `market_basis`, `market_confidence`) are only present when Competition monitoring is on for that channel — they are omitted, not zero, otherwise. Market figures are estimates: `market_basis` is `derived` (from your Sales & Traffic: your units ÷ your page-view-weighted Buy Box share), `derived_hourly` (hourly Buy Box holder × page views; adds `competitor_units_by_seller`) or `keepa`; low-confidence estimates are not reported.\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.index)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 15,\n    \"brand\": {\n      \"id\": 36,\n      \"name\": \"Summit Flask\"\n    },\n    \"profile_id\": 1,\n    \"period_type\": \"weekly\",\n    \"period_start\": \"2026-09-21\",\n    \"period_end\": \"2026-09-27\",\n    \"period_label\": \"Sep 21 – 27, 2026\",\n    \"version\": 1,\n    \"latest_version\": null,\n    \"has_sent_prior_version\": null,\n    \"status\": \"in_review\",\n    \"status_label\": \"In review\",\n    \"generated_at\": \"2026-09-29T13:05:00+00:00\",\n    \"brand_sales\": {\n      \"current\": 4254.45,\n      \"prior\": 5312.4,\n      \"change_pct\": -19.91,\n      \"units\": 151,\n      \"prior_units\": 172\n    },\n    \"core_sales\": {\n      \"current\": 4254.45,\n      \"prior\": 5312.4,\n      \"change_pct\": -19.91,\n      \"units\": 151,\n      \"prior_units\": 172,\n      \"change_units_pct\": -12.21\n    },\n    \"flags\": {\n      \"map\": 4,\n      \"amazon_retail\": 1,\n      \"buy_box_lost\": 6,\n      \"disco\": 0,\n      \"needs_decision\": 2,\n      \"suggested\": 17,\n      \"zero_sales\": 4,\n      \"low_share\": 3,\n      \"unit_swing\": 2,\n      \"seller_count_change\": 0\n    },\n    \"warning_count\": 1,\n    \"reviewed_by\": null,\n    \"reviewed_by_name\": null,\n    \"sent_by\": null,\n    \"sent_by_name\": null,\n    \"reviewed_at\": null,\n    \"scheduled_send_at\": null,\n    \"sent_at\": null,\n    \"sent_to\": null,\n    \"skipped_reason\": null,\n    \"failure_reason\": null,\n    \"tracked_job_log_id\": 54,\n    \"updated_at\": \"2026-09-29T13:05:00+00:00\",\n    \"data_as_of\": {\n      \"orders\": \"2026-10-03 18:36:43\",\n      \"traffic\": \"2026-10-01\",\n      \"offers\": \"2026-10-03T07:05:00+00:00\",\n      \"rank\": \"2026-09-27\",\n      \"market\": \"2026-09-29T13:05:00+00:00\"\n    },\n    \"warnings\": [\n      {\n        \"code\": \"unmapped_watch_asins\",\n        \"count\": 1\n      }\n    ],\n    \"totals\": {\n      \"account_sales\": {\n        \"current\": 8239.01,\n        \"prior\": 9408.89,\n        \"change_pct\": -12.43,\n        \"units\": 295\n      },\n      \"brand_sales\": {\n        \"current\": 4254.45,\n        \"prior\": 5312.4,\n        \"change_pct\": -19.91,\n        \"units\": 151,\n        \"prior_units\": 172\n      },\n      \"core_sales\": {\n        \"current\": 4254.45,\n        \"prior\": 5312.4,\n        \"change_pct\": -19.91,\n        \"units\": 151,\n        \"prior_units\": 172,\n        \"change_units_pct\": -12.21\n      },\n      \"brand_share_of_account_pct\": 51.64,\n      \"core_share_of_brand_pct\": 100,\n      \"channel_share_of_brand_pct\": {\n        \"6\": 95.05,\n        \"8\": 2.35,\n        \"10\": 2.61\n      },\n      \"market\": {\n        \"market_units\": 269,\n        \"market_sales_amount\": 7768.08,\n        \"own_units\": 138,\n        \"covered_lines\": 5,\n        \"total_lines\": 9,\n        \"partial\": true,\n        \"share_pct\": 51.3\n      }\n    },\n    \"summary_text\": null,\n    \"share_expires_at\": null,\n    \"share_open_count\": 0,\n    \"email_log_id\": null,\n    \"profile\": {\n      \"id\": 1,\n      \"review_required\": true,\n      \"map_mode\": \"pricing_tier\",\n      \"timezone\": \"America/Los_Angeles\"\n    },\n    \"channels\": [\n      {\n        \"sales_channel_id\": 6,\n        \"integration_instance_id\": 4,\n        \"marketplace_id\": \"ATVPDKIKX0DER\",\n        \"name\": \"Amazon\",\n        \"is_amazon\": true,\n        \"our_seller_id\": \"A2PNWDOCS7QK3Z\",\n        \"include_in_report\": true,\n        \"competition_monitored\": true,\n        \"competition_monitored_since\": \"2026-07-27T16:00:00+00:00\"\n      },\n      {\n        \"sales_channel_id\": 8,\n        \"integration_instance_id\": 6,\n        \"marketplace_id\": null,\n        \"name\": \"Shopify Storefront\",\n        \"is_amazon\": false,\n        \"our_seller_id\": null,\n        \"include_in_report\": true,\n        \"competition_monitored\": false,\n        \"competition_monitored_since\": null\n      },\n      {\n        \"sales_channel_id\": 10,\n        \"integration_instance_id\": 8,\n        \"marketplace_id\": null,\n        \"name\": \"Walmart Marketplace\",\n        \"is_amazon\": false,\n        \"our_seller_id\": null,\n        \"include_in_report\": true,\n        \"competition_monitored\": false,\n        \"competition_monitored_since\": null\n      }\n    ],\n    \"narrative\": {\n      \"insights\": {\n        \"notable_decreases\": [\n          \"SF-INS-32 (B0D7SF3201): 1 seller below MAP (lowest $36.99) — A3DMYIN9K0RYXC, 7 days in the period\",\n          \"SF-TUM-20 (B0D7SF2004): Amazon is selling this ASIN and holds the Buy Box\",\n          \"SF-TUM-20 (B0D7SF2004): 1 seller below MAP (lowest $26.95) — Amazon, 7 days in the period\",\n          \"B0D7SF3209: 1 seller below MAP (lowest $36.99) — A3DMYIN9K0RYXC, 7 days in the period\",\n          \"SF-JUG-64 (B0D7SF6405): units down 100% (10 → 0)\",\n          \"SF-KIT-CLN (B0D7SFCK08): 1 seller below MAP (lowest $10.62) — A2FRZPVYLUOBKW, 7 days in the period\"\n        ],\n        \"low_sales_competition\": [\n          \"SF-TUM-20 (B0D7SF2004): 4 other sellers including 3 other FBA sellers — we have 12% of the buy box\",\n          \"SF-JUG-64 (B0D7SF6405): 1 other seller including 0 other FBA sellers — we have 0% of the buy box\",\n          \"SF-KIT-CLN (B0D7SFCK08): 4 other sellers including 3 other FBA sellers — we have 8% of the buy box\"\n        ],\n        \"stockouts\": [\n          \"SF-JUG-64 (B0D7SF6405): Late FBA inbound — Shipment ID: FBA18SFJG64Q, ETA unknown\"\n        ]\n      },\n      \"actions\": [\n        \"Identify and cut off dealers selling directly to Amazon.com.\",\n        \"Cut off the MAP violators listed above.\",\n        \"A permission-based marketplace MAP policy would reduce seller cannibalisation on contested ASINs.\"\n      ],\n      \"text\": \"Insights\\n\\nNotable sales decreases:\\n- SF-INS-32 (B0D7SF3201): 1 seller below MAP (lowest $36.99) — A3DMYIN9K0RYXC, 7 days in the period\\n- SF-TUM-20 (B0D7SF2004): Amazon is selling this ASIN and holds the Buy Box\\n- SF-TUM-20 (B0D7SF2004): 1 seller below MAP (lowest $26.95) — Amazon, 7 days in the period\\n- B0D7SF3209: 1 seller below MAP (lowest $36.99) — A3DMYIN9K0RYXC, 7 days in the period\\n- SF-JUG-64 (B0D7SF6405): units down 100% (10 → 0)\\n- SF-KIT-CLN (B0D7SFCK08): 1 seller below MAP (lowest $10.62) — A2FRZPVYLUOBKW, 7 days in the period\\n\\nLow sales due to competition:\\n- SF-TUM-20 (B0D7SF2004): 4 other sellers including 3 other FBA sellers — we have 12% of the buy box\\n- SF-JUG-64 (B0D7SF6405): 1 other seller including 0 other FBA sellers — we have 0% of the buy box\\n- SF-KIT-CLN (B0D7SFCK08): 4 other sellers including 3 other FBA sellers — we have 8% of the buy box\\n\\nStockouts:\\n- SF-JUG-64 (B0D7SF6405): Late FBA inbound — Shipment ID: FBA18SFJG64Q, ETA unknown\\n\\nActions\\n\\n- Identify and cut off dealers selling directly to Amazon.com.\\n- Cut off the MAP violators listed above.\\n- A permission-based marketplace MAP policy would reduce seller cannibalisation on contested ASINs.\"\n    },\n    \"versions\": [\n      {\n        \"id\": 15,\n        \"version\": 1,\n        \"status\": \"in_review\",\n        \"generated_at\": \"2026-09-29T13:05:00+00:00\",\n        \"sent_at\": null\n      }\n    ],\n    \"lines\": [\n      {\n        \"id\": 260,\n        \"section\": \"channel\",\n        \"sales_channel_id\": 6,\n        \"asin\": null,\n        \"product_id\": null,\n        \"product\": null,\n        \"sku\": null,\n        \"is_core\": true,\n        \"sort_order\": 0,\n        \"price\": null,\n        \"sales_amount\": 4043.75,\n        \"units\": 145,\n        \"prior_sales_amount\": 5172.6,\n        \"prior_units\": 168,\n        \"change_pct\": -21.82,\n        \"change_units_pct\": -13.69,\n        \"channel_breakdown\": {\n          \"channel_name\": \"Amazon\",\n          \"account_sales\": 7918.34,\n          \"account_prior_sales\": 9070.14,\n          \"brand_share_of_channel_pct\": 51.07,\n          \"share_of_brand_pct\": 95.05,\n          \"listed_products\": 8,\n          \"issues\": {\n            \"map\": 4,\n            \"amazon_retail\": 1,\n            \"buy_box_lost\": 6\n          },\n          \"competition_monitored\": true,\n          \"competition_monitored_since\": \"2026-07-27T16:00:00+00:00\"\n        },\n        \"listing_gaps\": null,\n        \"on_hand\": null,\n        \"fba_available\": null,\n        \"inbound_units\": null,\n        \"inbound_shipment_ids\": null,\n        \"days_in_stock\": null,\n        \"reserved_units\": null,\n        \"sessions\": null,\n        \"page_views\": null,\n        \"buy_box_pct\": null,\n        \"sales_rank\": null,\n        \"sales_rank_avg\": null,\n        \"prior_sales_rank_avg\": null,\n        \"competition_monitored\": false,\n        \"map_violation\": null,\n        \"map_price\": null,\n        \"map_lowest_price\": null,\n        \"map_violating_seller_ids\": null,\n        \"map_days_below\": null,\n        \"our_offer_present\": null,\n        \"amazon_retail_present\": null,\n        \"buy_box_seller_id\": null,\n        \"buy_box_is_us\": null,\n        \"observed_at\": null,\n        \"listing_status\": null,\n        \"listing_quality_score\": null,\n        \"note_reason\": null,\n        \"note_reason_label\": null,\n        \"note_reason_source\": null,\n        \"note_text\": null,\n        \"suggested_note_reason\": null,\n        \"suggested_note_text\": null,\n        \"note_evidence\": null,\n        \"product_listing_ids\": [],\n        \"internal_only\": false,\n        \"needs_decision\": false,\n        \"analysis_flags\": [],\n        \"review_priority\": 6\n      },\n      {\n        \"id\": 263,\n        \"section\": \"product\",\n        \"sales_channel_id\": null,\n        \"asin\": null,\n        \"product_id\": 188,\n        \"product\": {\n          \"id\": 188,\n          \"sku\": \"SF-INS-32\",\n          \"name\": \"Summit Flask Insulated Bottle 32 oz — Glacier\"\n        },\n        \"sku\": \"SF-INS-32\",\n        \"is_core\": true,\n        \"sort_order\": 0,\n        \"price\": null,\n        \"sales_amount\": 1398.25,\n        \"units\": 35,\n        \"prior_sales_amount\": 2077.4,\n        \"prior_units\": 52,\n        \"change_pct\": -32.69,\n        \"change_units_pct\": -32.69,\n        \"channel_breakdown\": {\n          \"6\": {\n            \"sales_amount\": 1398.25,\n            \"units\": 35,\n            \"prior_sales_amount\": 2037.45,\n            \"prior_units\": 51,\n            \"listed\": true,\n            \"listing_status\": \"active\"\n          },\n          \"8\": {\n            \"sales_amount\": 0,\n            \"units\": 0,\n            \"prior_sales_amount\": 39.95,\n            \"prior_units\": 1,\n            \"listed\": false,\n            \"listing_status\": \"not_listed\"\n          },\n          \"10\": {\n            \"sales_amount\": 0,\n            \"units\": 0,\n            \"prior_sales_amount\": 0,\n            \"prior_units\": 0,\n            \"listed\": false,\n            \"listing_status\": \"not_listed\"\n          }\n        },\n        \"listing_gaps\": [\n          8,\n          10\n        ],\n        \"on_hand\": 150,\n        \"fba_available\": null,\n        \"inbound_units\": null,\n        \"inbound_shipment_ids\": null,\n        \"days_in_stock\": null,\n        \"reserved_units\": null,\n        \"sessions\": null,\n        \"page_views\": null,\n        \"buy_box_pct\": null,\n        \"sales_rank\": null,\n        \"sales_rank_avg\": null,\n        \"prior_sales_rank_avg\": null,\n        \"competition_monitored\": false,\n        \"map_violation\": null,\n        \"map_price\": null,\n        \"map_lowest_price\": null,\n        \"map_violating_seller_ids\": null,\n        \"map_days_below\": null,\n        \"our_offer_present\": null,\n        \"amazon_retail_present\": null,\n        \"buy_box_seller_id\": null,\n        \"buy_box_is_us\": null,\n        \"observed_at\": null,\n        \"listing_status\": null,\n        \"listing_quality_score\": null,\n        \"note_reason\": \"not_listed\",\n        \"note_reason_label\": \"Not listed on channel\",\n        \"note_reason_source\": \"suggested\",\n        \"note_text\": \"Not listed on Shopify Storefront, Walmart Marketplace\",\n        \"suggested_note_reason\": \"not_listed\",\n        \"suggested_note_text\": \"Not listed on Shopify Storefront, Walmart Marketplace\",\n        \"note_evidence\": {\n          \"channels\": [\n            \"Shopify Storefront\",\n            \"Walmart Marketplace\"\n          ]\n        },\n        \"product_listing_ids\": [],\n        \"internal_only\": true,\n        \"needs_decision\": false,\n        \"analysis_flags\": [],\n        \"review_priority\": 6\n      },\n      {\n        \"id\": 271,\n        \"section\": \"amazon\",\n        \"sales_channel_id\": 6,\n        \"asin\": \"B0D7SF3201\",\n        \"product_id\": 188,\n        \"product\": {\n          \"id\": 188,\n          \"sku\": \"SF-INS-32\",\n          \"name\": \"Summit Flask Insulated Bottle 32 oz — Glacier\"\n        },\n        \"sku\": \"SF-INS-32\",\n        \"is_core\": true,\n        \"sort_order\": 0,\n        \"price\": 39.95,\n        \"sales_amount\": 1398.25,\n        \"units\": 35,\n        \"prior_sales_amount\": 2037.45,\n        \"prior_units\": 51,\n        \"change_pct\": -31.37,\n        \"change_units_pct\": -31.37,\n        \"channel_breakdown\": null,\n        \"listing_gaps\": null,\n        \"on_hand\": 150,\n        \"fba_available\": 420,\n        \"inbound_units\": 0,\n        \"inbound_shipment_ids\": null,\n        \"days_in_stock\": 7,\n        \"reserved_units\": 2,\n        \"sessions\": 621,\n        \"page_views\": 756,\n        \"buy_box_pct\": 40.39,\n        \"sales_rank\": 1639,\n        \"sales_rank_avg\": 1380,\n        \"prior_sales_rank_avg\": 1492,\n        \"competition_monitored\": true,\n        \"map_violation\": true,\n        \"map_price\": 39.95,\n        \"map_lowest_price\": 36.99,\n        \"map_violating_seller_ids\": [\n          \"A3DMYIN9K0RYXC\"\n        ],\n        \"map_days_below\": 7,\n        \"our_offer_present\": true,\n        \"amazon_retail_present\": false,\n        \"buy_box_seller_id\": \"A1KQ7ZRV3M2T8B\",\n        \"buy_box_is_us\": false,\n        \"observed_at\": \"2026-09-27T19:05:00+00:00\",\n        \"listing_status\": \"active\",\n        \"listing_quality_score\": null,\n        \"note_reason\": \"competitor_buy_box\",\n        \"note_reason_label\": \"Low sales due to competitor\",\n        \"note_reason_source\": \"suggested\",\n        \"note_text\": \"Low sales due to competitor — 1 seller below MAP, lowest $36.99\",\n        \"suggested_note_reason\": \"competitor_buy_box\",\n        \"suggested_note_text\": \"Low sales due to competitor — 1 seller below MAP, lowest $36.99\",\n        \"note_evidence\": {\n          \"buy_box_seller_id\": \"A1KQ7ZRV3M2T8B\",\n          \"offer_count\": 7,\n          \"map_lowest_price\": 36.99,\n          \"sellers\": [\n            \"A3DMYIN9K0RYXC\"\n          ]\n        },\n        \"product_listing_ids\": [\n          292\n        ],\n        \"internal_only\": false,\n        \"needs_decision\": false,\n        \"analysis_flags\": [\n          \"map_break\"\n        ],\n        \"review_priority\": 3,\n        \"offer_count\": 7,\n        \"offer_count_capped\": false,\n        \"prime_offer_count\": 5,\n        \"fba_offer_count\": 5,\n        \"offer_count_max\": 7,\n        \"prior_offer_count\": 7,\n        \"prior_prime_offer_count\": 5,\n        \"market_units\": 87,\n        \"market_sales_amount\": 3497.4,\n        \"market_change_pct\": -1.14,\n        \"market_share_pct\": 40.23,\n        \"competitor_units\": 52,\n        \"competitor_units_by_seller\": null,\n        \"market_basis\": \"derived\",\n        \"market_confidence\": \"high\"\n      }\n    ]\n  }\n}"}]},{"name":"Update Brand Report Line","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/lines/{line}","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","lines",":line"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"},{"key":"line","value":"412","description":"Report line ID"}]},"description":"Sets or clears the note on one report line. Allowed while the report is in review.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage).\n\nBody:\n- note_reason (string|null) — disco, merged, duplicate_asin, late_inbound, stock_out, not_listed, backorder, amazon_retail, competitor_buy_box, map_break, we_hold_buy_box, ltsf_risk\n- note_text (string|null, max 500) — the brand-facing text\n- remember (boolean) — keep this reason for the ASIN/product in future reports until cleared\n- internal_only (boolean|null) — keep the note out of the brand-facing report","body":{"mode":"raw","raw":"{\n  \"note_reason\": \"late_inbound\",\n  \"note_text\": \"Late FBA inbound — Shipment ID: FBA18SFJG64Q, ETA Oct 9\",\n  \"remember\": false,\n  \"internal_only\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/lines/{line}","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","lines",":line"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"},{"key":"line","value":"412","description":"Report line ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 271,\n    \"section\": \"amazon\",\n    \"sales_channel_id\": 6,\n    \"asin\": \"B0D7SF3201\",\n    \"product_id\": 188,\n    \"product\": {\n      \"id\": 188,\n      \"sku\": \"SF-INS-32\",\n      \"name\": \"Summit Flask Insulated Bottle 32 oz — Glacier\"\n    },\n    \"sku\": \"SF-INS-32\",\n    \"is_core\": true,\n    \"sort_order\": 0,\n    \"price\": 39.95,\n    \"sales_amount\": 1398.25,\n    \"units\": 35,\n    \"prior_sales_amount\": 2037.45,\n    \"prior_units\": 51,\n    \"change_pct\": -31.37,\n    \"change_units_pct\": -31.37,\n    \"channel_breakdown\": null,\n    \"listing_gaps\": null,\n    \"on_hand\": 150,\n    \"fba_available\": 420,\n    \"inbound_units\": 0,\n    \"inbound_shipment_ids\": null,\n    \"days_in_stock\": 7,\n    \"reserved_units\": 2,\n    \"sessions\": 621,\n    \"page_views\": 756,\n    \"buy_box_pct\": 40.39,\n    \"sales_rank\": 1639,\n    \"sales_rank_avg\": 1380,\n    \"prior_sales_rank_avg\": 1492,\n    \"competition_monitored\": true,\n    \"map_violation\": true,\n    \"map_price\": 39.95,\n    \"map_lowest_price\": 36.99,\n    \"map_violating_seller_ids\": [\n      \"A3DMYIN9K0RYXC\"\n    ],\n    \"map_days_below\": 7,\n    \"our_offer_present\": true,\n    \"amazon_retail_present\": false,\n    \"buy_box_seller_id\": \"A1KQ7ZRV3M2T8B\",\n    \"buy_box_is_us\": false,\n    \"observed_at\": \"2026-09-27T19:05:00+00:00\",\n    \"listing_status\": \"active\",\n    \"listing_quality_score\": null,\n    \"note_reason\": \"competitor_buy_box\",\n    \"note_reason_label\": \"Low sales due to competitor\",\n    \"note_reason_source\": \"suggested\",\n    \"note_text\": \"Low sales due to competitor — 1 seller below MAP, lowest $36.99\",\n    \"suggested_note_reason\": \"competitor_buy_box\",\n    \"suggested_note_text\": \"Low sales due to competitor — 1 seller below MAP, lowest $36.99\",\n    \"note_evidence\": {\n      \"buy_box_seller_id\": \"A1KQ7ZRV3M2T8B\",\n      \"offer_count\": 7,\n      \"map_lowest_price\": 36.99,\n      \"sellers\": [\n        \"A3DMYIN9K0RYXC\"\n      ]\n    },\n    \"product_listing_ids\": [\n      292\n    ],\n    \"internal_only\": false,\n    \"needs_decision\": false,\n    \"analysis_flags\": [\n      \"map_break\"\n    ],\n    \"review_priority\": 3,\n    \"offer_count\": 7,\n    \"offer_count_capped\": false,\n    \"prime_offer_count\": 5,\n    \"fba_offer_count\": 5,\n    \"offer_count_max\": 7,\n    \"prior_offer_count\": 7,\n    \"prior_prime_offer_count\": 5,\n    \"market_units\": 87,\n    \"market_sales_amount\": 3497.4,\n    \"market_change_pct\": -1.14,\n    \"market_share_pct\": 40.23,\n    \"competitor_units\": 52,\n    \"competitor_units_by_seller\": null,\n    \"market_basis\": \"derived\",\n    \"market_confidence\": \"high\"\n  }\n}"}]},{"name":"Confirm Brand Report Suggestions","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/confirm-suggestions","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","confirm-suggestions"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]},"description":"Confirms every suggested note on the report as-is.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage)."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/confirm-suggestions","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","confirm-suggestions"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"17 suggested notes confirmed.\",\n  \"data\": {\n    \"confirmed\": 17\n  }\n}"}]},{"name":"Approve Brand Report","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/approve","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","approve"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]},"description":"Approves a report in review. With send_at, the report is sent automatically at that time using the template defaults and the brand's report recipients.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage; sending a send_at also needs brand_reports.send — 403 otherwise).\n\nBody (optional):\n- send_at (datetime, in the future)\n- updated_at (datetime) — the report's updated_at you last read; a mismatch returns 409 so concurrent reviewers don't overwrite each other","body":{"mode":"raw","raw":"{\n  \"send_at\": \"2026-10-06T16:00:00Z\",\n  \"updated_at\": \"2026-10-03T19:02:11+00:00\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/approve","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","approve"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Report approved.\",\n  \"data\": {\n    \"id\": 16,\n    \"brand\": {\n      \"id\": 37,\n      \"name\": \"EverHydrate\"\n    },\n    \"profile_id\": 2,\n    \"period_type\": \"weekly\",\n    \"period_start\": \"2026-09-21\",\n    \"period_end\": \"2026-09-27\",\n    \"period_label\": \"Sep 21 – 27, 2026\",\n    \"version\": 1,\n    \"latest_version\": 1,\n    \"has_sent_prior_version\": false,\n    \"status\": \"approved\",\n    \"status_label\": \"Approved\",\n    \"generated_at\": \"2026-09-29T13:06:00+00:00\",\n    \"brand_sales\": {\n      \"current\": 3984.56,\n      \"prior\": 4096.49,\n      \"change_pct\": -2.73,\n      \"units\": 144,\n      \"prior_units\": 151\n    },\n    \"core_sales\": {\n      \"current\": 3984.56,\n      \"prior\": 4096.49,\n      \"change_pct\": -2.73,\n      \"units\": 144,\n      \"prior_units\": 151,\n      \"change_units_pct\": -4.64\n    },\n    \"flags\": {\n      \"map\": 1,\n      \"amazon_retail\": 0,\n      \"buy_box_lost\": 0,\n      \"disco\": 0,\n      \"needs_decision\": 0,\n      \"suggested\": 0,\n      \"zero_sales\": 2,\n      \"low_share\": 0,\n      \"unit_swing\": 3,\n      \"seller_count_change\": 0\n    },\n    \"warning_count\": 0,\n    \"reviewed_by\": 1,\n    \"reviewed_by_name\": \"Administrator\",\n    \"sent_by\": null,\n    \"sent_by_name\": null,\n    \"reviewed_at\": \"2026-09-29T13:46:00+00:00\",\n    \"scheduled_send_at\": \"2026-10-05T09:00:00+00:00\",\n    \"sent_at\": null,\n    \"sent_to\": null,\n    \"skipped_reason\": null,\n    \"failure_reason\": null,\n    \"tracked_job_log_id\": 55,\n    \"updated_at\": \"2026-09-29T13:46:00+00:00\"\n  }\n}"}]},{"name":"Skip Brand Report","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/skip","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","skip"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]},"description":"Skips a report so it is never sent, recording why.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage).\n\nBody:\n- reason (required, max 255)\n- updated_at (datetime, optional) — optimistic lock, 409 on mismatch","body":{"mode":"raw","raw":"{\n  \"reason\": \"Brand paused its Amazon program this week\",\n  \"updated_at\": \"2026-10-03T19:02:11+00:00\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/skip","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","skip"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Report skipped.\",\n  \"data\": {\n    \"id\": 16,\n    \"brand\": {\n      \"id\": 37,\n      \"name\": \"EverHydrate\"\n    },\n    \"profile_id\": 2,\n    \"period_type\": \"weekly\",\n    \"period_start\": \"2026-09-21\",\n    \"period_end\": \"2026-09-27\",\n    \"period_label\": \"Sep 21 – 27, 2026\",\n    \"version\": 1,\n    \"latest_version\": 1,\n    \"has_sent_prior_version\": false,\n    \"status\": \"skipped\",\n    \"status_label\": \"Skipped\",\n    \"generated_at\": \"2026-09-29T13:06:00+00:00\",\n    \"brand_sales\": {\n      \"current\": 3984.56,\n      \"prior\": 4096.49,\n      \"change_pct\": -2.73,\n      \"units\": 144,\n      \"prior_units\": 151\n    },\n    \"core_sales\": {\n      \"current\": 3984.56,\n      \"prior\": 4096.49,\n      \"change_pct\": -2.73,\n      \"units\": 144,\n      \"prior_units\": 151,\n      \"change_units_pct\": -4.64\n    },\n    \"flags\": {\n      \"map\": 1,\n      \"amazon_retail\": 0,\n      \"buy_box_lost\": 0,\n      \"disco\": 0,\n      \"needs_decision\": 0,\n      \"suggested\": 0,\n      \"zero_sales\": 2,\n      \"low_share\": 0,\n      \"unit_swing\": 3,\n      \"seller_count_change\": 0\n    },\n    \"warning_count\": 0,\n    \"reviewed_by\": 1,\n    \"reviewed_by_name\": \"Administrator\",\n    \"sent_by\": null,\n    \"sent_by_name\": null,\n    \"reviewed_at\": \"2026-09-29T13:46:00+00:00\",\n    \"scheduled_send_at\": \"2026-10-05T09:00:00+00:00\",\n    \"sent_at\": null,\n    \"sent_to\": null,\n    \"skipped_reason\": \"Brand paused its Amazon program this week\",\n    \"failure_reason\": null,\n    \"tracked_job_log_id\": 55,\n    \"updated_at\": \"2026-09-29T13:46:00+00:00\"\n  }\n}"}]},{"name":"Regenerate Brand Report","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/regenerate","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","regenerate"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]},"description":"Generates a new version of the report from current data, in the background. The previous version stays readable; a version that was already sent never changes.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage)."},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/regenerate","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","regenerate"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Generating version 2 — track progress in the job tray.\",\n  \"data\": {\n    \"run\": {\n      \"id\": 16,\n      \"brand\": {\n        \"id\": 37,\n        \"name\": \"EverHydrate\"\n      },\n      \"profile_id\": 2,\n      \"period_type\": \"weekly\",\n      \"period_start\": \"2026-09-21\",\n      \"period_end\": \"2026-09-27\",\n      \"period_label\": \"Sep 21 – 27, 2026\",\n      \"version\": 2,\n      \"latest_version\": 1,\n      \"has_sent_prior_version\": false,\n      \"status\": \"scheduled\",\n      \"status_label\": \"Approved\",\n      \"generated_at\": \"2026-09-29T13:06:00+00:00\",\n      \"brand_sales\": {\n        \"current\": 3984.56,\n        \"prior\": 4096.49,\n        \"change_pct\": -2.73,\n        \"units\": 144,\n        \"prior_units\": 151\n      },\n      \"core_sales\": {\n        \"current\": 3984.56,\n        \"prior\": 4096.49,\n        \"change_pct\": -2.73,\n        \"units\": 144,\n        \"prior_units\": 151,\n        \"change_units_pct\": -4.64\n      },\n      \"flags\": {\n        \"map\": 1,\n        \"amazon_retail\": 0,\n        \"buy_box_lost\": 0,\n        \"disco\": 0,\n        \"needs_decision\": 0,\n        \"suggested\": 0,\n        \"zero_sales\": 2,\n        \"low_share\": 0,\n        \"unit_swing\": 3,\n        \"seller_count_change\": 0\n      },\n      \"warning_count\": 0,\n      \"reviewed_by\": 1,\n      \"reviewed_by_name\": \"Administrator\",\n      \"sent_by\": null,\n      \"sent_by_name\": null,\n      \"reviewed_at\": \"2026-09-29T13:46:00+00:00\",\n      \"scheduled_send_at\": \"2026-10-05T09:00:00+00:00\",\n      \"sent_at\": null,\n      \"sent_to\": null,\n      \"skipped_reason\": null,\n      \"failure_reason\": null,\n      \"tracked_job_log_id\": 55,\n      \"updated_at\": \"2026-09-29T13:46:00+00:00\"\n    },\n    \"tracked_job_log_id\": 5831\n  }\n}"}]},{"name":"Bulk Approve Brand Reports","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/bulk-approve","host":["{{baseUrl}}"],"path":["api","v2","brand-reports","bulk-approve"]},"description":"Approves several reports in review — the given IDs, or every report matching the list filters (latest versions only unless filters.latest_only is 0). Reports whose status does not allow approval are left unchanged and listed under `skipped`.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage).\n\nBody:\n- ids[] (max 500) — required unless apply_to_all is true\n- apply_to_all (boolean) + filters (object, same as List Brand Reports)","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    15,\n    17\n  ],\n  \"apply_to_all\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/bulk-approve","host":["{{baseUrl}}"],"path":["api","v2","brand-reports","bulk-approve"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"1 report approved. 1 left unchanged (its status does not allow it).\",\n  \"data\": {\n    \"processed\": 1,\n    \"skipped\": [\n      {\n        \"id\": 17,\n        \"reason\": \"A Sent report cannot be approved.\"\n      }\n    ]\n  }\n}"}]},{"name":"Bulk Skip Brand Reports","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/bulk-skip","host":["{{baseUrl}}"],"path":["api","v2","brand-reports","bulk-skip"]},"description":"Skips several reports with one reason — the given IDs, or every report matching the list filters.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage).\n\nBody:\n- reason (required, max 255)\n- ids[] (max 500) — required unless apply_to_all is true\n- apply_to_all (boolean) + filters (object, same as List Brand Reports)","body":{"mode":"raw","raw":"{\n  \"reason\": \"Holiday week — reports paused\",\n  \"apply_to_all\": true,\n  \"filters\": {\n    \"status\": \"in_review\",\n    \"period_type\": \"weekly\"\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/bulk-skip","host":["{{baseUrl}}"],"path":["api","v2","brand-reports","bulk-skip"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 reports skipped.\",\n  \"data\": {\n    \"processed\": 2,\n    \"skipped\": []\n  }\n}"}]},{"name":"Get Brand Report Email Composition","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/send-composition","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","send-composition"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]},"description":"The default email for this report: recipients (contacts with the Report Recipient role), CC, subject and body from the brand report template, attachment defaults, the active share link, the summary paragraph, the sender identity and — when sending is not possible — `send_blocked_reason` (`no_report_recipients` or `status`).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.send)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/send-composition","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","send-composition"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"to\": [\n      {\n        \"email\": \"dana.whitfield@summitflask.example\",\n        \"name\": \"Dana Whitfield\"\n      },\n      {\n        \"email\": \"marcus.lee@summitflask.example\",\n        \"name\": \"Marcus Lee\"\n      }\n    ],\n    \"cc\": [\n      \"brands@northwind.example\"\n    ],\n    \"subject\": \"{{brand_name}} weekly report — {{period_label}}\",\n    \"body\": \"<p>Hi {{contact_first_name}},</p><p>Here is the {{brand_name}} weekly report for <strong>{{period_label}}</strong>.</p><p>{{summary_paragraph}}</p><p><strong>Brand sales:</strong> {{brand_sales}} ({{brand_sales_change}} vs the prior week)<br><strong>Core sales:</strong> {{core_sales}} ({{core_sales_change}})</p><p>The full report: {{share_url}}</p><p>Thank you,<br>{{store_name}}</p>\",\n    \"template_is_custom\": false,\n    \"attachments\": {\n      \"xlsx\": true,\n      \"pdf\": true,\n      \"link\": true\n    },\n    \"share_link_days\": 90,\n    \"share_url\": null,\n    \"summary\": null,\n    \"ai_summary_available\": true,\n    \"from\": {\n      \"address\": \"noreply@sku.io\",\n      \"name\": \"Mamimoo Trading, Inc.\"\n    },\n    \"reply_to\": {\n      \"address\": \"dev@sku.io\",\n      \"name\": \"Administrator\"\n    },\n    \"from_default_mode\": \"system\",\n    \"scheduled_send_at\": null,\n    \"send_blocked_reason\": null\n  }\n}"}]},{"name":"Preview Brand Report Email","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/send-preview","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","send-preview"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]},"description":"Renders the email exactly as the first recipient would receive it (merge fields filled). Same body as Send Brand Report; recipients are optional.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.send).\n\nBody:\n- to[] (required, 1–50) — {email (required), name}\n- cc[] (max 20 emails)\n- subject (required, max 255) and body (required, HTML, max 100000) — merge fields such as {{brand_name}}, {{period_label}}, {{contact_first_name}}, {{summary_paragraph}}, {{share_url}} are filled per recipient\n- attachments.xlsx / attachments.pdf / attachments.link (boolean)\n- send_at (datetime, optional) — schedule the send instead of sending now (the report is approved and the message stored)\n- from_mode — merchant (your own sending domain) | system\n- summary (string, optional, max 10000) — the summary paragraph as edited in the send dialog; saved on the report before the attachments are built so the PDF, XLSX and share page carry the same words as the email (send only; omit to keep the stored summary)","body":{"mode":"raw","raw":"{\n  \"to\": [\n    {\n      \"email\": \"dana.whitfield@summitflask.example\",\n      \"name\": \"Dana Whitfield\"\n    }\n  ],\n  \"cc\": [\n    \"brands@acme.example\"\n  ],\n  \"subject\": \"{{brand_name}} weekly report — {{period_label}}\",\n  \"body\": \"<p>Hi {{contact_first_name}},</p><p>{{summary_paragraph}}</p><p>Full report: {{share_url}}</p>\",\n  \"attachments\": {\n    \"xlsx\": true,\n    \"pdf\": true,\n    \"link\": true\n  },\n  \"send_at\": null,\n  \"from_mode\": \"merchant\",\n  \"summary\": \"Core sales held steady; two sellers broke MAP on the 32 oz bottle.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/send-preview","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","send-preview"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"subject\": \"Summit Flask weekly report — Sep 21 – 27, 2026\",\n    \"body\": \"<p>Hi Dana,</p><p>Summit Flask sales were $4,254 this week, down 20%. One seller is below MAP on SF-INS-32 since Sep 18.</p><p>Full report: <a href=\\\"https://acme.sku.io/brand-report/Xy7…\\\">https://acme.sku.io/brand-report/Xy7…</a></p>\"\n  }\n}"}]},{"name":"Draft Brand Report Summary","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/draft-summary","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","draft-summary"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]},"description":"Drafts (or redrafts) the summary paragraph with AI from the report's confirmed notes and keeps it on the report so it can be edited and appears in the PDF and share page. Returns summary null and leaves existing text unchanged when AI is turned off or fails.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage)."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/draft-summary","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","draft-summary"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"summary\": \"Summit Flask sales were $4,254 for Sep 21 – 27, down 20% on the prior week. A seller has been below MAP on the 32 oz Glacier bottle since Sep 18, and Amazon retail holds the Buy Box on the Travel Tumbler.\",\n    \"ai_available\": true\n  }\n}"}]},{"name":"Send Brand Report","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/send","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","send"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]},"description":"Sends the report to the given recipients from your outbound email identity, with the chosen attachments and share link, and records the delivery (per-recipient status and email log). While the email goes out the report is `sending` (claimed by this send — edits, approval, skip and regenerate are refused until it finishes). If some recipients fail the report becomes `sent_partial`; if none succeed, 422 is returned and the report returns to the status it had before the send.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.send).\n\nBody:\n- to[] (required, 1–50) — {email (required), name}\n- cc[] (max 20 emails)\n- subject (required, max 255) and body (required, HTML, max 100000) — merge fields such as {{brand_name}}, {{period_label}}, {{contact_first_name}}, {{summary_paragraph}}, {{share_url}} are filled per recipient\n- attachments.xlsx / attachments.pdf / attachments.link (boolean)\n- send_at (datetime, optional) — schedule the send instead of sending now (the report is approved and the message stored)\n- from_mode — merchant (your own sending domain) | system\n- summary (string, optional, max 10000) — the summary paragraph as edited in the send dialog; saved on the report before the attachments are built so the PDF, XLSX and share page carry the same words as the email (send only; omit to keep the stored summary)\n\n**Permissions:** `brand_reports.send`. Sending a report that is still `in_review` approves it, and a `summary` that differs from the stored one rewrites it — both also require `brand_reports.manage` (403 otherwise). A send-only user delivers already-approved content as it stands (omit `summary` or send it unchanged).\n\n**Failures:** a scheduled send that delivers nothing (attachment rendering failed, or no recipient accepted the email) is retried 15 minutes later, up to 3 attempts, then waits for a person to resend.","body":{"mode":"raw","raw":"{\n  \"to\": [\n    {\n      \"email\": \"dana.whitfield@summitflask.example\",\n      \"name\": \"Dana Whitfield\"\n    }\n  ],\n  \"cc\": [\n    \"brands@acme.example\"\n  ],\n  \"subject\": \"{{brand_name}} weekly report — {{period_label}}\",\n  \"body\": \"<p>Hi {{contact_first_name}},</p><p>{{summary_paragraph}}</p><p>Full report: {{share_url}}</p>\",\n  \"attachments\": {\n    \"xlsx\": true,\n    \"pdf\": true,\n    \"link\": true\n  },\n  \"send_at\": null,\n  \"from_mode\": \"merchant\",\n  \"summary\": \"Core sales held steady; two sellers broke MAP on the 32 oz bottle.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/send","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","send"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 13,\n    \"status\": \"sent\",\n    \"status_label\": \"Sent\",\n    \"version\": 1,\n    \"sent_at\": \"2026-09-22T14:40:00+00:00\",\n    \"sent_by\": 1,\n    \"sent_to\": [\n      {\n        \"email\": \"dana.whitfield@summitflask.example\",\n        \"name\": \"Dana Whitfield\",\n        \"status\": \"sent\",\n        \"email_log_id\": 7,\n        \"error\": null,\n        \"sent_at\": \"2026-09-22T14:40:00+00:00\"\n      },\n      {\n        \"email\": \"marcus.lee@summitflask.example\",\n        \"name\": \"Marcus Lee\",\n        \"status\": \"sent\",\n        \"email_log_id\": 8,\n        \"error\": null,\n        \"sent_at\": \"2026-09-22T14:40:00+00:00\"\n      }\n    ],\n    \"email_log_id\": 7,\n    \"scheduled_send_at\": null,\n    \"failure_reason\": null,\n    \"share\": {\n      \"url\": \"https://acme.sku.io/brand-report/y5dygBfLTyUdeWECoLBsr8PmCmg809KutpRTwHOFWZ9TPWsm\",\n      \"active\": true,\n      \"expires_at\": \"2026-12-21T14:40:00+00:00\",\n      \"open_count\": 2,\n      \"last_opened_at\": \"2026-09-22T19:05:00+00:00\"\n    }\n  }\n}"}]},{"name":"Resend Brand Report","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/resend","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","resend"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]},"description":"Resends the same version. Without emails, only the recipients that failed last time are retried.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.send).\n\nBody (optional):\n- emails[] (max 50)","body":{"mode":"raw","raw":"{\n  \"emails\": [\n    \"marcus.lee@summitflask.example\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/resend","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","resend"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 13,\n    \"status\": \"sent\",\n    \"status_label\": \"Sent\",\n    \"version\": 1,\n    \"sent_at\": \"2026-09-22T14:40:00+00:00\",\n    \"sent_by\": 1,\n    \"sent_to\": [\n      {\n        \"email\": \"dana.whitfield@summitflask.example\",\n        \"name\": \"Dana Whitfield\",\n        \"status\": \"sent\",\n        \"email_log_id\": 7,\n        \"error\": null,\n        \"sent_at\": \"2026-09-22T14:40:00+00:00\"\n      },\n      {\n        \"email\": \"marcus.lee@summitflask.example\",\n        \"name\": \"Marcus Lee\",\n        \"status\": \"sent\",\n        \"email_log_id\": 8,\n        \"error\": null,\n        \"sent_at\": \"2026-09-22T14:40:00+00:00\"\n      }\n    ],\n    \"email_log_id\": 7,\n    \"scheduled_send_at\": null,\n    \"failure_reason\": null,\n    \"share\": {\n      \"url\": \"https://acme.sku.io/brand-report/y5dygBfLTyUdeWECoLBsr8PmCmg809KutpRTwHOFWZ9TPWsm\",\n      \"active\": true,\n      \"expires_at\": \"2026-12-21T14:40:00+00:00\",\n      \"open_count\": 2,\n      \"last_opened_at\": \"2026-09-22T19:05:00+00:00\"\n    }\n  }\n}"}]},{"name":"Export Brand Report","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/export?format=xlsx","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","export"],"query":[{"key":"format","value":"xlsx","description":"xlsx | pdf","disabled":true}],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]},"description":"Downloads the brand-facing report as XLSX or PDF (the same files attached to the email). Internal-only notes are never included. Response is a file download.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.index).\n\nQuery:\n- format (required) — xlsx | pdf"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/export?format=xlsx","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","export"],"query":[{"key":"format","value":"xlsx","description":"xlsx | pdf","disabled":true}],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary file: Summit-Flask-weekly-2026-09-21.xlsx)"}]},{"name":"Get Brand Report Delivery Status","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/delivery","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","delivery"],"variable":[{"key":"run","value":"13","description":"Brand report run ID"}]},"description":"Delivery record of a report: status, sent at, per-recipient results (status, email log id, error), scheduled send time and the share link state (URL, expiry, open count).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.index)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/delivery","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","delivery"],"variable":[{"key":"run","value":"13","description":"Brand report run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 13,\n    \"status\": \"sent\",\n    \"status_label\": \"Sent\",\n    \"version\": 1,\n    \"sent_at\": \"2026-09-22T14:40:00+00:00\",\n    \"sent_by\": 1,\n    \"sent_to\": [\n      {\n        \"email\": \"dana.whitfield@summitflask.example\",\n        \"name\": \"Dana Whitfield\",\n        \"status\": \"sent\",\n        \"email_log_id\": 7,\n        \"error\": null,\n        \"sent_at\": \"2026-09-22T14:40:00+00:00\"\n      },\n      {\n        \"email\": \"marcus.lee@summitflask.example\",\n        \"name\": \"Marcus Lee\",\n        \"status\": \"sent\",\n        \"email_log_id\": 8,\n        \"error\": null,\n        \"sent_at\": \"2026-09-22T14:40:00+00:00\"\n      }\n    ],\n    \"email_log_id\": 7,\n    \"scheduled_send_at\": null,\n    \"failure_reason\": null,\n    \"share\": {\n      \"url\": \"https://acme.sku.io/brand-report/y5dygBfLTyUdeWECoLBsr8PmCmg809KutpRTwHOFWZ9TPWsm\",\n      \"active\": true,\n      \"expires_at\": \"2026-12-21T14:40:00+00:00\",\n      \"open_count\": 2,\n      \"last_opened_at\": \"2026-09-22T19:05:00+00:00\"\n    }\n  }\n}"}]},{"name":"Get Brand Report Share Link","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/share-link","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","share-link"],"variable":[{"key":"run","value":"13","description":"Brand report run ID"}]},"description":"Returns the report's delivery record including `share` — the read-only public link the brand can open without logging in (null when none is active), its expiry and open count.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.index)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/share-link","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","share-link"],"variable":[{"key":"run","value":"13","description":"Brand report run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 13,\n    \"status\": \"sent\",\n    \"status_label\": \"Sent\",\n    \"version\": 1,\n    \"sent_at\": \"2026-09-22T14:40:00+00:00\",\n    \"sent_by\": 1,\n    \"sent_to\": [\n      {\n        \"email\": \"dana.whitfield@summitflask.example\",\n        \"name\": \"Dana Whitfield\",\n        \"status\": \"sent\",\n        \"email_log_id\": 7,\n        \"error\": null,\n        \"sent_at\": \"2026-09-22T14:40:00+00:00\"\n      },\n      {\n        \"email\": \"marcus.lee@summitflask.example\",\n        \"name\": \"Marcus Lee\",\n        \"status\": \"sent\",\n        \"email_log_id\": 8,\n        \"error\": null,\n        \"sent_at\": \"2026-09-22T14:40:00+00:00\"\n      }\n    ],\n    \"email_log_id\": 7,\n    \"scheduled_send_at\": null,\n    \"failure_reason\": null,\n    \"share\": {\n      \"url\": \"https://acme.sku.io/brand-report/y5dygBfLTyUdeWECoLBsr8PmCmg809KutpRTwHOFWZ9TPWsm\",\n      \"active\": true,\n      \"expires_at\": \"2026-12-21T14:40:00+00:00\",\n      \"open_count\": 2,\n      \"last_opened_at\": \"2026-09-22T19:05:00+00:00\"\n    }\n  }\n}"}]},{"name":"Create Brand Report Share Link","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/share-link","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","share-link"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]},"description":"Creates the report's share link if none is active (expiry from the brand's share-link days), and returns it.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.send)."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/share-link","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","share-link"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 13,\n    \"status\": \"sent\",\n    \"status_label\": \"Sent\",\n    \"version\": 1,\n    \"sent_at\": \"2026-09-22T14:40:00+00:00\",\n    \"sent_by\": 1,\n    \"sent_to\": [\n      {\n        \"email\": \"dana.whitfield@summitflask.example\",\n        \"name\": \"Dana Whitfield\",\n        \"status\": \"sent\",\n        \"email_log_id\": 7,\n        \"error\": null,\n        \"sent_at\": \"2026-09-22T14:40:00+00:00\"\n      },\n      {\n        \"email\": \"marcus.lee@summitflask.example\",\n        \"name\": \"Marcus Lee\",\n        \"status\": \"sent\",\n        \"email_log_id\": 8,\n        \"error\": null,\n        \"sent_at\": \"2026-09-22T14:40:00+00:00\"\n      }\n    ],\n    \"email_log_id\": 7,\n    \"scheduled_send_at\": null,\n    \"failure_reason\": null,\n    \"share\": {\n      \"url\": \"https://acme.sku.io/brand-report/y5dygBfLTyUdeWECoLBsr8PmCmg809KutpRTwHOFWZ9TPWsm\",\n      \"active\": true,\n      \"expires_at\": \"2026-12-21T14:40:00+00:00\",\n      \"open_count\": 2,\n      \"last_opened_at\": \"2026-09-22T19:05:00+00:00\"\n    }\n  }\n}"}]},{"name":"Revoke Brand Report Share Link","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/share-link/revoke","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","share-link","revoke"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]},"description":"Revokes the active share link immediately; anyone opening the old link sees a neutral expired page. Creating a link afterwards issues a new token.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.send)."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brand-reports/{run}/share-link/revoke","host":["{{baseUrl}}"],"path":["api","v2","brand-reports",":run","share-link","revoke"],"variable":[{"key":"run","value":"15","description":"Brand report run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 13,\n    \"status\": \"sent\",\n    \"status_label\": \"Sent\",\n    \"version\": 1,\n    \"sent_at\": \"2026-09-22T14:40:00+00:00\",\n    \"sent_by\": 1,\n    \"sent_to\": [\n      {\n        \"email\": \"dana.whitfield@summitflask.example\",\n        \"name\": \"Dana Whitfield\",\n        \"status\": \"sent\",\n        \"email_log_id\": 7,\n        \"error\": null,\n        \"sent_at\": \"2026-09-22T14:40:00+00:00\"\n      },\n      {\n        \"email\": \"marcus.lee@summitflask.example\",\n        \"name\": \"Marcus Lee\",\n        \"status\": \"sent\",\n        \"email_log_id\": 8,\n        \"error\": null,\n        \"sent_at\": \"2026-09-22T14:40:00+00:00\"\n      }\n    ],\n    \"email_log_id\": 7,\n    \"scheduled_send_at\": null,\n    \"failure_reason\": null,\n    \"share\": {\n      \"url\": null,\n      \"active\": false,\n      \"expires_at\": null,\n      \"open_count\": 2,\n      \"last_opened_at\": \"2026-09-22T19:05:00+00:00\"\n    }\n  }\n}"}]},{"name":"Get Brand Listing Quality","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/listing-quality","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","listing-quality"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]},"description":"Latest listing-quality audit per watched ASIN (score 0–100, per-check result and evidence, action items), the brand score, the monthly score trend and the Brand Store confirmation. Checks: image count, white main-image background, mobile-readable images (AI vision), bullet count, bullet lead-ins, A+ content, formatted description, Brand Store, title length. Checks that cannot be determined are `unknown` and excluded from the score.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.index)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/listing-quality","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","listing-quality"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"profile_id\": 1,\n    \"brand_id\": 36,\n    \"brand_score\": 80,\n    \"last_audited_on\": \"2026-10-01\",\n    \"action_threshold\": 70,\n    \"below_threshold\": 1,\n    \"rows\": [\n      {\n        \"watch_asin_id\": 8,\n        \"asin\": \"B0D7SFCK08\",\n        \"sales_channel_id\": 6,\n        \"integration_instance_id\": 4,\n        \"product_id\": 195,\n        \"sku\": \"SF-KIT-CLN\",\n        \"score\": 18,\n        \"audited_on\": \"2026-10-01\",\n        \"checks\": {\n          \"image_count\": {\n            \"result\": \"partial\",\n            \"weight\": 15,\n            \"credit\": 0.5,\n            \"label\": \"Image count\",\n            \"note\": \"Only 4 images.\",\n            \"evidence\": {\n              \"count\": 4,\n              \"variants\": [\n                \"MAIN\"\n              ]\n            }\n          },\n          \"main_image_white_bg\": {\n            \"result\": \"fail\",\n            \"weight\": 10,\n            \"credit\": 0,\n            \"label\": \"Main image on white\",\n            \"note\": \"0% of the border is white.\",\n            \"evidence\": {\n              \"image_url\": \"https://images.docs.example/brand/B0D7SFCK08-MAIN.png\",\n              \"white_ratio\": 0,\n              \"samples\": 464,\n              \"threshold\": 0.98\n            }\n          },\n          \"mobile_optimized_images\": {\n            \"result\": \"unknown\",\n            \"weight\": 15,\n            \"credit\": 0,\n            \"label\": \"Mobile-readable images\",\n            \"note\": \"The AI provider did not answer for any image.\",\n            \"evidence\": {\n              \"images\": [\n                {\n                  \"variant\": \"PT01\",\n                  \"image_url\": \"https://images.docs.example/brand/B0D7SFCK08-PT01.png\",\n                  \"has_readable_overlay\": null,\n                  \"confidence\": null,\n                  \"overlay_text\": null\n                }\n              ],\n              \"model\": \"claude-sonnet-4-5\",\n              \"provider\": \"anthropic\",\n              \"prompt_hash\": \"d9021971ba4b2874\",\n              \"confidence_threshold\": 0.7\n            }\n          },\n          \"bullet_count\": {\n            \"result\": \"fail\",\n            \"weight\": 10,\n            \"credit\": 0,\n            \"label\": \"Five bullets\",\n            \"note\": \"2 of 5 bullets.\",\n            \"evidence\": {\n              \"count\": 2\n            }\n          },\n          \"bullet_lead_in\": {\n            \"result\": \"fail\",\n            \"weight\": 10,\n            \"credit\": 0,\n            \"label\": \"Bullet lead-ins\",\n            \"note\": \"0 of 2 bullets have an ALL-CAPS lead-in.\",\n            \"evidence\": {\n              \"matching\": 0,\n              \"bullets\": 2\n            }\n          },\n          \"aplus_content\": {\n            \"result\": \"unknown\",\n            \"weight\": 15,\n            \"credit\": 0,\n            \"label\": \"A+ content\",\n            \"note\": \"Not confirmed yet — A+ content needs Brand Registry to check automatically.\",\n            \"evidence\": []\n          },\n          \"description_formatted\": {\n            \"result\": \"fail\",\n            \"weight\": 10,\n            \"credit\": 0,\n            \"label\": \"Formatted description\",\n            \"note\": \"Description is only 91 characters.\",\n            \"evidence\": {\n              \"length\": 91,\n              \"line_breaks\": 0,\n              \"dash_bullets\": 0\n            }\n          },\n          \"brand_store\": {\n            \"result\": \"pass\",\n            \"weight\": 5,\n            \"credit\": 1,\n            \"label\": \"Brand Store page\",\n            \"note\": null,\n            \"evidence\": {\n              \"url\": \"https://www.amazon.com/stores/SummitFlask/page/7B2F1E40\",\n              \"confirmed_at\": \"2026-08-12\",\n              \"source\": \"manual\"\n            }\n          },\n          \"title_length\": {\n            \"result\": \"fail\",\n            \"weight\": 10,\n            \"credit\": 0,\n            \"label\": \"Title length\",\n            \"note\": \"Title is 32 characters.\",\n            \"evidence\": {\n              \"length\": 32\n            }\n          }\n        },\n        \"action_items\": [\n          {\n            \"check\": \"image_count\",\n            \"label\": \"Image count\",\n            \"text\": \"Use all 9 image slots (at least 7).\",\n            \"result\": \"partial\"\n          }\n        ],\n        \"aplus_confirmed_at\": null\n      }\n    ],\n    \"trend\": [\n      {\n        \"month\": \"2026-09\",\n        \"score\": 74,\n        \"audited\": 9\n      }\n    ],\n    \"brand_store\": {\n      \"url\": \"https://www.amazon.com/stores/SummitFlask/page/7B2F1E40\",\n      \"confirmed_at\": \"2026-08-12\"\n    },\n    \"rubric\": [\n      {\n        \"key\": \"image_count\",\n        \"label\": \"Image count\",\n        \"weight\": 15,\n        \"manual\": false,\n        \"action\": \"Use all 9 image slots (at least 7).\"\n      }\n    ]\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Start Brand Listing Quality Audit","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/listing-quality/audit","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","listing-quality","audit"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]},"description":"Audits every watched ASIN of the brand now, in the background (it also runs monthly).\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage)."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/listing-quality/audit","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","listing-quality","audit"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 5840\n  },\n  \"message\": \"Listing quality audit started.\"\n}"}]},{"name":"Confirm A+ Content for Watched ASIN","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/listing-quality/watch-asins/{watchAsin}/aplus","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","listing-quality","watch-asins",":watchAsin","aplus"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"},{"key":"watchAsin","value":"1","description":"Watched ASIN ID"}]},"description":"Records (or clears) a manual confirmation that the ASIN has A+ content, dated today.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage).\n\nBody:\n- confirmed (boolean, required)","body":{"mode":"raw","raw":"{\n  \"confirmed\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/listing-quality/watch-asins/{watchAsin}/aplus","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","listing-quality","watch-asins",":watchAsin","aplus"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"},{"key":"watchAsin","value":"1","description":"Watched ASIN ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"asin\": \"B0D7SF3201\",\n    \"aplus_confirmed_at\": \"2026-10-03\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Confirm Brand Store","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/listing-quality/brand-store","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","listing-quality","brand-store"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]},"description":"Records (or clears) a manual confirmation that the brand has an Amazon Brand Store page.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage).\n\nBody:\n- confirmed (boolean, required)\n- url (URL, required when confirmed is true)","body":{"mode":"raw","raw":"{\n  \"confirmed\": true,\n  \"url\": \"https://www.amazon.com/stores/SummitFlask/page/1A2B3C4D\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/listing-quality/brand-store","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","listing-quality","brand-store"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"url\": \"https://www.amazon.com/stores/SummitFlask/page/1A2B3C4D\",\n    \"confirmed_at\": \"2026-10-03\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Get Brand FBA Inventory Health","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/fba-inventory-health","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","fba-inventory-health"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]},"description":"FBA inventory health for the brand: units and value by age bucket, units at 271–365 days and over 365 days, current and projected aged-inventory surcharge, surcharge charged in the period, days of supply, units stuck in Reserved for more than 14 days, cross-channel listing gaps for slow movers and the top slow movers.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.index).\n\nQuery (optional, together): period_start, period_end (YYYY-MM-DD). Default: the previous calendar month."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/fba-inventory-health","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","fba-inventory-health"],"variable":[{"key":"brand","value":"36","description":"Product brand ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"brand_id\": 36,\n    \"period_start\": \"2026-09-01\",\n    \"period_end\": \"2026-09-30\",\n    \"as_of\": \"2026-10-03T20:09:18+00:00\",\n    \"has_fba_inventory\": true,\n    \"integration_instance_ids\": [\n      4\n    ],\n    \"total_units\": 1277,\n    \"total_value\": 9680.7,\n    \"age_buckets\": [\n      {\n        \"label\": \"0-30\",\n        \"units\": 1225,\n        \"value\": 9597.5\n      },\n      {\n        \"label\": \"31-60\",\n        \"units\": 0,\n        \"value\": 0\n      }\n    ],\n    \"warning_units\": 40,\n    \"over_365_units\": 12,\n    \"current_monthly_surcharge\": 58,\n    \"projected_surcharge_next_month\": 58,\n    \"charged_surcharge_in_period\": 0,\n    \"charged_surcharge_units\": 0,\n    \"charged_surcharge_currency\": null,\n    \"days_of_supply\": 41.8,\n    \"restock_alerts\": 1,\n    \"reserved_units\": 18,\n    \"stuck_reserved_units\": 18,\n    \"reserved_history_days\": 21,\n    \"stuck_reserved\": [\n      {\n        \"fnsku\": \"X00F9F2DE7\",\n        \"merchant_sku\": \"SF-INS-24\",\n        \"asin\": \"B0D7SF2402\",\n        \"units\": 18,\n        \"since\": \"2026-09-13\"\n      }\n    ],\n    \"listing_gaps\": [\n      {\n        \"product_id\": 195,\n        \"sku\": \"SF-KIT-CLN\",\n        \"name\": \"Summit Flask Bottle Cleaning Kit\",\n        \"fba_units\": 100,\n        \"sold_90d\": 4,\n        \"missing_channels\": [\n          {\n            \"sales_channel_id\": 8,\n            \"name\": \"Shopify Storefront\"\n          },\n          {\n            \"sales_channel_id\": 10,\n            \"name\": \"Walmart Marketplace\"\n          }\n        ]\n      }\n    ],\n    \"slow_movers\": [\n      {\n        \"product_id\": 195,\n        \"fba_units\": 100,\n        \"fba_value\": 240,\n        \"max_days_old\": 25,\n        \"sold_90d\": 4,\n        \"days_of_cover\": 2250,\n        \"sku\": \"SF-KIT-CLN\",\n        \"name\": \"Summit Flask Bottle Cleaning Kit\",\n        \"action_status\": null,\n        \"action_type\": null\n      }\n    ],\n    \"workbench\": {\n      \"path\": \"/v2/reports/inventory-aging\",\n      \"query\": {\n        \"search\": \"Summit Flask\"\n      }\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Get ASIN Listing Quality History","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/amazon/asins/{asin}/listing-quality/history?integration_instance_id=4","host":["{{baseUrl}}"],"path":["api","v2","amazon","asins",":asin","listing-quality","history"],"query":[{"key":"integration_instance_id","value":"4","description":"Amazon integration instance ID","disabled":true}],"variable":[{"key":"asin","value":"B0D7SF3201","description":"Amazon ASIN"}]},"description":"Every listing-quality audit of one ASIN on an Amazon integration, newest first.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.index).\n\nQuery:\n- integration_instance_id (required)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/amazon/asins/{asin}/listing-quality/history?integration_instance_id=4","host":["{{baseUrl}}"],"path":["api","v2","amazon","asins",":asin","listing-quality","history"],"query":[{"key":"integration_instance_id","value":"4","description":"Amazon integration instance ID","disabled":true}],"variable":[{"key":"asin","value":"B0D7SF3201","description":"Amazon ASIN"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 76,\n      \"integration_instance_id\": 4,\n      \"asin\": \"B0D7SF3201\",\n      \"profile_id\": 1,\n      \"audited_on\": \"2026-10-01\",\n      \"score\": 88,\n      \"checks\": {\n        \"image_count\": {\n          \"result\": \"pass\",\n          \"weight\": 15,\n          \"credit\": 1,\n          \"label\": \"Image count\",\n          \"note\": null,\n          \"evidence\": {\n            \"count\": 9,\n            \"variants\": [\n              \"MAIN\"\n            ]\n          }\n        },\n        \"main_image_white_bg\": {\n          \"result\": \"pass\",\n          \"weight\": 10,\n          \"credit\": 1,\n          \"label\": \"Main image on white\",\n          \"note\": null,\n          \"evidence\": {\n            \"image_url\": \"https://images.docs.example/brand/B0D7SF3201-MAIN.png\",\n            \"white_ratio\": 1,\n            \"samples\": 464,\n            \"threshold\": 0.98\n          }\n        },\n        \"mobile_optimized_images\": {\n          \"result\": \"unknown\",\n          \"weight\": 15,\n          \"credit\": 0,\n          \"label\": \"Mobile-readable images\",\n          \"note\": \"The AI provider did not answer for any image.\",\n          \"evidence\": {\n            \"images\": [\n              {\n                \"variant\": \"PT01\",\n                \"image_url\": \"https://images.docs.example/brand/B0D7SF3201-PT01.png\",\n                \"has_readable_overlay\": null,\n                \"confidence\": null,\n                \"overlay_text\": null\n              }\n            ],\n            \"model\": \"claude-sonnet-4-5\",\n            \"provider\": \"anthropic\",\n            \"prompt_hash\": \"d9021971ba4b2874\",\n            \"confidence_threshold\": 0.7\n          }\n        },\n        \"bullet_count\": {\n          \"result\": \"pass\",\n          \"weight\": 10,\n          \"credit\": 1,\n          \"label\": \"Five bullets\",\n          \"note\": null,\n          \"evidence\": {\n            \"count\": 5\n          }\n        },\n        \"bullet_lead_in\": {\n          \"result\": \"pass\",\n          \"weight\": 10,\n          \"credit\": 1,\n          \"label\": \"Bullet lead-ins\",\n          \"note\": null,\n          \"evidence\": {\n            \"matching\": 5,\n            \"bullets\": 5\n          }\n        },\n        \"aplus_content\": {\n          \"result\": \"pass\",\n          \"weight\": 15,\n          \"credit\": 1,\n          \"label\": \"A+ content\",\n          \"note\": null,\n          \"evidence\": {\n            \"confirmed_at\": \"2026-10-03\",\n            \"source\": \"manual\"\n          }\n        },\n        \"description_formatted\": {\n          \"result\": \"pass\",\n          \"weight\": 10,\n          \"credit\": 1,\n          \"label\": \"Formatted description\",\n          \"note\": null,\n          \"evidence\": {\n            \"length\": 242,\n            \"line_breaks\": 6,\n            \"dash_bullets\": 3\n          }\n        },\n        \"brand_store\": {\n          \"result\": \"pass\",\n          \"weight\": 5,\n          \"credit\": 1,\n          \"label\": \"Brand Store page\",\n          \"note\": null,\n          \"evidence\": {\n            \"url\": \"https://www.amazon.com/stores/SummitFlask/page/7B2F1E40\",\n            \"confirmed_at\": \"2026-08-12\",\n            \"source\": \"manual\"\n          }\n        },\n        \"title_length\": {\n          \"result\": \"fail\",\n          \"weight\": 10,\n          \"credit\": 0,\n          \"label\": \"Title length\",\n          \"note\": \"Title is 45 characters.\",\n          \"evidence\": {\n            \"length\": 45\n          }\n        }\n      },\n      \"action_items\": [\n        {\n          \"check\": \"title_length\",\n          \"label\": \"Title length\",\n          \"text\": \"Write a title of 80–200 characters.\",\n          \"result\": \"fail\"\n        }\n      ],\n      \"catalog_synced_at\": \"2026-10-03T18:35:44+00:00\",\n      \"ai_provider\": \"anthropic\",\n      \"ai_model\": \"claude-sonnet-4-5\"\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Get Keepa Connection","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brand-management/market-data/keepa","host":["{{baseUrl}}"],"path":["api","v2","brand-management","market-data","keepa"]},"description":"Keepa connection status: connected, tokens left, refill rate, last check and last error. The API key is never returned.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.index)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brand-management/market-data/keepa","host":["{{baseUrl}}"],"path":["api","v2","brand-management","market-data","keepa"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"provider\": \"keepa\",\n    \"connected\": false,\n    \"connected_at\": null,\n    \"tokens_left\": null,\n    \"refill_rate\": null,\n    \"checked_at\": null,\n    \"last_error\": null,\n    \"last_error_at\": null,\n    \"history_pulled_asin_count\": 0,\n    \"queued_tracked_job_log_ids\": []\n  }\n}"}]},{"name":"Connect Keepa","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/brand-management/market-data/keepa","host":["{{baseUrl}}"],"path":["api","v2","brand-management","market-data","keepa"]},"description":"Validates the Keepa API key against Keepa and stores it encrypted. Replaces any existing key. Starts the one-off history pull for channels with Competition monitoring on.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage).\n\nBody:\n- api_key (required, 20–128 alphanumeric characters)","body":{"mode":"raw","raw":"{\n  \"api_key\": \"abcd1234efgh5678ijkl9012mnop3456qrst7890uvwx1234yzab5678cdef9012\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/brand-management/market-data/keepa","host":["{{baseUrl}}"],"path":["api","v2","brand-management","market-data","keepa"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"provider\": \"keepa\",\n    \"connected\": true,\n    \"connected_at\": \"2026-10-03T19:30:00+00:00\",\n    \"tokens_left\": 1180,\n    \"refill_rate\": 20,\n    \"checked_at\": \"2026-10-03T19:30:00+00:00\",\n    \"last_error\": null,\n    \"last_error_at\": null,\n    \"history_pulled_asin_count\": 0,\n    \"queued_tracked_job_log_ids\": []\n  },\n  \"message\": \"Keepa connected.\"\n}"}]},{"name":"Disconnect Keepa","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brand-management/market-data/keepa","host":["{{baseUrl}}"],"path":["api","v2","brand-management","market-data","keepa"]},"description":"Removes the stored Keepa key. Market estimates continue from your own Sales & Traffic data; low-confidence ASINs show insufficient signal.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage)."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/brand-management/market-data/keepa","host":["{{baseUrl}}"],"path":["api","v2","brand-management","market-data","keepa"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"provider\": \"keepa\",\n    \"connected\": false,\n    \"connected_at\": null,\n    \"tokens_left\": null,\n    \"refill_rate\": null,\n    \"checked_at\": null,\n    \"last_error\": null,\n    \"last_error_at\": null,\n    \"history_pulled_asin_count\": 0,\n    \"queued_tracked_job_log_ids\": []\n  },\n  \"message\": \"Keepa disconnected.\"\n}"}]},{"name":"Test Keepa Connection","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brand-management/market-data/keepa/test","host":["{{baseUrl}}"],"path":["api","v2","brand-management","market-data","keepa","test"]},"description":"Checks the stored key with Keepa and refreshes tokens left.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope: reports, permission: brand_reports.manage)."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/brand-management/market-data/keepa/test","host":["{{baseUrl}}"],"path":["api","v2","brand-management","market-data","keepa","test"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"provider\": \"keepa\",\n    \"connected\": true,\n    \"connected_at\": \"2026-10-03T19:30:00+00:00\",\n    \"tokens_left\": 1180,\n    \"refill_rate\": 20,\n    \"checked_at\": \"2026-10-03T19:30:00+00:00\",\n    \"last_error\": null,\n    \"last_error_at\": null,\n    \"history_pulled_asin_count\": 0,\n    \"queued_tracked_job_log_ids\": []\n  },\n  \"message\": \"Keepa answered.\"\n}"}]}]},{"name":"Product Listings (V2)","item":[{"name":"End Listing on Channel","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/product-listings/{productListing}/end","host":["{{baseUrl}}"],"path":["api","v2","product-listings",":productListing","end"],"variable":[{"key":"productListing","value":"1","description":"The ProductListing ID to end on its sales channel"}]},"description":"End a live listing on its sales channel (listing publishing).\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nPer-channel semantics:\n- Amazon: deletes the SKU's listings item (SP-API deleteListingsItem)\n- BigCommerce: hides the product (is_visible=false, reversible)\n- eBay: ends the item (Trading EndFixedPriceItem, EndingReason=NotAvailable)\n- Walmart: retires the item (DELETE /v3/items/{sku}, async up to 48h)\n- Shopify: archives the product (productUpdate status ARCHIVED, reversible)\n- WooCommerce: trashes the product (DELETE without force, reversible)\n\nOn success the local ProductListing's remote_status is stamped with the channel-side state the listing was left in — `missing` for hard deletes/retires (Amazon, eBay, Walmart) or `archived` for native unpublish (Shopify, BigCommerce, WooCommerce) — so the Publish matrix demotes the cell and offers a republish.\n\nAuthentication: Requires Bearer token.\n\nIdempotent: ending a listing that was already removed on the channel still succeeds (remote_status becomes `missing`)."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/product-listings/{productListing}/end","host":["{{baseUrl}}"],"path":["api","v2","product-listings",":productListing","end"],"variable":[{"key":"productListing","value":"1","description":"The ProductListing ID to end on its sales channel"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 55,\n    \"remote_status\": \"archived\",\n    \"remote_status_checked_at\": \"2026-07-03T08:15:42.000000Z\"\n  },\n  \"message\": \"Listing ended on the channel.\"\n}"}]}]},{"name":"PDF Templates","item":[{"name":"List PDF Templates","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/pdf-templates","host":["{{baseUrl}}"],"path":["api","pdf-templates"]},"description":"Returns every PDF template — the global templates plus every store-specific override.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (read access).\n\nEach row carries only what a list needs: `name`, `type`, `description`, and the owning store (`store_id` and `store_name`, both null on a global template). The template markup and its variable schema are deliberately not included — fetch a single template to get those. Template types: purchase_order, sales_order, packing, quote, pick_list, vendor_credit, sales_credit. There is no server-side type filter — request the full list and filter by `type` on the client."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/pdf-templates","host":["{{baseUrl}}"],"path":["api","pdf-templates"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Purchase Order\",\n      \"type\": \"purchase_order\",\n      \"description\": \"Default purchase order layout.\",\n      \"store_id\": null,\n      \"store_name\": null,\n      \"updated_at\": \"2026-07-19T11:05:00+00:00\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Sales Order\",\n      \"type\": \"sales_order\",\n      \"description\": \"Default sales order layout.\",\n      \"store_id\": null,\n      \"store_name\": null,\n      \"updated_at\": \"2026-07-19T11:05:00+00:00\"\n    },\n    {\n      \"id\": 3,\n      \"name\": \"Packing Slip\",\n      \"type\": \"packing\",\n      \"description\": \"Default packing slip layout.\",\n      \"store_id\": null,\n      \"store_name\": null,\n      \"updated_at\": \"2026-07-20T14:32:10+00:00\"\n    },\n    {\n      \"id\": 42,\n      \"name\": \"Packing Slip\",\n      \"type\": \"packing\",\n      \"description\": \"Default packing slip layout.\",\n      \"store_id\": 7,\n      \"store_name\": \"Acme US\",\n      \"updated_at\": \"2026-07-21T09:14:55+00:00\"\n    }\n  ]\n}"}]},{"name":"Get PDF Template","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Returns a single PDF template by ID, including its raw `content`, resolved `variables`, `variables_tree`/`schema`, and `preview_destinations`.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (read access).\n\n`preview_destinations` lists the destination countries a preview can be rendered against, each as a `code` and a `label`. Pass one of those codes as `destination_country` when previewing to see how country-conditional sections render."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Purchase Order\",\n    \"content\": \"<html><body><h1>Purchase Order {{order_number}}</h1></body></html>\",\n    \"type\": \"purchase_order\",\n    \"description\": \"Default purchase order layout.\",\n    \"variables\": [\n      {\n        \"name\": \"Order Number\",\n        \"variable\": \"order_number\",\n        \"mockValue\": \"PO-2044\"\n      },\n      {\n        \"name\": \"Vendor Name\",\n        \"variable\": \"vendor_name\",\n        \"mockValue\": \"Acme Supply Co.\"\n      }\n    ],\n    \"variables_tree\": {\n      \"order\": {\n        \"label\": \"Order\",\n        \"children\": {\n          \"number\": {\n            \"label\": \"Order Number\",\n            \"type\": \"string\",\n            \"mock\": \"PO-2044\"\n          }\n        }\n      }\n    },\n    \"schema\": {\n      \"order\": {\n        \"label\": \"Order\",\n        \"children\": {\n          \"number\": {\n            \"label\": \"Order Number\",\n            \"type\": \"string\",\n            \"mock\": \"PO-2044\"\n          }\n        }\n      }\n    },\n    \"store_id\": null,\n    \"store_name\": null,\n    \"reset_target\": \"default\",\n    \"updated_at\": \"2026-07-20T14:32:10+00:00\",\n    \"preview_destinations\": [\n      {\n        \"code\": \"US\",\n        \"label\": \"United States\"\n      },\n      {\n        \"code\": \"CA\",\n        \"label\": \"Canada\"\n      },\n      {\n        \"code\": \"GB\",\n        \"label\": \"United Kingdom\"\n      },\n      {\n        \"code\": \"AU\",\n        \"label\": \"Australia\"\n      },\n      {\n        \"code\": \"NZ\",\n        \"label\": \"New Zealand\"\n      },\n      {\n        \"code\": \"DE\",\n        \"label\": \"Germany\"\n      }\n    ]\n  }\n}"}]},{"name":"Update PDF Template","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Updates the `content` of a PDF template and snapshots the previous content as a new version.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (write access).\n\nFields:\n- content (required): the template markup string.\n- updated_at (optional): the template's last-known `updated_at` timestamp. When supplied it enables optimistic-lock checking, so an edit made by someone else since that timestamp is rejected with a 409 instead of being silently overwritten. Re-send without `updated_at` to overwrite deliberately.\n\n`preview_destinations` lists the destination countries a preview can be rendered against, each as a `code` and a `label`. Pass one of those codes as `destination_country` when previewing to see how country-conditional sections render.","body":{"mode":"raw","raw":"{\n  \"content\": \"<html><body><h1>Purchase Order {{order_number}}</h1><p>Updated layout</p></body></html>\",\n  \"updated_at\": \"2026-07-20T14:32:10+00:00\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Purchase Order\",\n    \"content\": \"<html><body><h1>Purchase Order {{order_number}}</h1><p>Updated layout</p></body></html>\",\n    \"type\": \"purchase_order\",\n    \"description\": \"Default purchase order layout.\",\n    \"variables\": [\n      {\n        \"name\": \"Order Number\",\n        \"variable\": \"order_number\",\n        \"mockValue\": \"PO-2044\"\n      },\n      {\n        \"name\": \"Vendor Name\",\n        \"variable\": \"vendor_name\",\n        \"mockValue\": \"Acme Supply Co.\"\n      }\n    ],\n    \"variables_tree\": {\n      \"order\": {\n        \"label\": \"Order\",\n        \"children\": {\n          \"number\": {\n            \"label\": \"Order Number\",\n            \"type\": \"string\",\n            \"mock\": \"PO-2044\"\n          }\n        }\n      }\n    },\n    \"schema\": {\n      \"order\": {\n        \"label\": \"Order\",\n        \"children\": {\n          \"number\": {\n            \"label\": \"Order Number\",\n            \"type\": \"string\",\n            \"mock\": \"PO-2044\"\n          }\n        }\n      }\n    },\n    \"store_id\": null,\n    \"store_name\": null,\n    \"reset_target\": \"default\",\n    \"updated_at\": \"2026-07-21T09:15:00+00:00\",\n    \"preview_destinations\": [\n      {\n        \"code\": \"US\",\n        \"label\": \"United States\"\n      },\n      {\n        \"code\": \"CA\",\n        \"label\": \"Canada\"\n      },\n      {\n        \"code\": \"GB\",\n        \"label\": \"United Kingdom\"\n      },\n      {\n        \"code\": \"AU\",\n        \"label\": \"Australia\"\n      },\n      {\n        \"code\": \"NZ\",\n        \"label\": \"New Zealand\"\n      },\n      {\n        \"code\": \"DE\",\n        \"label\": \"Germany\"\n      }\n    ]\n  }\n}"}]},{"name":"Reset PDF Template to Default","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/reset","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","reset"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Resets a PDF template back to its default content and records the reset as a new version so the prior content can still be restored.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nWhat \"default\" means depends on the template: a store's own copy resets to the account's default template for that type, so a global customisation is not thrown away for one store; the default template itself resets to the built-in content. The reset_target field on the template says which one applies before the reset is run — `global` or `default`.\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (write access)."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/reset","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","reset"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Purchase Order\",\n    \"content\": \"<html><body><!-- default purchase order layout --></body></html>\",\n    \"type\": \"purchase_order\",\n    \"description\": \"Default purchase order layout.\",\n    \"variables\": [\n      {\n        \"name\": \"Order Number\",\n        \"variable\": \"order_number\",\n        \"mockValue\": \"PO-2044\"\n      },\n      {\n        \"name\": \"Vendor Name\",\n        \"variable\": \"vendor_name\",\n        \"mockValue\": \"Acme Supply Co.\"\n      }\n    ],\n    \"variables_tree\": {\n      \"order\": {\n        \"label\": \"Order\",\n        \"children\": {\n          \"number\": {\n            \"label\": \"Order Number\",\n            \"type\": \"string\",\n            \"mock\": \"PO-2044\"\n          }\n        }\n      }\n    },\n    \"schema\": {\n      \"order\": {\n        \"label\": \"Order\",\n        \"children\": {\n          \"number\": {\n            \"label\": \"Order Number\",\n            \"type\": \"string\",\n            \"mock\": \"PO-2044\"\n          }\n        }\n      }\n    },\n    \"store_id\": null,\n    \"store_name\": null,\n    \"reset_target\": \"default\",\n    \"updated_at\": \"2026-07-20T14:32:10+00:00\"\n  }\n}"}]},{"name":"Preview PDF Template","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/preview","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","preview"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Renders a preview of draft template content without saving it.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (read access).\n\nFields:\n- content (required): the draft markup to render.\n- format (optional): `pdf` (default) returns a base64-encoded PDF in `data.pdf`; `html` returns rendered markup in `data.html`.\n- sample_record_id (optional): render against one of your own records instead of sample data. The template's type decides what kind of record that is — a sales order, a quote, a purchase order, a pick list or a credit. Use the sample-records endpoint to list the eligible records for a template.\n- destination_country (optional): render the sample data against a destination country so country-conditional sections can be seen. Accepts one of the codes in the template's `preview_destinations` (US, CA, GB, AU, NZ, DE). Ignored when `sample_record_id` is supplied — a real record carries its own address.\n\nInvalid template markup returns a 422 with the offending line rather than a server error.\n\nResponse:\n- data.pdf: the base64-encoded document (pdf format only).\n- data.html: the rendered markup (html format only).\n- data.rendered_with: which engine produced this preview, so the preview can be trusted to match the printed document.\n- data.warnings: page-setup problems found while rendering — a dropped footer banner, artwork too tall for the page. Empty when nothing was wrong, and always present. The html format does not run page setup, so it returns no warnings.","body":{"mode":"raw","raw":"{\n  \"content\": \"<html><body><h1>Packing Slip {{order_number}}</h1></body></html>\",\n  \"format\": \"pdf\",\n  \"sample_record_id\": 4821,\n  \"destination_country\": \"NZ\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/preview","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","preview"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"pdf\": \"JVBERi0xLjQKJeLjz9MKMyAwIG9iago8PC9UeXBlL1BhZ2UvUGFyZW50IDEgMCBSL1Jlc291cmNlcyAyIDAgUj4+CmVuZG9iagolJUVPRg==\",\n    \"type\": \"application/pdf\",\n    \"rendered_with\": \"wkhtmltopdf\",\n    \"warnings\": []\n  }\n}"}]},{"name":"List Sample Records for Preview","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/sample-records","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","sample-records"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Lists the records a template can be previewed against, so a preview can be rendered from real data instead of sample data.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (read access).\n\nThe template's type decides which kind of record is searched — a sales order for sales-order and packing templates, a quote, a purchase order, a pick list, a sales credit or a vendor credit. A record returned here can never be one the template cannot render.\n\nPath params:\n- id: the ID of the template the records will be previewed against.\n\nReturns up to 20 records, newest first. Pass the chosen `id` as `sample_record_id` when previewing."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/sample-records","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","sample-records"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 4821,\n      \"reference\": \"SO-1042\"\n    },\n    {\n      \"id\": 4816,\n      \"reference\": \"SO-1039\"\n    },\n    {\n      \"id\": 4802,\n      \"reference\": \"SO-1031\"\n    }\n  ]\n}"}]},{"name":"Validate PDF Template","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/validate","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","validate"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Validates draft template content against the template type's schema without rendering it. Use this to surface unknown variables, unbalanced sections, and non-blocking warnings before saving.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (read access).\n\nFields:\n- content (required): the draft markup to validate.\n\nThe response `data` contains `ok` (true when there are no errors), an `errors` array, and a `warnings` array.","body":{"mode":"raw","raw":"{\n  \"content\": \"<html><body><h1>{{order_number}}</h1>{{#items}}<p>{{sku}}</p>{{/items}}</body></html>\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/validate","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","validate"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"ok\": true,\n    \"errors\": [],\n    \"warnings\": [\n      \"Template has no line-items block ({{items}} or {{#items}}…{{/items}}). The PDF will render without itemized rows.\"\n    ]\n  }\n}"}]},{"name":"List PDF Template Versions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/versions","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","versions"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Returns the change history for a template, newest first, as a paginated list. Each version captures the content at that point plus who changed it and why.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (read access).\n\nThis endpoint returns a standard paginator envelope (`data`, `current_page`, `last_page`, `per_page`, `total`, …) rather than the `{ data: ... }` wrapper used by the other endpoints.\n\nQuery params:\n- page (optional): page number (default 1).\n- per_page (optional): items per page (default 10).\n\nVersion `source` is one of: manual, ai, reset, migration, import."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/versions","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","versions"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 58,\n      \"pdf_template_id\": 1,\n      \"content\": \"<html><body><h1>Purchase Order {{order_number}}</h1></body></html>\",\n      \"source\": \"manual\",\n      \"source_label\": \"Manual\",\n      \"summary\": \"Updated footer address block.\",\n      \"ai_message_id\": null,\n      \"created_by\": 7,\n      \"created_by_name\": \"Jane Smith\",\n      \"created_at\": \"2026-07-20T14:32:10+00:00\"\n    },\n    {\n      \"id\": 57,\n      \"pdf_template_id\": 1,\n      \"content\": \"<html><body><h1>Purchase Order {{order_number}}</h1></body></html>\",\n      \"source\": \"ai\",\n      \"source_label\": \"AI\",\n      \"summary\": \"Applied AI suggestion to restyle the header.\",\n      \"ai_message_id\": null,\n      \"created_by\": 7,\n      \"created_by_name\": \"Jane Smith\",\n      \"created_at\": \"2026-07-20T14:32:10+00:00\"\n    },\n    {\n      \"id\": 56,\n      \"pdf_template_id\": 1,\n      \"content\": \"<html><body><h1>Purchase Order {{order_number}}</h1></body></html>\",\n      \"source\": \"reset\",\n      \"source_label\": \"Reset\",\n      \"summary\": \"Reset to default layout.\",\n      \"ai_message_id\": null,\n      \"created_by\": 7,\n      \"created_by_name\": \"Ops Admin\",\n      \"created_at\": \"2026-07-20T14:32:10+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/pdf-templates/1/versions?page=1\",\n  \"from\": 1,\n  \"last_page\": 4,\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/pdf-templates/1/versions?page=4\",\n  \"next_page_url\": \"{{protocol}}{{domain}}/api/pdf-templates/1/versions?page=2\",\n  \"path\": \"{{protocol}}{{domain}}/api/pdf-templates/1/versions\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 3,\n  \"total\": 34\n}"}]},{"name":"Get PDF Template Version","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/versions/{versionId}","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","versions",":versionId"],"variable":[{"key":"id","value":"","description":"The record ID."},{"key":"versionId","value":"","description":"The version ID."}]},"description":"Returns a single template version by ID, including its full content snapshot.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (read access)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/versions/{versionId}","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","versions",":versionId"],"variable":[{"key":"id","value":"","description":"The record ID."},{"key":"versionId","value":"","description":"The version ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 58,\n    \"pdf_template_id\": 1,\n    \"content\": \"<html><body><h1>Purchase Order {{order_number}}</h1></body></html>\",\n    \"source\": \"manual\",\n    \"source_label\": \"Manual\",\n    \"summary\": \"Updated footer address block.\",\n    \"ai_message_id\": null,\n    \"created_by\": 7,\n    \"created_by_name\": \"Jane Smith\",\n    \"created_at\": \"2026-07-20T14:32:10+00:00\"\n  }\n}"}]},{"name":"Restore PDF Template Version","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/versions/{versionId}/restore","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","versions",":versionId","restore"],"variable":[{"key":"id","value":"","description":"The record ID."},{"key":"versionId","value":"","description":"The version ID."}]},"description":"Restores a prior template version, replacing the template's current content with that version's snapshot and recording the previous content as a new version.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (write access)."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/versions/{versionId}/restore","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","versions",":versionId","restore"],"variable":[{"key":"id","value":"","description":"The record ID."},{"key":"versionId","value":"","description":"The version ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Purchase Order\",\n    \"content\": \"<html><body><h1>Purchase Order {{order_number}}</h1></body></html>\",\n    \"type\": \"purchase_order\",\n    \"description\": \"Default purchase order layout.\",\n    \"variables\": [\n      {\n        \"name\": \"Order Number\",\n        \"variable\": \"order_number\",\n        \"mockValue\": \"PO-2044\"\n      },\n      {\n        \"name\": \"Vendor Name\",\n        \"variable\": \"vendor_name\",\n        \"mockValue\": \"Acme Supply Co.\"\n      }\n    ],\n    \"variables_tree\": {\n      \"order\": {\n        \"label\": \"Order\",\n        \"children\": {\n          \"number\": {\n            \"label\": \"Order Number\",\n            \"type\": \"string\",\n            \"mock\": \"PO-2044\"\n          }\n        }\n      }\n    },\n    \"schema\": {\n      \"order\": {\n        \"label\": \"Order\",\n        \"children\": {\n          \"number\": {\n            \"label\": \"Order Number\",\n            \"type\": \"string\",\n            \"mock\": \"PO-2044\"\n          }\n        }\n      }\n    },\n    \"store_id\": null,\n    \"store_name\": null,\n    \"reset_target\": \"default\",\n    \"updated_at\": \"2026-07-21T09:20:00+00:00\"\n  }\n}"}]},{"name":"List Store Overrides","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/pdf-templates/{type}/store-overrides","host":["{{baseUrl}}"],"path":["api","pdf-templates",":type","store-overrides"],"variable":[{"key":"type","value":"","description":"The type ID."}]},"description":"Returns every store-specific override for a given template type. A store override is a copy of the global template that a single store can customize independently.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (read access).\n\nPath params:\n- type: template type — one of purchase_order, sales_order, packing, quote, pick_list, vendor_credit, sales_credit.\n\nRows carry the same slim shape as the template list — the markup and variable schema are not included; fetch a single template to get those."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/pdf-templates/{type}/store-overrides","host":["{{baseUrl}}"],"path":["api","pdf-templates",":type","store-overrides"],"variable":[{"key":"type","value":"","description":"The type ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 42,\n      \"name\": \"Packing Slip\",\n      \"type\": \"packing\",\n      \"description\": \"Default packing slip layout.\",\n      \"store_id\": 7,\n      \"store_name\": \"Acme US\",\n      \"updated_at\": \"2026-07-21T09:14:55+00:00\"\n    },\n    {\n      \"id\": 57,\n      \"name\": \"Packing Slip\",\n      \"type\": \"packing\",\n      \"description\": \"Default packing slip layout.\",\n      \"store_id\": 9,\n      \"store_name\": \"Acme EU\",\n      \"updated_at\": \"2026-07-22T08:01:44+00:00\"\n    }\n  ]\n}"}]},{"name":"Create Store Override","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/store-override","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","store-override"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Creates a store-specific override from a global template. The override starts with the global template's content and can then be edited independently for that store.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (write access).\n\nFields:\n- store_id (required): the store the override is created for.\n\nA store can hold only one template per type, and a store copy is always made from the default template — not from another store's copy. Both are refused with a 422 rather than created.","body":{"mode":"raw","raw":"{\n  \"store_id\": 3\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/store-override","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","store-override"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"name\": \"Packing Slip\",\n    \"content\": \"<html><body><h1>Packing Slip {{order_number}}</h1>{{#items}}<p>{{sku}} x {{quantity}}</p>{{/items}}</body></html>\",\n    \"type\": \"packing\",\n    \"description\": \"Default packing slip layout.\",\n    \"variables\": [\n      {\n        \"name\": \"Order Number\",\n        \"variable\": \"order_number\",\n        \"mockValue\": \"SO-1001\"\n      },\n      {\n        \"name\": \"Warehouse\",\n        \"variable\": \"warehouse\",\n        \"mockValue\": \"Main Warehouse\"\n      },\n      {\n        \"name\": \"Items\",\n        \"variable\": \"items\",\n        \"mockValue\": \"\"\n      }\n    ],\n    \"variables_tree\": {\n      \"order\": {\n        \"label\": \"Order\",\n        \"children\": {\n          \"number\": {\n            \"label\": \"Order Number\",\n            \"type\": \"string\",\n            \"mock\": \"SO-1001\"\n          }\n        }\n      },\n      \"items\": {\n        \"label\": \"Items\",\n        \"type\": \"section\",\n        \"mock\": \"\"\n      },\n      \"asset\": {\n        \"label\": \"Custom Assets\",\n        \"children\": {\n          \"company_logo\": {\n            \"label\": \"Company Logo\",\n            \"type\": \"html\",\n            \"mock\": \"\"\n          }\n        }\n      }\n    },\n    \"schema\": {\n      \"order\": {\n        \"label\": \"Order\",\n        \"children\": {\n          \"number\": {\n            \"label\": \"Order Number\",\n            \"type\": \"string\",\n            \"mock\": \"SO-1001\"\n          }\n        }\n      },\n      \"items\": {\n        \"label\": \"Items\",\n        \"type\": \"section\",\n        \"mock\": \"\"\n      },\n      \"asset\": {\n        \"label\": \"Custom Assets\",\n        \"children\": {\n          \"company_logo\": {\n            \"label\": \"Company Logo\",\n            \"type\": \"html\",\n            \"mock\": \"\"\n          }\n        }\n      }\n    },\n    \"store_id\": 3,\n    \"store_name\": \"Acme Store\",\n    \"reset_target\": \"global\",\n    \"updated_at\": \"2026-07-20T14:32:10+00:00\"\n  }\n}"}]},{"name":"Delete Store Override","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/store-override","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","store-override"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Deletes a store-specific override. That store reverts to the global template of the same type.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (write access).\n\nPath params:\n- id: the ID of the store override template to delete.\n\nOnly a store's own copy can be deleted. Pointing this at the default template is refused with a 422 — every store prints from it. Deleting a store copy also removes that copy's version history, so there is nothing left to restore it from."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/pdf-templates/{id}/store-override","host":["{{baseUrl}}"],"path":["api","pdf-templates",":id","store-override"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"message\": \"Store override deleted successfully.\"\n  }\n}"}]},{"name":"List Template Assets","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/pdf-template-assets","host":["{{baseUrl}}"],"path":["api","pdf-template-assets"]},"description":"Returns every uploaded image asset available to templates. Reference an asset inside template content with its `token` (for example `{{{asset.company_logo}}}`), which renders as an inline image in the PDF.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (read access)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/pdf-template-assets","host":["{{baseUrl}}"],"path":["api","pdf-template-assets"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 11,\n      \"slug\": \"company_logo\",\n      \"name\": \"Company Logo\",\n      \"mime_type\": \"image/png\",\n      \"size_bytes\": 20481,\n      \"width\": 320,\n      \"height\": 120,\n      \"url\": \"https://assets.sku.io/pdf-template-assets/company_logo.png\",\n      \"token\": \"{{{asset.company_logo}}}\",\n      \"created_at\": \"2026-07-19T11:05:00+00:00\"\n    },\n    {\n      \"id\": 12,\n      \"slug\": \"signature\",\n      \"name\": \"Signature\",\n      \"mime_type\": \"image/png\",\n      \"size_bytes\": 20481,\n      \"width\": 320,\n      \"height\": 120,\n      \"url\": \"https://assets.sku.io/pdf-template-assets/signature.png\",\n      \"token\": \"{{{asset.signature}}}\",\n      \"created_at\": \"2026-07-19T11:05:00+00:00\"\n    }\n  ]\n}"}]},{"name":"Upload Template Asset","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/pdf-template-assets","host":["{{baseUrl}}"],"path":["api","pdf-template-assets"]},"description":"Uploads a new image asset for use in templates. Send the request as multipart/form-data.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (write access).\n\nFields:\n- image (required): the image file (JPG, PNG, GIF, or WEBP, max 5 MB).\n- name (optional): a display name; the reference slug is derived from it.\n\nThe response includes the generated `slug` and the `token` you place in template content.","body":{"mode":"formdata","formdata":[{"key":"image","type":"file","src":[]},{"key":"name","type":"text","value":""}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/pdf-template-assets","host":["{{baseUrl}}"],"path":["api","pdf-template-assets"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 13,\n    \"slug\": \"company_logo\",\n    \"name\": \"Company Logo\",\n    \"mime_type\": \"image/png\",\n    \"size_bytes\": 20481,\n    \"width\": 320,\n    \"height\": 120,\n    \"url\": \"https://assets.sku.io/pdf-template-assets/company_logo.png\",\n    \"token\": \"{{{asset.company_logo}}}\",\n    \"created_at\": \"2026-07-19T11:05:00+00:00\"\n  }\n}"}]},{"name":"Get Template Asset Usage","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/pdf-template-assets/{id}/usage","host":["{{baseUrl}}"],"path":["api","pdf-template-assets",":id","usage"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Reports which templates still reference an image asset's `{{{asset.<slug>}}}` token.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (read access).\n\nDeleting an asset does not remove its token from the templates that use it — those templates keep rendering, with nothing where the image was — and re-uploading the same file does not restore it, because a colliding slug is suffixed and the token therefore changes. Call this before a delete to name what would break.\n\nPath params:\n- id: the ID of the asset to check.\n\nReturns `in_use` plus the templates holding the token. `store` is null on a global template."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/pdf-template-assets/{id}/usage","host":["{{baseUrl}}"],"path":["api","pdf-template-assets",":id","usage"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"in_use\": true,\n    \"templates\": [\n      {\n        \"id\": 3,\n        \"name\": \"Packing Slip\",\n        \"type\": \"packing\",\n        \"store\": null\n      },\n      {\n        \"id\": 42,\n        \"name\": \"Packing Slip\",\n        \"type\": \"packing\",\n        \"store\": \"Acme US\"\n      }\n    ]\n  }\n}"}]},{"name":"Delete Template Asset","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/pdf-template-assets/{id}","host":["{{baseUrl}}"],"path":["api","pdf-template-assets",":id"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Deletes an uploaded image asset and its file.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Personal Access Token (sent as a Bearer token) with the `settings` scope (write access).\n\nThe delete is refused with a 409 while any template still holds the asset's `{{{asset.<slug>}}}` token, and the response names those templates — deleting anyway leaves a blank space in every future print of them, and re-uploading the same file does not undo it because the slug (and therefore the token) changes on collision. Repeat the request with `force=1` to delete regardless.\n\nPath params:\n- id: the ID of the asset to delete.\n\nQuery params:\n- force (optional): `1` deletes an asset that templates still reference."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/pdf-template-assets/{id}","host":["{{baseUrl}}"],"path":["api","pdf-template-assets",":id"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"message\": \"Asset deleted.\"\n  }\n}"}]}]},{"name":"Workflow Evaluations","item":[{"name":"List Workflow Evaluations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/automation/workflows/{workflow}/evaluations?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","automation","workflows",":workflow","evaluations"],"query":[{"key":"page","value":"1","description":"Page number (1-based).","disabled":true},{"key":"per_page","value":"10","description":"Page size. Defaults to 10.","disabled":true}],"variable":[{"key":"workflow","value":"5","description":"Workflow ID."}]},"description":"Returns a paginated list of evaluations for the given workflow, newest first. Each evaluation includes its 20 most recent runs (`runs[]`).\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Response:** Standard Laravel `LengthAwarePaginator` shape where `data[*]` are evaluation objects: `id`, `workflowId`, `name`, `description`, `testData` (array of test-case input objects), `assertions`, `testCaseCount`, `createdBy`, `createdAt`, `updatedAt`, `runs[]`.\n\n**Sorting:** `sort` accepts `created_at` or `name` (prefix with `-` for descending). Default `-created_at`.\n\n**Route name:** `automation.evaluations.index`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/automation/workflows/{workflow}/evaluations?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","automation","workflows",":workflow","evaluations"],"query":[{"key":"page","value":"1","description":"Page number (1-based).","disabled":true},{"key":"per_page","value":"10","description":"Page size. Defaults to 10.","disabled":true}],"variable":[{"key":"workflow","value":"5","description":"Workflow ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": \"12\",\n      \"workflowId\": \"5\",\n      \"name\": \"Order tagging regression suite\",\n      \"description\": \"Verifies the tag-orders workflow output for representative orders\",\n      \"testData\": [\n        {\n          \"order_id\": 1001,\n          \"total\": 149.99,\n          \"status\": \"paid\"\n        },\n        {\n          \"order_id\": 1002,\n          \"total\": 12.5,\n          \"status\": \"paid\"\n        }\n      ],\n      \"assertions\": [\n        {\n          \"path\": \"output.tags\",\n          \"operator\": \"contains\",\n          \"value\": \"high-value\"\n        }\n      ],\n      \"testCaseCount\": 2,\n      \"createdBy\": \"3\",\n      \"createdAt\": \"2026-06-10T14:30:00+00:00\",\n      \"updatedAt\": \"2026-06-11T08:45:00+00:00\",\n      \"runs\": [\n        {\n          \"id\": \"43\",\n          \"evaluationId\": \"12\",\n          \"status\": \"completed\",\n          \"totalCases\": 2,\n          \"passedCases\": 2,\n          \"failedCases\": 0,\n          \"results\": [\n            {\n              \"case\": 0,\n              \"passed\": true,\n              \"output\": {\n                \"tags\": [\n                  \"high-value\"\n                ]\n              }\n            },\n            {\n              \"case\": 1,\n              \"passed\": true,\n              \"output\": {\n                \"tags\": []\n              }\n            }\n          ],\n          \"startedAt\": \"2026-06-11T09:00:01+00:00\",\n          \"finishedAt\": \"2026-06-11T09:00:04+00:00\",\n          \"duration\": 3120,\n          \"createdAt\": \"2026-06-11T09:00:00+00:00\"\n        }\n      ]\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"https://sku2.test/api/automation/workflows/5/evaluations?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://sku2.test/api/automation/workflows/5/evaluations?page=1\",\n  \"links\": [\n    {\n      \"url\": null,\n      \"label\": \"&laquo; Previous\",\n      \"active\": false\n    },\n    {\n      \"url\": \"https://sku2.test/api/automation/workflows/5/evaluations?page=1\",\n      \"label\": \"1\",\n      \"active\": true\n    },\n    {\n      \"url\": null,\n      \"label\": \"Next &raquo;\",\n      \"active\": false\n    }\n  ],\n  \"next_page_url\": null,\n  \"path\": \"https://sku2.test/api/automation/workflows/5/evaluations\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"Create Workflow Evaluation","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/automation/workflows/{workflow}/evaluations","host":["{{baseUrl}}"],"path":["api","automation","workflows",":workflow","evaluations"],"variable":[{"key":"workflow","value":"5","description":"Workflow ID."}]},"description":"Creates a new evaluation for the workflow. Returns the created evaluation with HTTP 201 (unwrapped — the resource fields are at the response root).\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Body fields:**\n- `name` (string, required, max 255)\n- `description` (string, nullable)\n- `test_data` (array, required, min 1 item) — each entry is an object representing one test case's trigger/input payload\n- `test_data.*` (array/object, required)\n- `assertions` (array, nullable) — optional assertions evaluated against each test case's output\n\n**Route name:** `automation.evaluations.store`","body":{"mode":"raw","raw":"{\n  \"name\": \"Order tagging regression suite\",\n  \"description\": \"Verifies the tag-orders workflow output for representative orders\",\n  \"test_data\": [\n    {\n      \"order_id\": 1001,\n      \"total\": 149.99,\n      \"status\": \"paid\"\n    },\n    {\n      \"order_id\": 1002,\n      \"total\": 12.5,\n      \"status\": \"paid\"\n    }\n  ],\n  \"assertions\": [\n    {\n      \"path\": \"output.tags\",\n      \"operator\": \"contains\",\n      \"value\": \"high-value\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/automation/workflows/{workflow}/evaluations","host":["{{baseUrl}}"],"path":["api","automation","workflows",":workflow","evaluations"],"variable":[{"key":"workflow","value":"5","description":"Workflow ID."}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": \"12\",\n  \"workflowId\": \"5\",\n  \"name\": \"Order tagging regression suite\",\n  \"description\": \"Verifies the tag-orders workflow output for representative orders\",\n  \"testData\": [\n    {\n      \"order_id\": 1001,\n      \"total\": 149.99,\n      \"status\": \"paid\"\n    },\n    {\n      \"order_id\": 1002,\n      \"total\": 12.5,\n      \"status\": \"paid\"\n    }\n  ],\n  \"assertions\": [\n    {\n      \"path\": \"output.tags\",\n      \"operator\": \"contains\",\n      \"value\": \"high-value\"\n    }\n  ],\n  \"testCaseCount\": 2,\n  \"createdBy\": \"3\",\n  \"createdAt\": \"2026-06-10T14:30:00+00:00\",\n  \"updatedAt\": \"2026-06-11T08:45:00+00:00\"\n}"}]},{"name":"Show Workflow Evaluation","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/automation/workflow-evaluations/{evaluation}","host":["{{baseUrl}}"],"path":["api","automation","workflow-evaluations",":evaluation"],"variable":[{"key":"evaluation","value":"12","description":"Workflow evaluation ID."}]},"description":"Returns a single evaluation with ALL of its runs loaded (`runs[]`).\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Response:** The evaluation wrapped in `data`.\n\n**Route name:** `automation.evaluations.show`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/automation/workflow-evaluations/{evaluation}","host":["{{baseUrl}}"],"path":["api","automation","workflow-evaluations",":evaluation"],"variable":[{"key":"evaluation","value":"12","description":"Workflow evaluation ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": \"12\",\n    \"workflowId\": \"5\",\n    \"name\": \"Order tagging regression suite\",\n    \"description\": \"Verifies the tag-orders workflow output for representative orders\",\n    \"testData\": [\n      {\n        \"order_id\": 1001,\n        \"total\": 149.99,\n        \"status\": \"paid\"\n      },\n      {\n        \"order_id\": 1002,\n        \"total\": 12.5,\n        \"status\": \"paid\"\n      }\n    ],\n    \"assertions\": [\n      {\n        \"path\": \"output.tags\",\n        \"operator\": \"contains\",\n        \"value\": \"high-value\"\n      }\n    ],\n    \"testCaseCount\": 2,\n    \"createdBy\": \"3\",\n    \"createdAt\": \"2026-06-10T14:30:00+00:00\",\n    \"updatedAt\": \"2026-06-11T08:45:00+00:00\",\n    \"runs\": [\n      {\n        \"id\": \"43\",\n        \"evaluationId\": \"12\",\n        \"status\": \"completed\",\n        \"totalCases\": 2,\n        \"passedCases\": 2,\n        \"failedCases\": 0,\n        \"results\": [\n          {\n            \"case\": 0,\n            \"passed\": true,\n            \"output\": {\n              \"tags\": [\n                \"high-value\"\n              ]\n            }\n          },\n          {\n            \"case\": 1,\n            \"passed\": true,\n            \"output\": {\n              \"tags\": []\n            }\n          }\n        ],\n        \"startedAt\": \"2026-06-11T09:00:01+00:00\",\n        \"finishedAt\": \"2026-06-11T09:00:04+00:00\",\n        \"duration\": 3120,\n        \"createdAt\": \"2026-06-11T09:00:00+00:00\"\n      }\n    ]\n  }\n}"}]},{"name":"Update Workflow Evaluation","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/automation/workflow-evaluations/{evaluation}","host":["{{baseUrl}}"],"path":["api","automation","workflow-evaluations",":evaluation"],"variable":[{"key":"evaluation","value":"12","description":"Workflow evaluation ID."}]},"description":"Updates an evaluation. All body fields are optional (`sometimes`) — omitted `name`/`test_data` keep their current values.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Body fields:**\n- `name` (string, optional, max 255)\n- `description` (string, nullable)\n- `test_data` (array, optional, min 1 item when present; each entry an object)\n- `assertions` (array, nullable)\n\n**Response:** The updated evaluation wrapped in `data`.\n\n**Route name:** `automation.evaluations.update`","body":{"mode":"raw","raw":"{\n  \"name\": \"Order tagging regression suite (v2)\",\n  \"description\": \"Updated description\",\n  \"test_data\": [\n    {\n      \"order_id\": 1001,\n      \"total\": 149.99,\n      \"status\": \"paid\"\n    }\n  ],\n  \"assertions\": []\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/automation/workflow-evaluations/{evaluation}","host":["{{baseUrl}}"],"path":["api","automation","workflow-evaluations",":evaluation"],"variable":[{"key":"evaluation","value":"12","description":"Workflow evaluation ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": \"12\",\n    \"workflowId\": \"5\",\n    \"name\": \"Order tagging regression suite\",\n    \"description\": \"Verifies the tag-orders workflow output for representative orders\",\n    \"testData\": [\n      {\n        \"order_id\": 1001,\n        \"total\": 149.99,\n        \"status\": \"paid\"\n      },\n      {\n        \"order_id\": 1002,\n        \"total\": 12.5,\n        \"status\": \"paid\"\n      }\n    ],\n    \"assertions\": [\n      {\n        \"path\": \"output.tags\",\n        \"operator\": \"contains\",\n        \"value\": \"high-value\"\n      }\n    ],\n    \"testCaseCount\": 2,\n    \"createdBy\": \"3\",\n    \"createdAt\": \"2026-06-10T14:30:00+00:00\",\n    \"updatedAt\": \"2026-06-11T08:45:00+00:00\"\n  }\n}"}]},{"name":"Delete Workflow Evaluation","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/automation/workflow-evaluations/{evaluation}","host":["{{baseUrl}}"],"path":["api","automation","workflow-evaluations",":evaluation"],"variable":[{"key":"evaluation","value":"12","description":"Workflow evaluation ID."}]},"description":"Deletes the evaluation. Returns HTTP 204 with an empty body.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Route name:** `automation.evaluations.destroy`"},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/automation/workflow-evaluations/{evaluation}","host":["{{baseUrl}}"],"path":["api","automation","workflow-evaluations",":evaluation"],"variable":[{"key":"evaluation","value":"12","description":"Workflow evaluation ID."}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{}"}]},{"name":"Run Workflow Evaluation","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/automation/workflow-evaluations/{evaluation}/run","host":["{{baseUrl}}"],"path":["api","automation","workflow-evaluations",":evaluation","run"],"variable":[{"key":"evaluation","value":"12","description":"Workflow evaluation ID."}]},"description":"Starts an evaluation run: executes the workflow once per `test_data` test case and records pass/fail results. Returns the created run (unwrapped) with HTTP 201 and `status: \"pending\"`; poll **Show Workflow Evaluation** to observe the run progressing through `running` → `completed` / `failed`.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Run fields:** `id`, `evaluationId`, `status` (`pending` | `running` | `completed` | `failed`), `totalCases`, `passedCases`, `failedCases`, `results`, `startedAt`, `finishedAt`, `duration` (ms), `createdAt`.\n\n**Errors:** HTTP 403 `{ \"message\": \"...\" }` when automation is disabled for the tenant (`AutomationDisabledException`).\n\n**Route name:** `automation.evaluations.run`"},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/automation/workflow-evaluations/{evaluation}/run","host":["{{baseUrl}}"],"path":["api","automation","workflow-evaluations",":evaluation","run"],"variable":[{"key":"evaluation","value":"12","description":"Workflow evaluation ID."}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": \"44\",\n  \"evaluationId\": \"12\",\n  \"status\": \"pending\",\n  \"totalCases\": 2,\n  \"passedCases\": null,\n  \"failedCases\": null,\n  \"results\": null,\n  \"startedAt\": null,\n  \"finishedAt\": null,\n  \"duration\": null,\n  \"createdAt\": \"2026-06-12T10:20:00+00:00\"\n}"}]}]},{"name":"Card on File","item":[{"name":"List Cards On File","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/payment-methods","host":["{{baseUrl}}"],"path":["api","customers",":customer","payment-methods"],"variable":[{"key":"customer","value":"{{customer}}","description":"The customer id."}]},"description":"List a customer's saved (tokenized) cards on file. Returns only safe display metadata — brand, last four, expiry, cardholder name, the default flag, and the consent timestamp. The processor-vaulted reusable token is NEVER serialized and does not appear in the response.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (PAT).\n\nReturns 200 with `{ data: [ ... ] }` — an array of card-on-file records (no token field)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/{customer}/payment-methods","host":["{{baseUrl}}"],"path":["api","customers",":customer","payment-methods"],"variable":[{"key":"customer","value":"{{customer}}","description":"The customer id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 42,\n      \"processor\": \"square\",\n      \"brand\": \"Visa\",\n      \"last_four\": \"4242\",\n      \"exp_month\": 12,\n      \"exp_year\": 2028,\n      \"cardholder_name\": \"Alex Rivera\",\n      \"is_default\": true,\n      \"label\": \"Visa ••••4242\",\n      \"consent_given_at\": \"2026-07-06T14:30:00+00:00\",\n      \"created_at\": \"2026-07-06T14:30:00+00:00\"\n    },\n    {\n      \"id\": 43,\n      \"processor\": \"stripe\",\n      \"brand\": \"Mastercard\",\n      \"last_four\": \"4444\",\n      \"exp_month\": 3,\n      \"exp_year\": 2027,\n      \"cardholder_name\": \"Alex Rivera\",\n      \"is_default\": false,\n      \"label\": \"Mastercard ••••4444\",\n      \"consent_given_at\": \"2026-06-01T09:15:00+00:00\",\n      \"created_at\": \"2026-06-01T09:15:00+00:00\"\n    }\n  ]\n}"}]},{"name":"Save Card On File","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/payment-methods","host":["{{baseUrl}}"],"path":["api","customers",":customer","payment-methods"],"variable":[{"key":"customer","value":"{{customer}}","description":"The customer id."}]},"description":"Save a tokenized card on file for a customer. The client tokenizes the card with the processor SDK (Square / Stripe) and sends ONLY the resulting reusable token — a raw card number (PAN) or CVV is never accepted. Requires explicit, affirmative customer consent.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (PAT).\nPermission: `customers.update`.\n\nRequest body fields:\n- `processor` (string, required) — the tokenizing processor. One of: `square`, `stripe`.\n- `processor_token` (string, required, max 255) — the processor's reusable card token from client-side SDK vaulting (e.g. a Square card nonce or Stripe PaymentMethod id). NEVER a raw PAN or CVV.\n- `processor_customer_id` (string, nullable, max 255) — the processor's customer/vault id the token is attached to.\n- `consent_given` (required, must be accepted — `true`, `1`, `\"yes\"`, or `\"on\"`) — explicit customer consent to store the card. Error when missing/false: \"Explicit customer consent is required to store a card on file.\"\n- `brand` (string, nullable, max 50) — safe display card brand (e.g. Visa, Mastercard).\n- `last_four` (nullable, exactly 4 digits) — safe display last four of the card.\n- `exp_month` (integer, nullable, 1–12) — card expiry month.\n- `exp_year` (integer, nullable, 2020–2100) — card expiry year.\n- `cardholder_name` (string, nullable, max 255) — name on the card.\n- `is_default` (boolean, nullable) — make this the customer's default card. Defaults to false.\n\nReturns 201 with `{ data: <card on file>, message: \"Card saved on file.\" }` (no token field).","body":{"mode":"raw","raw":"{\n  \"processor\": \"square\",\n  \"processor_token\": \"cnon:card-nonce-vaulted\",\n  \"processor_customer_id\": \"sq_cust_9f3a2b\",\n  \"consent_given\": true,\n  \"brand\": \"Visa\",\n  \"last_four\": \"4242\",\n  \"exp_month\": 12,\n  \"exp_year\": 2028,\n  \"cardholder_name\": \"Alex Rivera\",\n  \"is_default\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/customers/{customer}/payment-methods","host":["{{baseUrl}}"],"path":["api","customers",":customer","payment-methods"],"variable":[{"key":"customer","value":"{{customer}}","description":"The customer id."}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"processor\": \"square\",\n    \"brand\": \"Visa\",\n    \"last_four\": \"4242\",\n    \"exp_month\": 12,\n    \"exp_year\": 2028,\n    \"cardholder_name\": \"Alex Rivera\",\n    \"is_default\": true,\n    \"label\": \"Visa ••••4242\",\n    \"consent_given_at\": \"2026-07-06T14:30:00+00:00\",\n    \"created_at\": \"2026-07-06T14:30:00+00:00\"\n  },\n  \"message\": \"Card saved on file.\"\n}"}]},{"name":"Set Default Card","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/payment-methods/{paymentMethod}/default","host":["{{baseUrl}}"],"path":["api","customers",":customer","payment-methods",":paymentMethod","default"],"variable":[{"key":"customer","value":"{{customer}}","description":"The customer id."},{"key":"paymentMethod","value":"{{paymentMethod}}","description":"The saved card (payment method) id."}]},"description":"Make a saved card the customer's default. Clears the default flag on the customer's other cards and sets it on this one.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (PAT).\nPermission: `customers.update`.\n\nNo request body. The card must belong to the customer in the path, otherwise a 422 is returned.\n\nReturns 200 with `{ data: <card on file>, message: \"Default card updated.\" }` (no token field)."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/customers/{customer}/payment-methods/{paymentMethod}/default","host":["{{baseUrl}}"],"path":["api","customers",":customer","payment-methods",":paymentMethod","default"],"variable":[{"key":"customer","value":"{{customer}}","description":"The customer id."},{"key":"paymentMethod","value":"{{paymentMethod}}","description":"The saved card (payment method) id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"processor\": \"square\",\n    \"brand\": \"Visa\",\n    \"last_four\": \"4242\",\n    \"exp_month\": 12,\n    \"exp_year\": 2028,\n    \"cardholder_name\": \"Alex Rivera\",\n    \"is_default\": true,\n    \"label\": \"Visa ••••4242\",\n    \"consent_given_at\": \"2026-07-06T14:30:00+00:00\",\n    \"created_at\": \"2026-07-06T14:30:00+00:00\"\n  },\n  \"message\": \"Default card updated.\"\n}"}]},{"name":"Remove Card On File","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/payment-methods/{paymentMethod}","host":["{{baseUrl}}"],"path":["api","customers",":customer","payment-methods",":paymentMethod"],"variable":[{"key":"customer","value":"{{customer}}","description":"The customer id."},{"key":"paymentMethod","value":"{{paymentMethod}}","description":"The saved card (payment method) id."}]},"description":"Remove a saved card from the customer's file. Deletes the stored token reference and its display metadata.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (PAT).\nPermission: `customers.update`.\n\nNo request body. The card must belong to the customer in the path, otherwise a 422 is returned.\n\nReturns 200 with `{ message: \"Card removed.\" }`."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/customers/{customer}/payment-methods/{paymentMethod}","host":["{{baseUrl}}"],"path":["api","customers",":customer","payment-methods",":paymentMethod"],"variable":[{"key":"customer","value":"{{customer}}","description":"The customer id."},{"key":"paymentMethod","value":"{{paymentMethod}}","description":"The saved card (payment method) id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Card removed.\"\n}"}]}]},{"name":"Metrics","item":[{"name":"Get Channel Health Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/channel-health/summary","host":["{{baseUrl}}"],"path":["api","channel-health","summary"]},"description":"Returns the latest value of every monitored seller-performance metric — one row per integration instance × marketplace × metric × dimension × source — with SKU's evaluation (`status`: healthy, at_risk, breached, no_data), the remaining headroom (more bad events before the target is breached), the recovery date, the date the current status began, open shipment-flag counts and up to 5 sales orders behind the bad events (`cause_orders`, with the full count in `cause_order_count`): flags already counted against the metric first (expired, or open and unfixable), then still-fixable ones marked `pending: true`; marketplace-itemised rows the seller isn't accountable for (e.g. customer-requested cancellations) are left out.\n\n**Required scope: `channel-health:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`alert_id` / `snoozed_until` are the calling user's own open alert for the metric, if any (snooze it with Snooze Alert).\n\n`sources` lists every monitored instance × marketplace, including ones whose first report hasn't arrived yet (`state` = no_data, no attempt). `state` is ok, refused (the marketplace denied access — e.g. a missing SP-API role), failing or no_data.\n\n`meta`: `last_synced_at`, `next_sync_at` (the next scheduled daily sync), `refresh_available_at` (null while a manual refresh is allowed) and `open_flag_count`.\n\nSKU-estimate rows (`source` = sku_computed, e.g. Valid Tracking Rate per Amazon category with `dimension` = category:automotive) are SKU's own calculation from the shipments it confirmed.\n\nAuthentication: Bearer token (Personal Access Token) with the `channel-health:read` scope. The user needs the Integrations › View permission."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/channel-health/summary","host":["{{baseUrl}}"],"path":["api","channel-health","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"integration_instance\": {\n        \"id\": 3,\n        \"name\": \"Northwind Outfitters\",\n        \"channel\": \"amazon\"\n      },\n      \"marketplace_id\": \"ATVPDKIKX0DER\",\n      \"marketplace_name\": \"Amazon US\",\n      \"metric\": \"valid_tracking_rate\",\n      \"metric_label\": \"Valid Tracking Rate\",\n      \"dimension\": null,\n      \"dimension_label\": null,\n      \"source\": \"marketplace\",\n      \"value\": 92.8571,\n      \"numerator\": 13,\n      \"denominator\": 14,\n      \"unit\": \"percent\",\n      \"direction\": \"higher_is_better\",\n      \"target_value\": 95,\n      \"target_operator\": \"gte\",\n      \"warn_value\": 97,\n      \"window_days\": 30,\n      \"reporting_from\": \"2026-09-09\",\n      \"reporting_to\": \"2026-10-08\",\n      \"status\": \"breached\",\n      \"marketplace_status\": \"BAD\",\n      \"headroom\": 0,\n      \"recovers_on\": \"2026-10-24\",\n      \"status_since\": \"2026-09-24\",\n      \"snapshot_date\": \"2026-10-09\",\n      \"synced_at\": \"2026-10-09T06:12:04Z\",\n      \"open_flag_count\": 1,\n      \"cause_orders\": [\n        {\n          \"id\": 88213,\n          \"sales_order_number\": \"SO-104882\",\n          \"pending\": false\n        }\n      ],\n      \"cause_order_count\": 1,\n      \"alert_id\": \"9d3f7c1e-1b2a-4c55-8f3e-2a6b7c8d9e01\",\n      \"snoozed_until\": null\n    }\n  ],\n  \"sources\": [\n    {\n      \"integration_instance_id\": 3,\n      \"integration_instance\": {\n        \"id\": 3,\n        \"name\": \"Northwind Outfitters\",\n        \"channel\": \"amazon\"\n      },\n      \"marketplace_id\": \"ATVPDKIKX0DER\",\n      \"marketplace_name\": \"Amazon US\",\n      \"state\": \"ok\",\n      \"last_success_at\": \"2026-10-09T06:12:04Z\",\n      \"last_attempt_at\": \"2026-10-09T06:12:04Z\",\n      \"last_error\": null\n    }\n  ],\n  \"meta\": {\n    \"last_synced_at\": \"2026-10-09T06:12:04Z\",\n    \"next_sync_at\": \"2026-10-10T10:17:00Z\",\n    \"refresh_available_at\": null,\n    \"open_flag_count\": 1\n  }\n}"}]},{"name":"List Channel Health Snapshots","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/channel-health/snapshots","host":["{{baseUrl}}"],"path":["api","channel-health","snapshots"]},"description":"Daily history of channel health metrics, newest first — one point per day per series. Each point carries the effective warn level, `flag_count` (shipment flags SKU raised for the metric that day) and `events` (one line per flag, plus `ACCOUNT_STATUS_CHANGED` on days the marketplace changed the account status).\n\n**Required scope: `channel-health:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nWhen `dimension` is omitted only the overall rows are returned; pass a dimension (e.g. `category:automotive`, `mfn`, `window:10d`) to read that series.\n\nPaginated: `per_page` default 10, max 500.\n\nAuthentication: Bearer token (Personal Access Token) with the `channel-health:read` scope. The user needs the Integrations › View permission."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/channel-health/snapshots","host":["{{baseUrl}}"],"path":["api","channel-health","snapshots"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 5512,\n      \"integration_instance_id\": 3,\n      \"marketplace_id\": \"ATVPDKIKX0DER\",\n      \"metric\": \"valid_tracking_rate\",\n      \"dimension\": null,\n      \"dimension_label\": null,\n      \"source\": \"marketplace\",\n      \"snapshot_date\": \"2026-10-09\",\n      \"value\": 92.8571,\n      \"numerator\": 13,\n      \"denominator\": 14,\n      \"target_value\": 95,\n      \"target_operator\": \"gte\",\n      \"warn_value\": 97,\n      \"window_days\": 30,\n      \"status\": \"breached\",\n      \"marketplace_status\": \"BAD\",\n      \"headroom\": 0,\n      \"synced_at\": \"2026-10-09T06:12:04Z\",\n      \"flag_count\": 1,\n      \"events\": [\n        \"Carrier sent as Other: SO-105210\"\n      ]\n    }\n  ],\n  \"first_page_url\": \"https://acme.sku.io/api/channel-health/snapshots?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://acme.sku.io/api/channel-health/snapshots?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://acme.sku.io/api/channel-health/snapshots\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"List Channel Health Metrics","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/channel-health/metrics","host":["{{baseUrl}}"],"path":["api","channel-health","metrics"]},"description":"The metric catalogue: every metric Channel Health monitors on at least one channel, with its label, short label, direction (`higher_is_better`, `lower_is_better`, `categorical`), unit (`percent`, `score`, `count`, `status`), default target and rolling window per channel, the noun for one bad event and a link to the marketplace's own performance page.\n\n**Required scope: `channel-health:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Bearer token (Personal Access Token) with the `channel-health:read` scope. The user needs the Integrations › View permission."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/channel-health/metrics","host":["{{baseUrl}}"],"path":["api","channel-health","metrics"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"key\": \"valid_tracking_rate\",\n      \"label\": \"Valid Tracking Rate\",\n      \"short_label\": \"VTR\",\n      \"direction\": \"higher_is_better\",\n      \"unit\": \"percent\",\n      \"default_targets\": {\n        \"amazon\": {\n          \"value\": 95,\n          \"operator\": \"gte\"\n        },\n        \"walmart\": {\n          \"value\": 99,\n          \"operator\": \"gte\"\n        }\n      },\n      \"windows\": {\n        \"amazon\": 30,\n        \"walmart\": 30\n      },\n      \"bad_event_noun\": \"shipment\",\n      \"help_urls\": {\n        \"amazon\": \"https://sellercentral.amazon.com/performance/dashboard\",\n        \"walmart\": null\n      }\n    }\n  ]\n}"}]},{"name":"List Orders Behind a Channel Health Metric","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/channel-health/metrics/{metric}/orders?integration_instance_id=3","host":["{{baseUrl}}"],"path":["api","channel-health","metrics",":metric","orders"],"query":[{"key":"integration_instance_id","value":"3","description":"Integration instance ID","disabled":true}],"variable":[{"key":"metric","value":"valid_tracking_rate","description":"Metric key (see List Channel Health Metrics). Unknown keys return 404."}]},"description":"The orders behind a metric for one integration instance, bad ones first, plus `breakdown` — every bad event (not just this page) grouped by fulfillment source and the raw carrier string sent.\n\n**Required scope: `channel-health:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nSources:\n- Amazon Valid Tracking Rate and Late Shipment Rate: the shipments SKU confirmed to Amazon within the metric's window, each judged the way SKU's estimate judges it (`counts_as` valid / invalid / late), linked to its shipment flag (`flag_id`). `is_estimate` is true.\n- Metrics the marketplace itemises (Walmart cancellations, on-time shipment, etc.): the rows of the latest per-order report, with `counts_as` invalid / late / cancelled / defect, or `not_counted` (and `counted` false) for a row the marketplace lists but doesn't hold the seller accountable for — those are listed last and left out of `breakdown`. `is_estimate` is false.\n\n`counted` is true when the row counts against the metric as a bad event.\n- Metrics no one itemises (e.g. Order Defect Rate claims): an empty page with `is_estimate` true and `seller_central_url` pointing to the marketplace's own report.\n\nPaginated: `per_page` default 10, max 500.\n\nAuthentication: Bearer token (Personal Access Token) with the `channel-health:read` scope. The user needs the Integrations › View permission."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/channel-health/metrics/{metric}/orders?integration_instance_id=3","host":["{{baseUrl}}"],"path":["api","channel-health","metrics",":metric","orders"],"query":[{"key":"integration_instance_id","value":"3","description":"Integration instance ID","disabled":true}],"variable":[{"key":"metric","value":"valid_tracking_rate","description":"Metric key (see List Channel Health Metrics). Unknown keys return 404."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"sales_order\": {\n        \"id\": 88213,\n        \"sales_order_number\": \"SO-104882\"\n      },\n      \"channel_order_id\": \"113-4567890-1234567\",\n      \"category\": \"Automotive\",\n      \"shipped_at\": \"2026-10-06T18:20:00Z\",\n      \"carrier_sent\": \"Other\",\n      \"tracking_number\": \"9400111899223344556677\",\n      \"counts_as\": \"invalid\",\n      \"reason\": \"Carrier sent as Other (\\\"STANDARD\\\")\",\n      \"fulfilled_by\": \"East Coast 3PL (ShipHero)\",\n      \"flag_id\": 41,\n      \"counted\": true\n    }\n  ],\n  \"first_page_url\": \"https://acme.sku.io/api/channel-health/metrics/valid_tracking_rate/orders?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://acme.sku.io/api/channel-health/metrics/valid_tracking_rate/orders?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://acme.sku.io/api/channel-health/metrics/valid_tracking_rate/orders\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1,\n  \"is_estimate\": true,\n  \"breakdown\": [\n    {\n      \"fulfilled_by\": \"East Coast 3PL (ShipHero)\",\n      \"carrier_string\": \"STANDARD\",\n      \"count\": 1\n    }\n  ],\n  \"seller_central_url\": \"https://sellercentral.amazon.com/performance/dashboard\"\n}"}]}]},{"name":"Shipment Flags","item":[{"name":"List Channel Health Flags","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/channel-health/flags","host":["{{baseUrl}}"],"path":["api","channel-health","flags"]},"description":"Shipments SKU predicts will hurt a marketplace metric (carrier sent as Other, missing or reused tracking, tracking that doesn't match the carrier, late or after-delivery confirmations, MCF cancellations counted by Walmart), with the predicted impact on the metric and the fix-by deadline.\n\n**Required scope: `channel-health:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`fixable` says whether Correct Channel Health Flag Carrier can act on the flag now; `not_fixable_reason` explains why not.\n\nFilters:\n- `filter[status]`: open (default when omitted), dismissed, resolved, expired or all\n- `filter[integration_instance_ids]`: comma-separated instance IDs\n- `filter[checks]`: comma-separated checks (carrier_not_recognised, missing_tracking, duplicate_tracking, tracking_format_mismatch, late_confirmation, confirmed_after_delivery, confirmation_outside_edit_window, mcf_cancellation_on_walmart)\n- `filter[search]`: matches ID (exact), sales order number, channel order ID or tracking number. Narrow with `search_columns` (id, sales_order_number, channel_order_id, tracking_number) and force exact matching with `search_strict_columns`\n- Operator filters `filter[<column>.<operator>]` on id (numeric), sales_order_number, channel_order_id, check, metric, marketplace_id, tracking_number (text) and raised_at, fix_by (datetime). Text operators: contains, does_not_contain, is, is_not, is_one_of, is_not_one_of, starts_with, does_not_start_with, ends_with, does_not_end_with, is_empty, is_not_empty\n- `filter_groups`: base64-encoded JSON tree of the same column/operator conditions with and/or conjunctions, e.g. {\"conjunction\":\"and\",\"children\":[{\"type\":\"condition\",\"condition\":{\"column\":\"check\",\"operator\":\"is\",\"value\":\"carrier_not_recognised\"}}]}\n\nSorts: id, raised_at, fix_by (prefix with - for descending; default -raised_at).\n\nPaginated: `per_page` default 10, max 500.\n\nAuthentication: Bearer token (Personal Access Token) with the `channel-health:read` scope. The user needs the Integrations › View permission."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/channel-health/flags","host":["{{baseUrl}}"],"path":["api","channel-health","flags"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 41,\n      \"check\": \"carrier_not_recognised\",\n      \"check_label\": \"Carrier sent as Other\",\n      \"status\": \"open\",\n      \"status_label\": \"Open\",\n      \"integration_instance\": {\n        \"id\": 3,\n        \"name\": \"Northwind Outfitters\",\n        \"channel\": \"amazon\"\n      },\n      \"marketplace_id\": \"ATVPDKIKX0DER\",\n      \"marketplace_name\": \"Amazon US\",\n      \"sales_order\": {\n        \"id\": 88213,\n        \"sales_order_number\": \"SO-105210\"\n      },\n      \"channel_order_id\": \"113-4567890-1234567\",\n      \"sales_order_fulfillment_id\": 77120,\n      \"metric\": \"valid_tracking_rate\",\n      \"dimension\": null,\n      \"detail\": {\n        \"sales_order_number\": \"SO-105210\",\n        \"channel_order_id\": \"113-4567890-1234567\",\n        \"carrier_sent\": \"Other\",\n        \"carrier_string\": \"STANDARD\",\n        \"tracking_number\": \"9400111899223344556677\",\n        \"fulfillment_source\": \"East Coast 3PL (ShipHero)\"\n      },\n      \"predicted_impact\": {\n        \"metric\": \"valid_tracking_rate\",\n        \"dimension\": null,\n        \"before\": 92.86,\n        \"after\": 86.67,\n        \"headroom_after\": 0,\n        \"metric_label\": \"Valid Tracking Rate\"\n      },\n      \"fix_by\": \"2026-10-11T00:00:00Z\",\n      \"fixable\": true,\n      \"not_fixable_reason\": null,\n      \"raised_at\": \"2026-10-09T13:41:00Z\",\n      \"resolved_at\": null,\n      \"dismissed_at\": null,\n      \"dismissed_note\": null\n    }\n  ],\n  \"first_page_url\": \"https://acme.sku.io/api/channel-health/flags?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://acme.sku.io/api/channel-health/flags?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://acme.sku.io/api/channel-health/flags\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"Dismiss Channel Health Flag","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/channel-health/flags/{flag}/dismiss","host":["{{baseUrl}}"],"path":["api","channel-health","flags",":flag","dismiss"],"variable":[{"key":"flag","value":"41","description":"Channel health flag ID"}]},"description":"Dismisses an open flag with an optional note. A dismissed flag stays in history but no longer counts as needing action; the marketplace may still count the shipment.\n\n**Required scope: `channel-health:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns 422 with `reason` = not_open when the flag is already closed.\n\nAuthentication: Bearer token (Personal Access Token) with the `channel-health:write` scope. The user needs the Integrations › View permission.","body":{"mode":"raw","raw":"{\n  \"note\": \"Customer collected the order in store\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/channel-health/flags/{flag}/dismiss","host":["{{baseUrl}}"],"path":["api","channel-health","flags",":flag","dismiss"],"variable":[{"key":"flag","value":"41","description":"Channel health flag ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 41,\n    \"check\": \"carrier_not_recognised\",\n    \"check_label\": \"Carrier sent as Other\",\n    \"status\": \"dismissed\",\n    \"status_label\": \"Dismissed\",\n    \"integration_instance\": {\n      \"id\": 3,\n      \"name\": \"Northwind Outfitters\",\n      \"channel\": \"amazon\"\n    },\n    \"marketplace_id\": \"ATVPDKIKX0DER\",\n    \"marketplace_name\": \"Amazon US\",\n    \"sales_order\": {\n      \"id\": 88213,\n      \"sales_order_number\": \"SO-105210\"\n    },\n    \"channel_order_id\": \"113-4567890-1234567\",\n    \"sales_order_fulfillment_id\": 77120,\n    \"metric\": \"valid_tracking_rate\",\n    \"dimension\": null,\n    \"detail\": {\n      \"sales_order_number\": \"SO-105210\",\n      \"channel_order_id\": \"113-4567890-1234567\",\n      \"carrier_sent\": \"Other\",\n      \"carrier_string\": \"STANDARD\",\n      \"tracking_number\": \"9400111899223344556677\",\n      \"fulfillment_source\": \"East Coast 3PL (ShipHero)\"\n    },\n    \"predicted_impact\": {\n      \"metric\": \"valid_tracking_rate\",\n      \"dimension\": null,\n      \"before\": 92.86,\n      \"after\": 86.67,\n      \"headroom_after\": 0,\n      \"metric_label\": \"Valid Tracking Rate\"\n    },\n    \"fix_by\": \"2026-10-11T00:00:00Z\",\n    \"fixable\": false,\n    \"not_fixable_reason\": \"This flag is already Dismissed.\",\n    \"raised_at\": \"2026-10-09T13:41:00Z\",\n    \"resolved_at\": null,\n    \"dismissed_at\": \"2026-10-09T15:02:11Z\",\n    \"dismissed_note\": \"Customer collected the order in store\"\n  }\n}"}]},{"name":"Correct Channel Health Flag Carrier","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/channel-health/flags/{flag}/correct-carrier","host":["{{baseUrl}}"],"path":["api","channel-health","flags",":flag","correct-carrier"],"variable":[{"key":"flag","value":"41","description":"Channel health flag ID"}]},"description":"Corrects the carrier (and optionally the tracking number) of a flagged shipment and re-sends it to the sales channel, as a background job. The flag resolves once the corrected confirmation is accepted. With `remember_mapping`, the 3PL's shipping-method string is mapped to the chosen carrier so future shipments resolve automatically (ShipHero fulfillments).\n\n**Required scope: `channel-health:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody:\n- `shipping_carrier_id` (integer, required): the carrier the shipment actually went with\n- `tracking_number` (string, optional, max 191): replacement tracking number; omit to keep the current one\n- `remember_mapping` (boolean, optional, default false)\n\nResponds 202 with the background job's `tracked_job_log_id`. Returns 422 with `reason` when the flag can't be corrected: not_open, not_fixable (informational checks), no_fulfillment, delivered (marketplaces only accept tracking corrections before delivery).\n\nAuthentication: Bearer token (Personal Access Token) with the `channel-health:write` scope. The user needs the Sales Orders › Fulfill permission.","body":{"mode":"raw","raw":"{\n  \"shipping_carrier_id\": 3,\n  \"tracking_number\": \"771234567890\",\n  \"remember_mapping\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/channel-health/flags/{flag}/correct-carrier","host":["{{baseUrl}}"],"path":["api","channel-health","flags",":flag","correct-carrier"],"variable":[{"key":"flag","value":"41","description":"Channel health flag ID"}]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Carrier correction started.\",\n  \"tracked_job_log_id\": 9154\n}"}]}]},{"name":"Thresholds","item":[{"name":"List Channel Health Thresholds","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/channel-health/thresholds","host":["{{baseUrl}}"],"path":["api","channel-health","thresholds"]},"description":"Effective warn thresholds for every monitored metric × channel (built-in defaults merged with the tenant's settings), followed by any per-instance overrides. Each row includes the marketplace target it is judged against (`target_source`: marketplace when the marketplace reported it, default otherwise) and the built-in defaults.\n\n**Required scope: `channel-health:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n- `warn_value`: the metric is At risk once it crosses this level\n- `clear_margin`: an open alert only clears once the value is better than the warn level by this much\n- `min_denominator`: below this many orders/shipments, alerts carry a low-volume note\n- `small_sample_warning`: also At risk when the next single bad event would breach the target\n- `enabled`: off stops alerting on the metric\n\nAuthentication: Bearer token (Personal Access Token) with the `channel-health:read` scope. The user needs the Integrations › View permission."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/channel-health/thresholds","host":["{{baseUrl}}"],"path":["api","channel-health","thresholds"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"metric\": \"valid_tracking_rate\",\n      \"metric_label\": \"Valid Tracking Rate\",\n      \"channel\": \"amazon\",\n      \"channel_label\": \"Amazon\",\n      \"integration_instance_id\": null,\n      \"unit\": \"percent\",\n      \"direction\": \"higher_is_better\",\n      \"target_value\": 95,\n      \"target_operator\": \"gte\",\n      \"target_source\": \"marketplace\",\n      \"warn_value\": 97,\n      \"clear_margin\": 0.5,\n      \"min_denominator\": 10,\n      \"small_sample_warning\": true,\n      \"enabled\": true,\n      \"is_default\": true,\n      \"default_warn_value\": 97,\n      \"default_clear_margin\": 0.5,\n      \"default_min_denominator\": 10\n    }\n  ]\n}"}]},{"name":"Update Channel Health Thresholds","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/channel-health/thresholds","host":["{{baseUrl}}"],"path":["api","channel-health","thresholds"]},"description":"Bulk upserts warn thresholds and returns the full effective list. Statuses and alerts are re-evaluated straight away.\n\n**Required scope: `channel-health:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody: `thresholds` (array, 1–200 items), each with:\n- `metric` (string, required): metric key\n- `channel` (string, required): amazon or walmart\n- `integration_instance_id` (integer, optional): null for the tenant default, an instance ID for an override\n- `warn_value` (number, optional): null restores the default warn level\n- `clear_margin` (number, optional, 0–100, default 0.5)\n- `min_denominator` (integer, optional, 0–1000000, default 10)\n- `small_sample_warning` (boolean, optional, default true)\n- `enabled` (boolean, optional, default true)\n\nReturns 422 when a warn level is on or past the marketplace target (a warn level can only be stricter than the target) or when the metric isn't monitored on the channel; errors are keyed `thresholds.<index>.<field>`.\n\nAuthentication: Bearer token (Personal Access Token) with the `channel-health:write` scope. The user needs the Alert Preferences › Update permission.","body":{"mode":"raw","raw":"{\n  \"thresholds\": [\n    {\n      \"metric\": \"valid_tracking_rate\",\n      \"channel\": \"amazon\",\n      \"integration_instance_id\": null,\n      \"warn_value\": 97,\n      \"clear_margin\": 0.5,\n      \"min_denominator\": 10,\n      \"small_sample_warning\": true,\n      \"enabled\": true\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/channel-health/thresholds","host":["{{baseUrl}}"],"path":["api","channel-health","thresholds"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"metric\": \"valid_tracking_rate\",\n      \"metric_label\": \"Valid Tracking Rate\",\n      \"channel\": \"amazon\",\n      \"channel_label\": \"Amazon\",\n      \"integration_instance_id\": null,\n      \"unit\": \"percent\",\n      \"direction\": \"higher_is_better\",\n      \"target_value\": 95,\n      \"target_operator\": \"gte\",\n      \"target_source\": \"marketplace\",\n      \"warn_value\": 97,\n      \"clear_margin\": 0.5,\n      \"min_denominator\": 10,\n      \"small_sample_warning\": true,\n      \"enabled\": true,\n      \"is_default\": false,\n      \"default_warn_value\": 97,\n      \"default_clear_margin\": 0.5,\n      \"default_min_denominator\": 10\n    }\n  ]\n}"}]}]},{"name":"Refresh","item":[{"name":"Refresh Channel Health","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/channel-health/refresh","host":["{{baseUrl}}"],"path":["api","channel-health","refresh"]},"description":"Requests fresh seller-performance numbers from the marketplaces as a background job, for one integration instance or (with `integration_instance_id` null) every monitored instance. Marketplaces update these numbers about once a day, so each instance can be refreshed at most once an hour; refreshing everything skips instances refreshed within the hour.\n\n**Required scope: `channel-health:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nResponds 202 with the background job's `tracked_job_log_id`, the instances being refreshed and when they may be refreshed again. Responds 429 with a `Retry-After` header (seconds) and `next_available_at` when every targeted instance was refreshed less than an hour ago, and 422 when the instance isn't monitored by Channel Health.\n\nAuthentication: Bearer token (Personal Access Token) with the `channel-health:write` scope. The user needs the Integrations › Sync permission.","body":{"mode":"raw","raw":"{\n  \"integration_instance_id\": 3\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/channel-health/refresh","host":["{{baseUrl}}"],"path":["api","channel-health","refresh"]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Channel health refresh started.\",\n  \"tracked_job_log_id\": 9155,\n  \"integration_instance_ids\": [\n    3\n  ],\n  \"next_available_at\": \"2026-10-09T15:12:00Z\"\n}"}]}]},{"name":"Channel Partners","item":[{"name":"Delete All Inbound Events","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/channel-partners/{id}/inbound-events/delete-all","host":["{{baseUrl}}"],"path":["api","channel-partners",":id","inbound-events","delete-all"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Delete ALL inbound events for a channel partner instance. Use with caution.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/channel-partners/{id}/inbound-events/delete-all","host":["{{baseUrl}}"],"path":["api","channel-partners",":id","inbound-events","delete-all"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"97 event(s) deleted.\"\n}"}]},{"name":"List Channel Partners","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/channel-partners","host":["{{baseUrl}}"],"path":["api","channel-partners"]},"description":"Returns all API Sales Channel integration instances (channel partners).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token (Sanctum user session)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/channel-partners","host":["{{baseUrl}}"],"path":["api","channel-partners"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"My Storefront API\",\n      \"integration_id\": 5,\n      \"connection_settings\": {},\n      \"integration_settings\": {},\n      \"sync_status\": null,\n      \"is_automatic_sync_enabled\": false,\n      \"sales_channel\": {\n        \"id\": 3,\n        \"name\": \"My Storefront\",\n        \"store_id\": 1\n      },\n      \"orders_count\": null,\n      \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2024-01-15T10:00:00.000000Z\"\n    }\n  ]\n}"}]},{"name":"Create Channel Partner","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/channel-partners","host":["{{baseUrl}}"],"path":["api","channel-partners"]},"description":"Create a new API Sales Channel integration instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nRequest body fields:\n- `name` (required, string, max:255)\n- `store_id` (optional, integer)","body":{"mode":"raw","raw":"{\n  \"name\": \"B2B Portal Integration\",\n  \"store_id\": 1\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/channel-partners","host":["{{baseUrl}}"],"path":["api","channel-partners"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 2,\n    \"name\": \"B2B Portal Integration\"\n  }\n}"}]},{"name":"Get Channel Partner","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/channel-partners/{id}","host":["{{baseUrl}}"],"path":["api","channel-partners",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns a single channel partner integration instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/channel-partners/{id}","host":["{{baseUrl}}"],"path":["api","channel-partners",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"My Storefront API\",\n    \"integration_id\": 5,\n    \"connection_settings\": {},\n    \"integration_settings\": {},\n    \"sync_status\": null,\n    \"is_automatic_sync_enabled\": false,\n    \"sales_channel\": {\n      \"id\": 3,\n      \"name\": \"My Storefront\",\n      \"store_id\": 1\n    },\n    \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2024-01-15T10:00:00.000000Z\"\n  }\n}"}]},{"name":"Update Channel Partner","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/channel-partners/{id}","host":["{{baseUrl}}"],"path":["api","channel-partners",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Updates a channel partner integration instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFields (all optional):\n- name: Updated name (max 255 chars)\n- store_id: Updated store ID (nullable)\n\n- unmatched_customer_behavior (optional): auto_create (default — unmatched inbound customers are matched or created automatically) or review (orders from unmatched customers are held with a 422 unmapped_references response until the identity is mapped).","body":{"mode":"raw","raw":"{\n  \"name\": \"Updated Storefront API\",\n  \"store_id\": 2,\n  \"unmatched_customer_behavior\": \"auto_create\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/channel-partners/{id}","host":["{{baseUrl}}"],"path":["api","channel-partners",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Updated Storefront API\",\n    \"integration_id\": 5\n  }\n}"}]},{"name":"Delete Channel Partner","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/channel-partners/{id}","host":["{{baseUrl}}"],"path":["api","channel-partners",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Deletes a channel partner integration instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nReturns 204 No Content."},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/channel-partners/{id}","host":["{{baseUrl}}"],"path":["api","channel-partners",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":""}]},{"name":"Get Channel Partner Dashboard","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/channel-partners/{id}/dashboard","host":["{{baseUrl}}"],"path":["api","channel-partners",":id","dashboard"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns dashboard stats and recent orders for a channel partner.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/channel-partners/{id}/dashboard","host":["{{baseUrl}}"],"path":["api","channel-partners",":id","dashboard"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"instance\": {\n      \"id\": 1,\n      \"name\": \"My Storefront API\"\n    },\n    \"stats\": {\n      \"total_orders\": 142,\n      \"pending_orders\": 12,\n      \"fulfilled_orders\": 130,\n      \"last_order_at\": \"2024-06-01T09:00:00.000000Z\"\n    },\n    \"recent_orders\": [\n      {\n        \"id\": 500,\n        \"sales_order_number\": \"SO-2024-500\",\n        \"order_status\": \"open\",\n        \"fulfillment_status\": \"unfulfilled\",\n        \"order_date\": \"2024-06-01T08:00:00.000000Z\"\n      }\n    ]\n  }\n}"}]},{"name":"List Channel Partner Orders","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/channel-partners/{id}/orders?page=1&per_page=15","host":["{{baseUrl}}"],"path":["api","channel-partners",":id","orders"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"15","description":"Results per page (default 10).","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns paginated sales orders for a channel partner's sales channel.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/channel-partners/{id}/orders?page=1&per_page=15","host":["{{baseUrl}}"],"path":["api","channel-partners",":id","orders"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"15","description":"Results per page (default 10).","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 500,\n      \"sales_order_number\": \"SO-2024-500\",\n      \"order_status\": \"open\",\n      \"fulfillment_status\": \"unfulfilled\",\n      \"customer_name\": \"John Doe\",\n      \"order_date\": \"2024-06-01T08:00:00.000000Z\",\n      \"created_at\": \"2024-06-01T08:00:00.000000Z\"\n    }\n  ],\n  \"per_page\": 15,\n  \"total\": 142\n}"}]},{"name":"List API Tokens","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/channel-partners/{id}/tokens","host":["{{baseUrl}}"],"path":["api","channel-partners",":id","tokens"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Lists all active API tokens for a channel partner.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/channel-partners/{id}/tokens","host":["{{baseUrl}}"],"path":["api","channel-partners",":id","tokens"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3,\n      \"name\": \"Production Token\",\n      \"abilities\": [\n        \"channel:read\",\n        \"channel:write\"\n      ],\n      \"created_at\": \"2024-06-01T12:00:00.000000Z\",\n      \"last_used_at\": \"2024-06-01T14:30:00.000000Z\"\n    }\n  ]\n}"}]},{"name":"Generate API Token","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/channel-partners/{id}/tokens","host":["{{baseUrl}}"],"path":["api","channel-partners",":id","tokens"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Generates a new API token for a channel partner. The plain text token is only shown once.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFields:\n- name (required): Token name/label (max 255 chars)","body":{"mode":"raw","raw":"{\n  \"name\": \"Production Token\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/channel-partners/{id}/tokens","host":["{{baseUrl}}"],"path":["api","channel-partners",":id","tokens"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"token\": \"3|abc123def456ghi789jkl012mno345pqr678|channel:read|channel:write\",\n    \"name\": \"Production Token\"\n  },\n  \"message\": \"Token generated. Please copy it now — it won't be shown again.\"\n}"}]},{"name":"Revoke API Token","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/channel-partners/{id}/tokens/{id2}","host":["{{baseUrl}}"],"path":["api","channel-partners",":id","tokens",":id2"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]},"description":"Revokes a specific API token for a channel partner.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/channel-partners/{id}/tokens/{id2}","host":["{{baseUrl}}"],"path":["api","channel-partners",":id","tokens",":id2"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Token revoked successfully.\"\n}"}]},{"name":"Create Channel API Token","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/channel/tokens","host":["{{baseUrl}}"],"path":["api","channel","tokens"]},"description":"Creates a new Sanctum token with channel:read and channel:write abilities for a sales channel integration instance. If a token with these abilities already exists, it will be replaced.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFields:\n- name (required): Token name (max 255 chars)\n- integration_instance_id (required): ID of the integration instance","body":{"mode":"raw","raw":"{\n  \"name\": \"channel-token-production\",\n  \"integration_instance_id\": 1\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/channel/tokens","host":["{{baseUrl}}"],"path":["api","channel","tokens"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"token\": \"4|channeltoken123abc456def789ghi012jkl\",\n    \"integration_instance_id\": 1,\n    \"name\": \"channel-token-production\",\n    \"abilities\": [\n      \"channel:read\",\n      \"channel:write\"\n    ],\n    \"created_at\": \"2024-06-01T12:00:00.000000Z\",\n    \"replaced_existing\": false\n  }\n}"}]}]},{"name":"Sales Channel Listing Profiles","item":[{"name":"List Listing Profiles","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channel-listing-profiles","host":["{{baseUrl}}"],"path":["api","v2","sales-channel-listing-profiles"]},"description":"Cross-channel paginated list of listing profiles, with the owning sales channel and \"used by\" template/draft counts.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach row carries `templates_count` / `drafts_count` (what uses the profile) and `mappings_count` — how many general field mappings the profile holds. A profile can do all its work through mappings and carry no settings at all, so `settings` alone does not say whether a profile is configured.\n\nAuthentication: Requires Bearer token.\n\nAvailable filters:\n- filter[sales_channel_id]: exact match on channel\n- filter[is_default]: exact match on the default flag (1/0)\n- filter[name]: partial match on name\n- filter[search]: search by name or exact id\n- filter[created_at] / filter[updated_at]: date filters\n\nAllowed sorts: id, name, sales_channel_id, is_default, created_at, updated_at (default: name).\n\nPagination: page (default 1), per_page (default 20, max 100)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channel-listing-profiles","host":["{{baseUrl}}"],"path":["api","v2","sales-channel-listing-profiles"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"sales_channel_id\": 5,\n      \"name\": \"Standard\",\n      \"is_default\": true,\n      \"settings\": {\n        \"condition\": \"NEW\",\n        \"handling_time\": \"1\"\n      },\n      \"created_at\": \"2026-07-18T09:00:00+00:00\",\n      \"updated_at\": \"2026-07-20T14:30:00+00:00\",\n      \"templates_count\": 2,\n      \"drafts_count\": 5,\n      \"mappings_count\": 14,\n      \"sales_channel\": {\n        \"id\": 5,\n        \"name\": \"eBay US\",\n        \"integration_id\": 3,\n        \"integration_name\": \"Ebay\",\n        \"integration_image_url\": \"https://cdn.sku.io/integrations/ebay.png\",\n        \"integration_instance_id\": 9\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 20,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Bulk Delete Listing Profiles","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/sales-channel-listing-profiles/bulk-destroy","host":["{{baseUrl}}"],"path":["api","v2","sales-channel-listing-profiles","bulk-destroy"]},"description":"Bulk delete listing profiles. Provide either an explicit `ids` array, or set `apply_to_all` to true with a `filters` object to delete every profile matching the current filter set.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body (one of):\n- ids: array of profile IDs to delete (required unless apply_to_all is true)\n- apply_to_all: when true, delete all profiles matching filters\n- filters: filter object (e.g. { \"sales_channel_id\": 5 }) applied when apply_to_all is true","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    12,\n    13,\n    14\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/sales-channel-listing-profiles/bulk-destroy","host":["{{baseUrl}}"],"path":["api","v2","sales-channel-listing-profiles","bulk-destroy"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"deleted\": 3\n  },\n  \"message\": \"Deleted 3 profiles.\"\n}"}]},{"name":"Get Listing Profile Schema","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{salesChannel}/listing-profiles/schema","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":salesChannel","listing-profiles","schema"],"variable":[{"key":"salesChannel","value":"15","description":"Sales channel id"}]},"description":"The settings this channel's publish path reads from a listing profile, described as labelled, typed fields, with any choices fetched live from the channel folded into the same response.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach field carries `key` (the exact settings key), `label`, `type` (`text`, `number`, `select`, `boolean`, `list`), an optional `default`, `help`, `group`, and either static `options` or `options_from` — a dotted path into `options` for choices that come from the channel (eBay business policies). When a live choice is stored, `label_key` names the settings key that should also receive the chosen option's label.\n\n`fields` is empty for a channel that declares no schema; treat any key as free-form there. `error` is set, with `options` empty, when the channel could not be reached for its live choices.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{salesChannel}/listing-profiles/schema","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":salesChannel","listing-profiles","schema"],"variable":[{"key":"salesChannel","value":"15","description":"Sales channel id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"fields\": [\n      {\n        \"key\": \"condition_id\",\n        \"label\": \"Condition\",\n        \"type\": \"select\",\n        \"help\": null,\n        \"default\": 1000,\n        \"required\": false,\n        \"options\": [\n          {\n            \"value\": 1000,\n            \"label\": \"New\"\n          },\n          {\n            \"value\": 3000,\n            \"label\": \"Used\"\n          }\n        ],\n        \"options_from\": null,\n        \"label_key\": null,\n        \"group\": \"Listing\"\n      },\n      {\n        \"key\": \"dispatch_time_max\",\n        \"label\": \"Handling time (days)\",\n        \"type\": \"number\",\n        \"help\": \"Business days from payment to shipment.\",\n        \"default\": null,\n        \"required\": false,\n        \"options\": null,\n        \"options_from\": null,\n        \"label_key\": null,\n        \"group\": \"Listing\"\n      },\n      {\n        \"key\": \"ebay_payment_policy_id\",\n        \"label\": \"Payment policy\",\n        \"type\": \"select\",\n        \"help\": \"Fetched from your eBay account.\",\n        \"default\": null,\n        \"required\": false,\n        \"options\": null,\n        \"options_from\": \"business_policies.payment\",\n        \"label_key\": \"ebay_payment_policy_name\",\n        \"group\": \"Business policies\"\n      }\n    ],\n    \"options\": {\n      \"business_policies\": {\n        \"payment\": [\n          {\n            \"id\": \"221479599013\",\n            \"name\": \"eBay Payments:Immediate pay\"\n          }\n        ],\n        \"fulfillment\": [\n          {\n            \"id\": \"106373561013\",\n            \"name\": \"Dropship 2biz\"\n          }\n        ],\n        \"return\": [\n          {\n            \"id\": \"261809662013\",\n            \"name\": \"30 days money back\"\n          }\n        ]\n      }\n    },\n    \"error\": null\n  }\n}"}]},{"name":"List Listing Profile Mappings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{salesChannel}/listing-profiles/{profile}/mappings","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":salesChannel","listing-profiles",":profile","mappings"],"variable":[{"key":"salesChannel","value":"15","description":"Sales channel id"},{"key":"profile","value":"4","description":"Listing profile id. Must belong to the sales channel in the path."}]},"description":"The general field mappings a listing profile carries — the mappings that hold whatever the category, so a category template does not have to repeat them.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA profile's vocabulary is the channel's CORE listing fields and nothing else — the fields the channel asks for on every listing whatever the category, as its module declares them. Category aspects are never returned and can never be stored here: a template is category-specific, a profile is not. A row with `is_core: false` is a mapping made before that rule was enforced — it is still live, so it is returned to be removed rather than hidden.\n\nEach row carries EITHER the profile's own binding in `source_type` / `source_value`, or — when the profile leaves the field unbound — what it would resolve to from a lower layer in `inherited_source_type` / `inherited_source_value` / `inherited_from` (`channel` or `integration`). Exactly one of the two pairs is populated.\n\n`source_type` is one of `product_field`, `product_attribute`, `static`, `expression`. `is_core` marks a listing-level field the channel always needs; `category_count` is how many synced categories carry the field (0 for core fields).\n\nA field's value resolves most-specific-first: per-listing override, then category template, then this profile, then the channel-wide mappings, then the integration-wide defaults.\n\nReturns 404 when the profile does not belong to the sales channel in the path.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{salesChannel}/listing-profiles/{profile}/mappings","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":salesChannel","listing-profiles",":profile","mappings"],"variable":[{"key":"salesChannel","value":"15","description":"Sales channel id"},{"key":"profile","value":"4","description":"Listing profile id. Must belong to the sales channel in the path."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"name\": \"Title\",\n      \"label\": \"Title\",\n      \"usage\": \"required\",\n      \"type\": \"text\",\n      \"cardinality\": 1,\n      \"allowed_values\": null,\n      \"allows_custom_value\": true,\n      \"description\": \"The listing title shown to shoppers. Prefilled from the product name — keep it clear and descriptive.\",\n      \"category_count\": 0,\n      \"is_core\": true,\n      \"source_type\": \"product_field\",\n      \"source_value\": \"name\",\n      \"inherited_source_type\": null,\n      \"inherited_source_value\": null,\n      \"inherited_from\": null\n    },\n    {\n      \"name\": \"Brand\",\n      \"label\": \"Brand\",\n      \"usage\": \"required\",\n      \"type\": \"text\",\n      \"cardinality\": 1,\n      \"allowed_values\": null,\n      \"allows_custom_value\": true,\n      \"description\": null,\n      \"category_count\": 0,\n      \"is_core\": true,\n      \"source_type\": null,\n      \"source_value\": null,\n      \"inherited_source_type\": \"product_field\",\n      \"inherited_source_value\": \"brand\",\n      \"inherited_from\": \"channel\"\n    }\n  ]\n}"}]},{"name":"Replace Listing Profile Mappings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{salesChannel}/listing-profiles/{profile}/mappings","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":salesChannel","listing-profiles",":profile","mappings"],"variable":[{"key":"salesChannel","value":"15","description":"Sales channel id"},{"key":"profile","value":"4","description":"Listing profile id. Must belong to the sales channel in the path."}]},"description":"Replace the listing profile's whole set of general field mappings.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThis is a full sync, not a per-row patch: send every row the editor rendered. A field omitted from `mappings` is removed from the profile, and a row whose `source_type` is `blank` or null (or whose `source_value` is empty) is treated as unmapped and likewise stores nothing — that is how a field is unbound without deleting the row. Field names are matched case-insensitively; when the same name appears twice the last entry wins.\n\nResponds with the profile's rows in the same shape as the GET, so a client can render the saved state without a second call.\n\nReturns 404 when the profile does not belong to the sales channel in the path, and 422 when a row is missing `channel_field_name` or carries an unrecognised `source_type`.\n\nA profile's vocabulary is the channel's CORE listing fields and nothing else — the fields the channel asks for on every listing whatever the category, as its module declares them. Category aspects are never returned and can never be stored here: a template is category-specific, a profile is not. A row with `is_core: false` is a mapping made before that rule was enforced — it is still live, so it is returned to be removed rather than hidden.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"mappings\": [\n    {\n      \"channel_field_name\": \"Title\",\n      \"source_type\": \"product_field\",\n      \"source_value\": \"name\"\n    },\n    {\n      \"channel_field_name\": \"Country of Origin\",\n      \"source_type\": \"static\",\n      \"source_value\": \"United States\"\n    },\n    {\n      \"channel_field_name\": \"Brand\",\n      \"source_type\": \"blank\",\n      \"source_value\": null\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{salesChannel}/listing-profiles/{profile}/mappings","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":salesChannel","listing-profiles",":profile","mappings"],"variable":[{"key":"salesChannel","value":"15","description":"Sales channel id"},{"key":"profile","value":"4","description":"Listing profile id. Must belong to the sales channel in the path."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"name\": \"Title\",\n      \"label\": \"Title\",\n      \"usage\": \"required\",\n      \"type\": \"text\",\n      \"cardinality\": 1,\n      \"allowed_values\": null,\n      \"allows_custom_value\": true,\n      \"description\": \"The listing title shown to shoppers. Prefilled from the product name — keep it clear and descriptive.\",\n      \"category_count\": 0,\n      \"is_core\": true,\n      \"source_type\": \"product_field\",\n      \"source_value\": \"name\",\n      \"inherited_source_type\": null,\n      \"inherited_source_value\": null,\n      \"inherited_from\": null\n    },\n    {\n      \"name\": \"Brand\",\n      \"label\": \"Brand\",\n      \"usage\": \"required\",\n      \"type\": \"text\",\n      \"cardinality\": 1,\n      \"allowed_values\": null,\n      \"allows_custom_value\": true,\n      \"description\": null,\n      \"category_count\": 0,\n      \"is_core\": true,\n      \"source_type\": null,\n      \"source_value\": null,\n      \"inherited_source_type\": \"product_field\",\n      \"inherited_source_value\": \"brand\",\n      \"inherited_from\": \"channel\"\n    }\n  ],\n  \"message\": \"Profile mappings saved.\"\n}"}]}]},{"name":"Sub Sales Channels","item":[{"name":"List Sub Sales Channels","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sub-sales-channels","host":["{{baseUrl}}"],"path":["api","sub-sales-channels"]},"description":"Returns a paginated list of sub sales channels. Each row carries its classified `source_type` (online, point_of_sale, marketplace, draft, other), a human `source_type_label`, an `is_point_of_sale` convenience flag, and `source_type_locked` (true once an operator has manually reclassified it). Authenticate with a Personal Access Token.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAllowed sorts: id, name, created_at, updated_at."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/sub-sales-channels","host":["{{baseUrl}}"],"path":["api","sub-sales-channels"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"sales_channel_id\": 3,\n      \"name\": \"POS\",\n      \"external_identifier\": \"pos\",\n      \"source_type\": \"point_of_sale\",\n      \"source_type_label\": \"Point of Sale\",\n      \"source_type_locked\": false,\n      \"is_point_of_sale\": true,\n      \"sales_nominal_code_id\": null,\n      \"cogs_nominal_code_id\": null,\n      \"shipping_revenue_nominal_code_id\": null,\n      \"shipping_refund_nominal_code_id\": null,\n      \"sales_returns_allowances_nominal_code_id\": null,\n      \"created_at\": \"2026-06-17T09:15:00.000000Z\",\n      \"updated_at\": \"2026-06-17T09:15:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1,\n  \"path\": \"{{protocol}}{{domain}}/api/sub-sales-channels\"\n}"}]},{"name":"Create Sub Sales Channel","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/sub-sales-channels","host":["{{baseUrl}}"],"path":["api","sub-sales-channels"]},"description":"Creates a new sub sales channel.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPower user access required.\n\nFields:\n- sales_channel_id (required): ID of the parent sales channel\n- name (required): Name of the sub sales channel (max 255 chars)\n- sales_nominal_code_id (optional): Nominal code ID for sales revenue\n- cogs_nominal_code_id (optional): Nominal code ID for cost of goods sold","body":{"mode":"raw","raw":"{\n  \"sales_channel_id\": 5,\n  \"name\": \"Amazon US\",\n  \"sales_nominal_code_id\": 10,\n  \"cogs_nominal_code_id\": 11,\n  \"shipping_revenue_nominal_code_id\": 12,\n  \"shipping_refund_nominal_code_id\": 13,\n  \"sales_returns_allowances_nominal_code_id\": 14\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/sub-sales-channels","host":["{{baseUrl}}"],"path":["api","sub-sales-channels"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Sub Sales Channel created successfully.\",\n  \"data\": {\n    \"id\": 2,\n    \"name\": \"Amazon US\"\n  }\n}"}]},{"name":"Reclassify Source Type","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/sub-sales-channels/{subSalesChannel}/source-type","host":["{{baseUrl}}"],"path":["api","sub-sales-channels",":subSalesChannel","source-type"],"variable":[{"key":"subSalesChannel","value":"12","description":"Sub sales channel ID"}]},"description":"Manually reclassify a sub sales channel's source type (e.g. mark an in-store source as Point of Sale). Sets `source_type_locked = true` so future integration syncs never revert the operator's choice.\n\nRequest body:\n- `source_type` (required) — one of: online, point_of_sale, marketplace, draft, other.\n\nReturns the updated sub sales channel.","body":{"mode":"raw","raw":"{\n  \"source_type\": \"point_of_sale\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/sub-sales-channels/{subSalesChannel}/source-type","host":["{{baseUrl}}"],"path":["api","sub-sales-channels",":subSalesChannel","source-type"],"variable":[{"key":"subSalesChannel","value":"12","description":"Sub sales channel ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"sales_channel_id\": 3,\n    \"name\": \"POS\",\n    \"external_identifier\": \"pos\",\n    \"source_type\": \"point_of_sale\",\n    \"source_type_label\": \"Point of Sale\",\n    \"source_type_locked\": true,\n    \"is_point_of_sale\": true,\n    \"sales_nominal_code_id\": null,\n    \"cogs_nominal_code_id\": null,\n    \"shipping_revenue_nominal_code_id\": null,\n    \"shipping_refund_nominal_code_id\": null,\n    \"sales_returns_allowances_nominal_code_id\": null,\n    \"created_at\": \"2026-06-17T09:15:00.000000Z\",\n    \"updated_at\": \"2026-06-17T09:15:00.000000Z\"\n  },\n  \"message\": \"Sub Sales Channel POS updated successfully.\"\n}"}]},{"name":"Get Sub Sales Channel","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sub-sales-channels/{id}","host":["{{baseUrl}}"],"path":["api","sub-sales-channels",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns a single sub sales channel by ID with all related data loaded.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPower user access required."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/sub-sales-channels/{id}","host":["{{baseUrl}}"],"path":["api","sub-sales-channels",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Amazon US\",\n    \"sales_channel_id\": 5\n  }\n}"}]},{"name":"Update Sub Sales Channel","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/sub-sales-channels/{id}","host":["{{baseUrl}}"],"path":["api","sub-sales-channels",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Updates an existing sub sales channel. All fields are optional (partial update supported).\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPower user access required.","body":{"mode":"raw","raw":"{\n  \"name\": \"Amazon US Updated\",\n  \"sales_nominal_code_id\": 10,\n  \"cogs_nominal_code_id\": 11\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/sub-sales-channels/{id}","host":["{{baseUrl}}"],"path":["api","sub-sales-channels",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Sub Sales Channel 'Amazon US Updated' updated successfully.\",\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Amazon US Updated\"\n  }\n}"}]},{"name":"Delete Sub Sales Channel","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/sub-sales-channels/{id}","host":["{{baseUrl}}"],"path":["api","sub-sales-channels",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Deletes a sub sales channel.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPower user access required."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/sub-sales-channels/{id}","host":["{{baseUrl}}"],"path":["api","sub-sales-channels",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Sub Sales Channel 'Amazon US' deleted successfully.\"\n}"}]},{"name":"Get Sub Sales Channels by Sales Channel","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sub-sales-channels/by-sales-channel/{id}","host":["{{baseUrl}}"],"path":["api","sub-sales-channels","by-sales-channel",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns all sub sales channels that belong to a specific sales channel.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPower user access required."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/sub-sales-channels/by-sales-channel/{id}","host":["{{baseUrl}}"],"path":["api","sub-sales-channels","by-sales-channel",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Amazon US\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Amazon CA\"\n    }\n  ]\n}"}]},{"name":"List Sub Sales Channel Options (id/name)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sub-sales-channels/list","host":["{{baseUrl}}"],"path":["api","sub-sales-channels","list"]},"description":"Returns a minimal list of sub sales channels (id and name only) for use in dropdown/combo box controls. Ordered by name.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPower user access required."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/sub-sales-channels/list","host":["{{baseUrl}}"],"path":["api","sub-sales-channels","list"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Amazon CA\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Amazon US\"\n    }\n  ]\n}"}]},{"name":"Lookup Sales Channels","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sub-sales-channels/lookup/sales-channels","host":["{{baseUrl}}"],"path":["api","sub-sales-channels","lookup","sales-channels"]},"description":"Returns every sales channel, ordered by name, for use in channel dropdowns.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach entry's `name` is the channel's display name: the integration instance name for a connected channel (so two stores on the same integration are listed separately, e.g. \"FS Shopify\" and \"FS Shopify USA\"), \"Customer Portal\" for the B2B portal channel, and \"SKU.io\" for manually created orders. These are the same values the sales order channel name filter matches on.\n\nAuthentication: Requires Bearer token.\n\nPower user access required."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/sub-sales-channels/lookup/sales-channels","host":["{{baseUrl}}"],"path":["api","sub-sales-channels","lookup","sales-channels"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3,\n      \"name\": \"Customer Portal\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"FS Shopify\"\n    },\n    {\n      \"id\": 4,\n      \"name\": \"FS Shopify USA\"\n    },\n    {\n      \"id\": 1,\n      \"name\": \"SKU.io\"\n    }\n  ]\n}"}]},{"name":"Lookup Nominal Codes","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/sub-sales-channels/lookup/nominal-codes","host":["{{baseUrl}}"],"path":["api","sub-sales-channels","lookup","nominal-codes"]},"description":"Returns all nominal codes for use in dropdowns when assigning accounting codes to sub sales channels. Ordered by code.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPower user access required."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/sub-sales-channels/lookup/nominal-codes","host":["{{baseUrl}}"],"path":["api","sub-sales-channels","lookup","nominal-codes"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 10,\n      \"name\": \"Sales Revenue\",\n      \"code\": \"4000\"\n    },\n    {\n      \"id\": 11,\n      \"name\": \"Cost of Goods Sold\",\n      \"code\": \"5000\"\n    }\n  ]\n}"}]}]},{"name":"Custom Report Builder","item":[{"name":"Get Metadata (Domains, Entities, Columns)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/report-builder/metadata","host":["{{baseUrl}}"],"path":["api","v2","report-builder","metadata"]},"description":"Returns all available domains, entities, and columns that can be used to build a report definition. Use this to populate the report builder UI with selectable fields.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe response contains the full schema of reportable data: which domains are available (e.g., inventory, sales), which entities exist within each domain, and which columns (dimensions and measures) can be selected for each entity."}},{"name":"Run Ad-Hoc Report","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/report-builder/run","host":["{{baseUrl}}"],"path":["api","v2","report-builder","run"]},"description":"Run a report definition without saving it. Use this for previewing results before saving a custom report.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe definition object must include:\n- `base_entity` (string, required): The primary entity to query\n- `columns` (array, required, min 1): Columns to include. Each column needs `id`, `kind` (dimension|measure), `entity`, and `field`. Optional: `label`, `granularity`, `aggregation`\n- `filters` (object, optional): Filter tree with `conjunction` (and|or) and `children` array of conditions/groups\n- `sorts` (array, optional): Sort rules with `column_id` and `direction` (asc|desc)\n- `limit` (integer, optional): Max rows (1-5000)\n\nThe top-level `limit` parameter also caps the result set (1-5000).\n\n`raw_sql` is the generated query with its parameter values inlined, ready to use as the starting point of an SQL report.","body":{"mode":"raw","raw":"{\n  \"definition\": {\n    \"base_entity\": \"sales_orders\",\n    \"columns\": [\n      {\n        \"id\": \"sales_orders.order_number\",\n        \"kind\": \"dimension\",\n        \"entity\": \"sales_orders\",\n        \"field\": \"order_number\"\n      },\n      {\n        \"id\": \"sales_orders.total\",\n        \"kind\": \"measure\",\n        \"entity\": \"sales_orders\",\n        \"field\": \"total\",\n        \"aggregation\": \"sum\"\n      }\n    ],\n    \"filters\": {\n      \"conjunction\": \"and\",\n      \"children\": [\n        {\n          \"type\": \"condition\",\n          \"condition\": {\n            \"column\": \"sales_orders.status\",\n            \"operator\": \"=\",\n            \"value\": \"shipped\"\n          }\n        }\n      ]\n    },\n    \"sorts\": [\n      {\n        \"column_id\": \"sales_orders.total\",\n        \"direction\": \"desc\"\n      }\n    ],\n    \"limit\": 100\n  },\n  \"limit\": 100\n}","options":{"raw":{"language":"json"}}}}},{"name":"List Custom Reports","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/custom-reports?page=1&per_page=20","host":["{{baseUrl}}"],"path":["api","v2","custom-reports"],"query":[{"key":"page","value":"1","description":"Page number for pagination","disabled":true},{"key":"per_page","value":"20","description":"Items per page (default: 20)","disabled":true}]},"description":"List all custom reports for the authenticated user. Supports pagination, search by name, and filtering by domain.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`tab` narrows the list: `my_reports` (owned), `shared` (reports with Shared visibility plus reports explicitly shared with you), `templates`, `favorites`.\n\nResponse uses standard Laravel pagination with `data`, `current_page`, `last_page`, `per_page`, `total`, etc. Each item in `data` is a custom report object."}},{"name":"Create Custom Report","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/custom-reports","host":["{{baseUrl}}"],"path":["api","v2","custom-reports"]},"description":"Create a new saved custom report.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequired fields:\n- `name` (string, max 255): Report name\n- `domain` (string, max 50): Domain category (e.g., sales, inventory)\n- `base_entity` (string, max 100): Primary entity for the report\n- `definition` (object): Full report definition containing columns, filters, sorts, etc.\n\nOptional fields:\n- `description` (string, max 1000): Report description\n- `visibility` (string): \"private\" (default) or \"shared\"\n- `definition.version` (integer): Schema version number\n- `definition.domain` (string): Domain within the definition\n- `definition.filters` (object): Filter tree with conjunction and children\n- `definition.sorts` (array): Sort rules\n- `definition.limit` (integer, 1-5000): Default row limit\n\nColumn requirements:\n- `id` (string, required): Unique column identifier (e.g., \"entity.field\")\n- `kind` (string, required): \"dimension\" or \"measure\"\n- `entity` (string, required): Entity the column belongs to\n- `field` (string, required): Field name on the entity\n- `label` (string, optional): Display label\n- `granularity` (string, optional): Time granularity for date dimensions (e.g., day, month, year)\n- `aggregation` (string, optional): Aggregation function for measures (e.g., sum, count, avg)\n\nReturns 201 with the created custom report object.\n\nSQL reports: send `definition` as `{\"version\": 2, \"mode\": \"sql\", \"sql\": \"SELECT ...\", \"limit\": 500}` instead of a builder definition; `base_entity` and `domain` are not needed (the report is stored under the `sql` domain with `mode` = `sql`). The statement is validated before it is saved (errors are returned as 422 on `definition.sql`). Creating or changing an SQL report requires the `reports.sql` permission (403 otherwise).","body":{"mode":"raw","raw":"{\n  \"name\": \"Monthly Sales by Product\",\n  \"description\": \"Aggregated sales totals grouped by product and month\",\n  \"domain\": \"sales\",\n  \"base_entity\": \"sales_orders\",\n  \"definition\": {\n    \"base_entity\": \"sales_orders\",\n    \"domain\": \"sales\",\n    \"version\": 1,\n    \"columns\": [\n      {\n        \"id\": \"sales_orders.order_date\",\n        \"kind\": \"dimension\",\n        \"entity\": \"sales_orders\",\n        \"field\": \"order_date\",\n        \"label\": \"Order Month\",\n        \"granularity\": \"month\"\n      },\n      {\n        \"id\": \"products.name\",\n        \"kind\": \"dimension\",\n        \"entity\": \"products\",\n        \"field\": \"name\",\n        \"label\": \"Product Name\"\n      },\n      {\n        \"id\": \"sales_orders.total\",\n        \"kind\": \"measure\",\n        \"entity\": \"sales_orders\",\n        \"field\": \"total\",\n        \"label\": \"Total Sales\",\n        \"aggregation\": \"sum\"\n      }\n    ],\n    \"filters\": {\n      \"conjunction\": \"and\",\n      \"children\": [\n        {\n          \"type\": \"condition\",\n          \"condition\": {\n            \"column\": \"sales_orders.status\",\n            \"operator\": \"!=\",\n            \"value\": \"cancelled\"\n          }\n        }\n      ]\n    },\n    \"sorts\": [\n      {\n        \"column_id\": \"sales_orders.order_date\",\n        \"direction\": \"desc\"\n      }\n    ],\n    \"limit\": 500\n  },\n  \"visibility\": \"private\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/custom-reports","host":["{{baseUrl}}"],"path":["api","v2","custom-reports"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 42,\n  \"user_id\": 1,\n  \"name\": \"Recent Orders (SQL)\",\n  \"description\": null,\n  \"domain\": \"sql\",\n  \"base_entity\": \"\",\n  \"mode\": \"sql\",\n  \"definition\": {\n    \"version\": 2,\n    \"mode\": \"sql\",\n    \"sql\": \"SELECT sales_order_number, total AS order_total, order_date\\nFROM sales_orders\\nORDER BY id DESC\\nLIMIT 50\",\n    \"limit\": 500\n  },\n  \"visibility\": \"private\",\n  \"is_template\": false,\n  \"is_favorited\": false,\n  \"last_run_at\": null,\n  \"created_at\": \"2026-10-05T10:09:12.000000Z\",\n  \"updated_at\": \"2026-10-05T10:09:12.000000Z\"\n}"}]},{"name":"Get Custom Report","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Get a single custom report by ID. The report must belong to or be shared with the authenticated user.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the full custom report object including the user name (eager-loaded). Returns 404 if the report is not found or not accessible."}},{"name":"Update Custom Report","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Update an existing custom report. The report owner, or a user the report was shared with `edit` permission, can update it (returns 403 otherwise). Only the owner can change `visibility` — it is ignored when an edit-share recipient sends it.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAll fields are optional (uses `sometimes` validation). When `definition` is provided, `definition.base_entity` and `definition.columns` become required.\n\nReturns 404 if the report is not found or not accessible. Returns 403 if the authenticated user is neither the owner nor an edit-share recipient.\n\nSQL reports: send `definition` as `{\"version\": 2, \"mode\": \"sql\", \"sql\": \"SELECT ...\", \"limit\": 500}` instead of a builder definition; `base_entity` and `domain` are not needed (the report is stored under the `sql` domain with `mode` = `sql`). The statement is validated before it is saved (errors are returned as 422 on `definition.sql`). Creating or changing an SQL report requires the `reports.sql` permission (403 otherwise). This also applies to edit-share recipients: without the permission they can rename an SQL report but not change its statement.","body":{"mode":"raw","raw":"{\n  \"name\": \"Monthly Sales by Product (Updated)\",\n  \"description\": \"Updated description for monthly sales report\",\n  \"definition\": {\n    \"base_entity\": \"sales_orders\",\n    \"domain\": \"sales\",\n    \"version\": 1,\n    \"columns\": [\n      {\n        \"id\": \"sales_orders.order_date\",\n        \"kind\": \"dimension\",\n        \"entity\": \"sales_orders\",\n        \"field\": \"order_date\",\n        \"label\": \"Order Month\",\n        \"granularity\": \"month\"\n      },\n      {\n        \"id\": \"products.sku\",\n        \"kind\": \"dimension\",\n        \"entity\": \"products\",\n        \"field\": \"sku\",\n        \"label\": \"SKU\"\n      },\n      {\n        \"id\": \"products.name\",\n        \"kind\": \"dimension\",\n        \"entity\": \"products\",\n        \"field\": \"name\",\n        \"label\": \"Product Name\"\n      },\n      {\n        \"id\": \"sales_orders.total\",\n        \"kind\": \"measure\",\n        \"entity\": \"sales_orders\",\n        \"field\": \"total\",\n        \"label\": \"Total Sales\",\n        \"aggregation\": \"sum\"\n      },\n      {\n        \"id\": \"sales_orders.id\",\n        \"kind\": \"measure\",\n        \"entity\": \"sales_orders\",\n        \"field\": \"id\",\n        \"label\": \"Order Count\",\n        \"aggregation\": \"count\"\n      }\n    ],\n    \"filters\": {\n      \"conjunction\": \"and\",\n      \"children\": [\n        {\n          \"type\": \"condition\",\n          \"condition\": {\n            \"column\": \"sales_orders.status\",\n            \"operator\": \"!=\",\n            \"value\": \"cancelled\"\n          }\n        }\n      ]\n    },\n    \"sorts\": [\n      {\n        \"column_id\": \"sales_orders.order_date\",\n        \"direction\": \"desc\"\n      }\n    ],\n    \"limit\": 1000\n  },\n  \"visibility\": \"shared\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 42,\n  \"user_id\": 1,\n  \"name\": \"Recent Orders (SQL)\",\n  \"description\": null,\n  \"domain\": \"sql\",\n  \"base_entity\": \"\",\n  \"mode\": \"sql\",\n  \"definition\": {\n    \"version\": 2,\n    \"mode\": \"sql\",\n    \"sql\": \"SELECT sales_order_number, total AS order_total, order_date\\nFROM sales_orders\\nORDER BY id DESC\\nLIMIT 50\",\n    \"limit\": 500\n  },\n  \"visibility\": \"private\",\n  \"is_template\": false,\n  \"is_favorited\": false,\n  \"last_run_at\": null,\n  \"created_at\": \"2026-10-05T10:09:12.000000Z\",\n  \"updated_at\": \"2026-10-05T10:09:12.000000Z\"\n}"}]},{"name":"Delete Custom Report","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Delete a custom report. Only the report owner can delete it (returns 403 otherwise). Also deletes associated shares.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns 204 No Content on success. Returns 404 if not found. Returns 403 if not the owner."}},{"name":"Run Saved Report","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}/run","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id","run"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Execute a saved custom report by ID. The report must belong to or be shared with the authenticated user.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nOptional body parameter:\n- `limit` (integer, default 500): Maximum number of rows to return\n\nReturns 404 if the report is not found. Returns 422 if the report definition is invalid (e.g., references unknown entities or columns).\n\nFor an SQL report (`mode` = `sql`) the response has the SQL result shape: `columns` (name, type), `rows`, `row_count`, `truncated`, `duration_ms`, `warnings`, `sql_executed`. Anyone who can see the report may run it; the statement is re-validated on every run and executes under the same read-only safeguards as Run SQL Statement.\n\nSQL-mode reports are rate limited per user: by default 30 runs and exports combined per minute. Over the limit the endpoint returns 429 with a `Retry-After` header (seconds). Builder-mode reports are not affected.","body":{"mode":"raw","raw":"{\n  \"limit\": 500\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}/run","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id","run"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"mode\": \"sql\",\n  \"columns\": [\n    {\n      \"name\": \"sales_order_number\",\n      \"type\": \"string\",\n      \"native_type\": \"VAR_STRING\"\n    },\n    {\n      \"name\": \"order_total\",\n      \"type\": \"number\",\n      \"native_type\": \"NEWDECIMAL\"\n    },\n    {\n      \"name\": \"order_date\",\n      \"type\": \"datetime\",\n      \"native_type\": \"DATETIME\"\n    }\n  ],\n  \"rows\": [\n    {\n      \"sales_order_number\": \"SO-1042\",\n      \"order_total\": \"1240.00\",\n      \"order_date\": \"2026-09-30 14:22:05\"\n    },\n    {\n      \"sales_order_number\": \"SO-1041\",\n      \"order_total\": \"980.50\",\n      \"order_date\": \"2026-09-30 11:03:47\"\n    }\n  ],\n  \"row_count\": 2,\n  \"truncated\": false,\n  \"cap\": 500,\n  \"duration_ms\": 18,\n  \"took_ms\": 18,\n  \"estimated_rows\": 1450,\n  \"warnings\": [],\n  \"sql_executed\": \"SELECT * FROM (\\nSELECT sales_order_number, total AS order_total, order_date FROM sales_orders ORDER BY id DESC LIMIT 2\\n) AS sql_report LIMIT 501\"\n}"}]},{"name":"Validate SQL Statement","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/report-builder/sql/validate","host":["{{baseUrl}}"],"path":["api","v2","report-builder","sql","validate"]},"description":"Check a SQL statement without running it. Returns whether it is valid, any errors (which block running) and warnings (which do not, e.g. a missing LIMIT), the allowlisted tables it reads, and the database's estimate of rows it will scan.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEvery statement passes the same safeguards: it must be a single read-only SELECT (CTEs, UNIONs, subqueries and window functions are supported); every table it reads must be on the SQL-reports allowlist (see Get SQL Schema); schema-qualified references, variables, placeholders, locking clauses, SELECT ... INTO, stored routines and a small set of unsafe functions (SLEEP, BENCHMARK, LOAD_FILE, GET_LOCK, sequence functions, ...) and server-information functions (DATABASE(), USER(), CURRENT_USER, VERSION(), CONNECTION_ID(), ...) are rejected. A few columns that hold access tokens (`purchase_orders.share_token`, `purchase_invoices.share_token`) can never be read: naming them anywhere is rejected, and so is `SELECT *` / `alias.*` on a table that has one — list the columns you need instead. Statements run in a read-only transaction with a per-statement time limit and a row cap, and every execution is recorded in an audit log.\n\nRequires the `reports.sql` permission (administrators always have it). Personal access tokens need the `reports:write` scope (`reports:read` is enough for Get SQL Schema). Users without the permission receive 403.\n\nRate limited to 120 requests per minute per user.\n\nBody:\n- `sql` (string, required, max 20000): the statement","body":{"mode":"raw","raw":"{\n  \"sql\": \"SELECT sales_order_number, total AS order_total, order_date\\nFROM sales_orders\\nORDER BY id DESC\\nLIMIT 50\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/report-builder/sql/validate","host":["{{baseUrl}}"],"path":["api","v2","report-builder","sql","validate"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"valid\": true,\n  \"errors\": [],\n  \"warnings\": [],\n  \"referenced_tables\": [\n    \"sales_orders\"\n  ],\n  \"estimated_rows\": 1450\n}"}]},{"name":"Run SQL Statement","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/report-builder/sql/run","host":["{{baseUrl}}"],"path":["api","v2","report-builder","sql","run"]},"description":"Run a SQL statement and return preview rows.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEvery statement passes the same safeguards: it must be a single read-only SELECT (CTEs, UNIONs, subqueries and window functions are supported); every table it reads must be on the SQL-reports allowlist (see Get SQL Schema); schema-qualified references, variables, placeholders, locking clauses, SELECT ... INTO, stored routines and a small set of unsafe functions (SLEEP, BENCHMARK, LOAD_FILE, GET_LOCK, sequence functions, ...) and server-information functions (DATABASE(), USER(), CURRENT_USER, VERSION(), CONNECTION_ID(), ...) are rejected. A few columns that hold access tokens (`purchase_orders.share_token`, `purchase_invoices.share_token`) can never be read: naming them anywhere is rejected, and so is `SELECT *` / `alias.*` on a table that has one — list the columns you need instead. Statements run in a read-only transaction with a per-statement time limit and a row cap, and every execution is recorded in an audit log.\n\nColumns are described from the result set, so they are returned even when there are no rows. Date-time values are returned as stored (UTC). `truncated` is true when more rows exist than the cap; export to get them all. `sql_executed` is the exact statement run, including the row-cap wrapper.\n\nWhen the database estimates the statement will scan more than 500,000 rows it is queued as a background job instead: the response is 202 with a `tracked_job_log_id`, and the finished CSV is available from that background job's results.\n\nRequires the `reports.sql` permission (administrators always have it). Personal access tokens need the `reports:write` scope (`reports:read` is enough for Get SQL Schema). Users without the permission receive 403.\n\nRate limited to 30 runs per minute per user.\n\nBody:\n- `sql` (string, required, max 20000): the statement\n- `limit` (integer, optional, 1-5000, default 500): preview row cap","body":{"mode":"raw","raw":"{\n  \"sql\": \"SELECT sales_order_number, total AS order_total, order_date\\nFROM sales_orders\\nORDER BY id DESC\\nLIMIT 50\",\n  \"limit\": 500\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/report-builder/sql/run","host":["{{baseUrl}}"],"path":["api","v2","report-builder","sql","run"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"mode\": \"sql\",\n  \"columns\": [\n    {\n      \"name\": \"sales_order_number\",\n      \"type\": \"string\",\n      \"native_type\": \"VAR_STRING\"\n    },\n    {\n      \"name\": \"order_total\",\n      \"type\": \"number\",\n      \"native_type\": \"NEWDECIMAL\"\n    },\n    {\n      \"name\": \"order_date\",\n      \"type\": \"datetime\",\n      \"native_type\": \"DATETIME\"\n    }\n  ],\n  \"rows\": [\n    {\n      \"sales_order_number\": \"SO-1042\",\n      \"order_total\": \"1240.00\",\n      \"order_date\": \"2026-09-30 14:22:05\"\n    },\n    {\n      \"sales_order_number\": \"SO-1041\",\n      \"order_total\": \"980.50\",\n      \"order_date\": \"2026-09-30 11:03:47\"\n    }\n  ],\n  \"row_count\": 2,\n  \"truncated\": false,\n  \"cap\": 500,\n  \"duration_ms\": 18,\n  \"took_ms\": 18,\n  \"estimated_rows\": 1450,\n  \"warnings\": [],\n  \"sql_executed\": \"SELECT * FROM (\\nSELECT sales_order_number, total AS order_total, order_date FROM sales_orders ORDER BY id DESC LIMIT 2\\n) AS sql_report LIMIT 501\"\n}"}]},{"name":"Get SQL Schema","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/report-builder/sql/schema","host":["{{baseUrl}}"],"path":["api","v2","report-builder","sql","schema"]},"description":"List every table a SQL report may read, with its columns and types. Tables that back a report-builder entity include that entity's key and label. Only allowlisted tables are listed.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequires the `reports.sql` permission (administrators always have it). Personal access tokens need the `reports:write` scope (`reports:read` is enough for Get SQL Schema). Users without the permission receive 403."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/report-builder/sql/schema","host":["{{baseUrl}}"],"path":["api","v2","report-builder","sql","schema"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"tables\": [\n    {\n      \"name\": \"sales_orders\",\n      \"entity_key\": \"sales_orders\",\n      \"entity_label\": \"Sales Orders\",\n      \"columns\": [\n        {\n          \"name\": \"id\",\n          \"type\": \"bigint(20) unsigned\",\n          \"nullable\": false\n        },\n        {\n          \"name\": \"sales_order_number\",\n          \"type\": \"varchar(255)\",\n          \"nullable\": false\n        },\n        {\n          \"name\": \"order_date\",\n          \"type\": \"datetime\",\n          \"nullable\": true\n        }\n      ]\n    },\n    {\n      \"name\": \"ledger_journal_lines\",\n      \"entity_key\": null,\n      \"entity_label\": null,\n      \"columns\": [\n        {\n          \"name\": \"id\",\n          \"type\": \"bigint(20) unsigned\",\n          \"nullable\": false\n        },\n        {\n          \"name\": \"debit\",\n          \"type\": \"decimal(20,4)\",\n          \"nullable\": false\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"Export SQL Statement Results","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/report-builder/sql/export","host":["{{baseUrl}}"],"path":["api","v2","report-builder","sql","export"]},"description":"Run a SQL statement and export its results.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n- `csv`: returned as a file download (`text/csv`), up to 100,000 rows, with a longer time limit than previews.\n- `xlsx` / `pdf`: returned as JSON with a base64 `content` field, up to 10,000 rows.\n\nEvery statement passes the same safeguards: it must be a single read-only SELECT (CTEs, UNIONs, subqueries and window functions are supported); every table it reads must be on the SQL-reports allowlist (see Get SQL Schema); schema-qualified references, variables, placeholders, locking clauses, SELECT ... INTO, stored routines and a small set of unsafe functions (SLEEP, BENCHMARK, LOAD_FILE, GET_LOCK, sequence functions, ...) and server-information functions (DATABASE(), USER(), CURRENT_USER, VERSION(), CONNECTION_ID(), ...) are rejected. A few columns that hold access tokens (`purchase_orders.share_token`, `purchase_invoices.share_token`) can never be read: naming them anywhere is rejected, and so is `SELECT *` / `alias.*` on a table that has one — list the columns you need instead. Statements run in a read-only transaction with a per-statement time limit and a row cap, and every execution is recorded in an audit log.\n\nRequires the `reports.sql` permission (administrators always have it). Personal access tokens need the `reports:write` scope (`reports:read` is enough for Get SQL Schema). Users without the permission receive 403.\n\nBody:\n- `sql` (string, required, max 20000): the statement\n- `format` (string, required): `csv`, `xlsx` or `pdf`\n- `name` (string, optional, max 255): file name prefix","body":{"mode":"raw","raw":"{\n  \"sql\": \"SELECT sales_order_number, total AS order_total, order_date\\nFROM sales_orders\\nORDER BY id DESC\\nLIMIT 50\",\n  \"format\": \"xlsx\",\n  \"name\": \"Recent Orders\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/report-builder/sql/export","host":["{{baseUrl}}"],"path":["api","v2","report-builder","sql","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"filename\": \"Recent_Orders-2026-10-05.xlsx\",\n  \"content\": \"UEsDBBQABgAIAAAAIQBi7p1oXgEAAJAEAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIooAAC...\",\n  \"mime_type\": \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\"\n}"}]},{"name":"Drill Down Into Aggregated Row","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/report-builder/drill-down","host":["{{baseUrl}}"],"path":["api","v2","report-builder","drill-down"]},"description":"Execute a drill-down query for a row in an aggregated report. Pass the same `definition` used to produce the parent row along with a `drill_values` map of {dimension_alias: value} pairs to isolate the underlying detail records.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns rows from the base entity that match all supplied drill values, respecting the definition's filters and tenant isolation. Use for powering drill-down drawers in the UI.","body":{"mode":"raw","raw":"{\n  \"definition\": {\n    \"base_entity\": \"sales_orders\",\n    \"columns\": [\n      {\n        \"id\": \"sales_orders.order_status\",\n        \"kind\": \"dimension\",\n        \"entity\": \"sales_orders\",\n        \"field\": \"order_status\"\n      },\n      {\n        \"id\": \"sales_orders.id\",\n        \"kind\": \"measure\",\n        \"entity\": \"sales_orders\",\n        \"field\": \"id\",\n        \"aggregation\": \"count\"\n      }\n    ]\n  },\n  \"drill_values\": {\n    \"sales_orders__order_status\": \"shipped\"\n  },\n  \"limit\": 100\n}","options":{"raw":{"language":"json"}}}}},{"name":"Validate Formula Expression","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/report-builder/validate-formula","host":["{{baseUrl}}"],"path":["api","v2","report-builder","validate-formula"]},"description":"Validate a user-entered formula expression before saving. The formula is parsed via Symfony ExpressionLanguage against a whitelist of functions (IF, COALESCE, ROUND, NULLIF, etc.) and field references (`{field_name}`).\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest body:\n- `formula` (string, required, max 2000): The expression to validate\n- `base_entity` (string, optional): Entity context for field reference resolution\n- `available_columns` (array, optional): Available columns in the current report for reference validation\n\nReturns `{ valid: true }` or `{ valid: false, error: '...' }`. Rejects SQL-injection attempts and unknown field references.","body":{"mode":"raw","raw":"{\n  \"formula\": \"{revenue} / NULLIF({units_sold}, 0)\",\n  \"base_entity\": \"sales_order_lines\",\n  \"available_columns\": [\n    {\n      \"id\": \"sales_order_lines.revenue\",\n      \"kind\": \"measure\"\n    },\n    {\n      \"id\": \"sales_order_lines.units_sold\",\n      \"kind\": \"measure\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}}},{"name":"Get Distinct Field Values (Autocomplete)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/report-builder/field-values/sales_orders/order_status?search=&limit=50","host":["{{baseUrl}}"],"path":["api","v2","report-builder","field-values","sales_orders","order_status"],"query":[{"key":"search","value":"","description":"Optional search term for autocomplete","disabled":true},{"key":"limit","value":"50","description":"Max distinct values to return (default 50)","disabled":true}]},"description":"Return distinct values for a given entity/field. Used to populate autocomplete dropdowns in the filter builder UI (e.g., distinct `order_status` values).\n\nRespects tenant isolation. Supports an optional `search` query param for LIKE matching and a `limit` param to cap results."}},{"name":"Search Share Recipients","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/report-builder/users?search=alice","host":["{{baseUrl}}"],"path":["api","v2","report-builder","users"],"query":[{"key":"search","value":"alice","description":"Search by name or email","disabled":true}]},"description":"Search for users that can be added as share recipients on a custom report. Limited to users in the same tenant. Returns up to 20 matches. Used to power the share dialog's user picker.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."}},{"name":"Duplicate Custom Report","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}/duplicate","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id","duplicate"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Create a personal copy of a report. The duplicate is always owned by the authenticated caller with visibility='private' regardless of the original's visibility. Works on shared reports too (lets a user fork a shared report into their own workspace).\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest body:\n- `name` (string, required, max 255): Name for the new copy\n\nReturns 201 with the new custom report object.","body":{"mode":"raw","raw":"{\n  \"name\": \"My Copy of Monthly Sales\"\n}","options":{"raw":{"language":"json"}}}}},{"name":"Toggle Favorite","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}/toggle-favorite","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id","toggle-favorite"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Toggle the `is_favorited` flag on a report. Only the owner can toggle favorites on their own report (returns 403 otherwise). The Favorites tab on the Reports Hub filters by this flag.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."}},{"name":"Export Custom Report","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}/export","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id","export"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Export a saved custom report. The response is a streamed/binary download in the requested format.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest body:\n- `format` (string, required): One of `csv`, `xlsx`, `pdf`\n\nReturns 200 with a file download. Returns 404 if the report is not owned by or shared with the caller.\n\nSQL-mode reports are rate limited per user: by default 30 runs and exports combined per minute. Over the limit the endpoint returns 429 with a `Retry-After` header (seconds). Builder-mode reports are not affected.","body":{"mode":"raw","raw":"{\n  \"format\": \"xlsx\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Unauthenticated — the bearer token is missing, revoked, expired, or malformed. Never retry automatically; fix the credential. See the [Errors guide](https://developer.sku.io/docs/guides/errors).","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}/export","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id","export"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"Unauthenticated — the bearer token is missing, revoked, expired, or malformed. Never retry automatically; fix the credential. See the [Errors guide](https://developer.sku.io/docs/guides/errors).","code":401,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Unauthenticated.\"\n}"}]},{"name":"List Shares","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}/shares","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id","shares"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"List all explicit share grants for a report. Each entry includes the recipient user (eager-loaded) and the permission (`view` or `edit`). Returns 403 if the caller is not the report owner.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."}},{"name":"Add Share","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}/shares","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id","shares"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Add a user as an explicit share recipient. Upserts (updateOrCreate) so calling twice with a different permission updates the existing record.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest body:\n- `user_id` (integer, required): Recipient user ID (cannot be self)\n- `permission` (string, required): `view` or `edit`\n\nReturns 403 if the caller is not the report owner. Returns 422 if sharing with self or invalid permission.\n\n`user_id` must be a user who belongs to this account (the same users returned by the user search); any other id returns 422.","body":{"mode":"raw","raw":"{\n  \"user_id\": 2,\n  \"permission\": \"view\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Unauthenticated — the bearer token is missing, revoked, expired, or malformed. Never retry automatically; fix the credential. See the [Errors guide](https://developer.sku.io/docs/guides/errors).","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}/shares","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id","shares"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"Unauthenticated — the bearer token is missing, revoked, expired, or malformed. Never retry automatically; fix the credential. See the [Errors guide](https://developer.sku.io/docs/guides/errors).","code":401,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Unauthenticated.\"\n}"}]},{"name":"Remove Share","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}/shares/{id2}","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id","shares",":id2"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]},"description":"Revoke an explicit share grant for a specific user. Returns 204 on success. Returns 403 if the caller is not the report owner.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."}},{"name":"List Schedules","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}/schedules","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id","schedules"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"List all schedules for a report. Each entry includes the creating user (eager-loaded), frequency, time_of_day (H:i:s), timezone, export_format, recipients, is_active, last_sent_at, and next_run_at. Accessible to anyone who can read the report.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."}},{"name":"Create Schedule","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}/schedules","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id","schedules"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Create a new schedule.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequired fields:\n- `frequency` (string): `daily`, `weekly`, or `monthly`\n- `time_of_day` (string): `H:i` or `H:i:s`\n- `recipients` (array of valid emails, min 1)\n\nOptional fields:\n- `day_of_week` (0-6) when frequency is `weekly`\n- `day_of_month` (1-31) when frequency is `monthly`\n- `timezone` (string, default `UTC`)\n- `export_format` (`csv`, `xlsx`, `pdf`, default `csv`)\n- `is_active` (boolean, default true)\n\nReturns 201 with the schedule resource. Returns 403 if the caller is not the report owner.","body":{"mode":"raw","raw":"{\n  \"frequency\": \"weekly\",\n  \"day_of_week\": 1,\n  \"time_of_day\": \"09:00\",\n  \"timezone\": \"America/New_York\",\n  \"export_format\": \"xlsx\",\n  \"recipients\": [\n    \"alice@example.com\",\n    \"bob@example.com\"\n  ],\n  \"is_active\": true\n}","options":{"raw":{"language":"json"}}}}},{"name":"Update Schedule","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}/schedules/{id2}","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id","schedules",":id2"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]},"description":"Update an existing schedule. All fields are optional (`sometimes` validation). `time_of_day` accepts both `H:i` and `H:i:s`; the server round-trips the value as `H:i:s` on reads, so re-submitting an unchanged time_of_day value from a prior GET works without 422 errors.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns 404 if the schedule does not belong to the URL's report. Returns 403 if the caller is not the report owner.","body":{"mode":"raw","raw":"{\n  \"time_of_day\": \"10:30:00\",\n  \"is_active\": false\n}","options":{"raw":{"language":"json"}}}}},{"name":"Delete Schedule","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/custom-reports/{id}/schedules/{id2}","host":["{{baseUrl}}"],"path":["api","v2","custom-reports",":id","schedules",":id2"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]},"description":"Delete a schedule. Returns 204 on success. Returns 404 if the schedule does not belong to the URL's report. Returns 403 if the caller is not the report owner.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."}}]},{"name":"Gift Cards","item":[{"name":"List Gift Cards","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/gift-cards","host":["{{baseUrl}}"],"path":["api","gift-cards"]},"description":"Paginated list of gift cards with their computed balance (N+1-safe). Filterable by status/customer/digital and searchable by code or recipient. Requires pos.operate.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token (PAT)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/gift-cards","host":["{{baseUrl}}"],"path":["api","gift-cards"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 91,\n      \"code\": \"GC-4821-KDical-0097\",\n      \"status\": \"active\",\n      \"status_label\": \"Active\",\n      \"balance\": 42.5,\n      \"currency_id\": 1,\n      \"customer_id\": 88,\n      \"is_digital\": true,\n      \"recipient_name\": \"Ada Lovelace\",\n      \"recipient_email\": \"ada@example.com\",\n      \"expires_at\": \"2027-07-06T00:00:00+00:00\",\n      \"activated_at\": \"2026-07-06T14:30:00+00:00\",\n      \"created_at\": \"2026-07-06T14:30:00+00:00\"\n    }\n  ],\n  \"per_page\": 15,\n  \"total\": 1,\n  \"last_page\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Issue Gift Card","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/gift-cards","host":["{{baseUrl}}"],"path":["api","gift-cards"]},"description":"Issue (create + fund) a new gift card. The initial `amount` is written as an activation ledger entry and becomes the card's opening balance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\nPermission: `pos.manage`.\n\nRequest body fields:\n- `amount` (numeric, required, > 0) — the amount to fund the card with. Error message when not > 0: \"The gift card amount must be greater than zero.\"\n- `code` (string, nullable, max 255, unique in `gift_cards.code`) — explicit card code; auto-generated (unique) when omitted. Error message on collision: \"That gift card code is already in use.\"\n- `security_code` (string, nullable, max 255) — optional PIN / security code.\n- `is_digital` (boolean, nullable) — whether the card is digital (emailed) vs a physical card. Defaults to false.\n- `customer_id` (integer, nullable, must exist in `customers`) — owning customer.\n- `recipient_name` (string, nullable, max 255) — gift recipient's name.\n- `recipient_email` (email, nullable, max 255) — gift recipient's email.\n- `expires_at` (date, nullable) — expiry date.\n- `notes` (string, nullable, max 2000) — internal note.\n\nReturns 201 with `{ data: <gift card>, balance: <float>, message: \"Gift card issued.\" }`.","body":{"mode":"raw","raw":"{\n  \"amount\": 50,\n  \"code\": \"GC-7F3A9C2E4B10\",\n  \"security_code\": \"4821\",\n  \"is_digital\": true,\n  \"customer_id\": 842,\n  \"recipient_name\": \"Alex Rivera\",\n  \"recipient_email\": \"alex.rivera@example.com\",\n  \"expires_at\": \"2027-07-06\",\n  \"notes\": \"Birthday gift card issued at register.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/gift-cards","host":["{{baseUrl}}"],"path":["api","gift-cards"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 15,\n    \"code\": \"GC-7F3A9C2E4B10\",\n    \"status\": \"active\",\n    \"status_label\": \"Active\",\n    \"balance\": 50,\n    \"currency_id\": 1,\n    \"customer_id\": 842,\n    \"is_digital\": true,\n    \"recipient_name\": \"Alex Rivera\",\n    \"recipient_email\": \"alex.rivera@example.com\",\n    \"expires_at\": \"2027-07-06T00:00:00+00:00\",\n    \"activated_at\": \"2026-07-06T14:30:00+00:00\",\n    \"created_at\": \"2026-07-06T14:30:00+00:00\"\n  },\n  \"balance\": 50,\n  \"message\": \"Gift card issued.\"\n}"}]},{"name":"Lookup Gift Card by Code","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/gift-cards/lookup?code=GC-7F3A9C2E4B10","host":["{{baseUrl}}"],"path":["api","gift-cards","lookup"],"query":[{"key":"code","value":"GC-7F3A9C2E4B10","description":"Required. The gift card code as entered at the register (string, max 255).","disabled":true}]},"description":"Resolve a gift card by its code (POS entry) and return it alongside its current balance. Use this when a customer presents a card at checkout.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\nPermission: `pos.operate`.\n\nQuery parameters:\n- `code` (string, required, max 255) — the gift card code.\n\nReturns 200 with `{ data: <gift card>, balance: <float> }`, or 404 `{ message }` when no card matches the code."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/gift-cards/lookup?code=GC-7F3A9C2E4B10","host":["{{baseUrl}}"],"path":["api","gift-cards","lookup"],"query":[{"key":"code","value":"GC-7F3A9C2E4B10","description":"Required. The gift card code as entered at the register (string, max 255).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 15,\n    \"code\": \"GC-7F3A9C2E4B10\",\n    \"status\": \"active\",\n    \"status_label\": \"Active\",\n    \"balance\": 50,\n    \"currency_id\": 1,\n    \"customer_id\": 842,\n    \"is_digital\": true,\n    \"recipient_name\": \"Alex Rivera\",\n    \"recipient_email\": \"alex.rivera@example.com\",\n    \"expires_at\": \"2027-07-06T00:00:00+00:00\",\n    \"activated_at\": \"2026-07-06T14:30:00+00:00\",\n    \"created_at\": \"2026-07-06T14:30:00+00:00\"\n  },\n  \"balance\": 50\n}"}]},{"name":"Get Gift Card","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/gift-cards/{giftCard}","host":["{{baseUrl}}"],"path":["api","gift-cards",":giftCard"],"variable":[{"key":"giftCard","value":"15","description":"The gift card id."}]},"description":"Show a single gift card by id, including its current balance and lifecycle status.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\nPermission: `pos.operate`.\n\nStatus values: `active`, `inactive`, `redeemed`, `expired`, `voided`.\n\nReturns 200 with `{ data: <gift card> }`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/gift-cards/{giftCard}","host":["{{baseUrl}}"],"path":["api","gift-cards",":giftCard"],"variable":[{"key":"giftCard","value":"15","description":"The gift card id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 15,\n    \"code\": \"GC-7F3A9C2E4B10\",\n    \"status\": \"active\",\n    \"status_label\": \"Active\",\n    \"balance\": 50,\n    \"currency_id\": 1,\n    \"customer_id\": 842,\n    \"is_digital\": true,\n    \"recipient_name\": \"Alex Rivera\",\n    \"recipient_email\": \"alex.rivera@example.com\",\n    \"expires_at\": \"2027-07-06T00:00:00+00:00\",\n    \"activated_at\": \"2026-07-06T14:30:00+00:00\",\n    \"created_at\": \"2026-07-06T14:30:00+00:00\"\n  }\n}"}]},{"name":"Get Gift Card History","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/gift-cards/{giftCard}/history","host":["{{baseUrl}}"],"path":["api","gift-cards",":giftCard","history"],"variable":[{"key":"giftCard","value":"15","description":"The gift card id."}]},"description":"Paginated ledger history for a gift card, newest first. Each entry records a value movement (activation, reload, redemption, refund, adjustment, or void), the resulting balance, and who created it.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\nPermission: `pos.operate`.\n\nEntry `type` values: `activation`, `reload`, `redemption`, `refund`, `adjustment`, `void`. `is_credit` is true when the entry adds value.\n\nPagination: `per_page` (default 10), `page` (default 1). Response includes standard Laravel pagination metadata."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/gift-cards/{giftCard}/history","host":["{{baseUrl}}"],"path":["api","gift-cards",":giftCard","history"],"variable":[{"key":"giftCard","value":"15","description":"The gift card id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 88,\n      \"gift_card_id\": 15,\n      \"reference\": \"GC-TXN-000088\",\n      \"amount\": 25,\n      \"balance_after\": 75,\n      \"type\": \"reload\",\n      \"type_label\": \"Reload\",\n      \"is_credit\": true,\n      \"currency_id\": 1,\n      \"notes\": \"Top-up at register.\",\n      \"source_type\": null,\n      \"source_id\": null,\n      \"created_by_user_id\": 7,\n      \"created_by_name\": \"Jane Cashier\",\n      \"created_at\": \"2026-07-06T15:10:00+00:00\"\n    },\n    {\n      \"id\": 84,\n      \"gift_card_id\": 15,\n      \"reference\": \"GC-TXN-000084\",\n      \"amount\": -20,\n      \"balance_after\": 50,\n      \"type\": \"redemption\",\n      \"type_label\": \"Redemption\",\n      \"is_credit\": false,\n      \"currency_id\": 1,\n      \"notes\": null,\n      \"source_type\": \"App\\\\Models\\\\PosTransaction\",\n      \"source_id\": 1001,\n      \"created_by_user_id\": 7,\n      \"created_by_name\": \"Jane Cashier\",\n      \"created_at\": \"2026-07-06T14:45:00+00:00\"\n    },\n    {\n      \"id\": 80,\n      \"gift_card_id\": 15,\n      \"reference\": \"GC-TXN-000080\",\n      \"amount\": 50,\n      \"balance_after\": 70,\n      \"type\": \"activation\",\n      \"type_label\": \"Activation\",\n      \"is_credit\": true,\n      \"currency_id\": 1,\n      \"notes\": \"Birthday gift card issued at register.\",\n      \"source_type\": null,\n      \"source_id\": null,\n      \"created_by_user_id\": 7,\n      \"created_by_name\": \"Jane Cashier\",\n      \"created_at\": \"2026-07-06T14:30:00+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/gift-cards/15/history?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/gift-cards/15/history?page=1\",\n  \"next_page_url\": null,\n  \"path\": \"{{protocol}}{{domain}}/api/gift-cards/15/history\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 3,\n  \"total\": 3\n}"}]},{"name":"Reload Gift Card","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/gift-cards/{giftCard}/reload","host":["{{baseUrl}}"],"path":["api","gift-cards",":giftCard","reload"],"variable":[{"key":"giftCard","value":"15","description":"The gift card id to add value to."}]},"description":"Add value to an existing gift card. Writes a reload ledger entry and returns it with the card's new balance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\nPermission: `pos.manage`.\n\nRequest body fields:\n- `amount` (numeric, required, > 0) — the amount to add. Error message when not > 0: \"The reload amount must be greater than zero.\"\n- `notes` (string, nullable, max 2000) — internal note.\n- `reference` (string, nullable, max 255) — external reference for the reload (e.g. a receipt or payment reference).\n\nReturns 201 with `{ data: <ledger entry>, balance: <float>, message: \"Gift card reloaded.\" }`.","body":{"mode":"raw","raw":"{\n  \"amount\": 25,\n  \"notes\": \"Top-up at register.\",\n  \"reference\": \"POS-RELOAD-1042\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/gift-cards/{giftCard}/reload","host":["{{baseUrl}}"],"path":["api","gift-cards",":giftCard","reload"],"variable":[{"key":"giftCard","value":"15","description":"The gift card id to add value to."}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 90,\n    \"gift_card_id\": 15,\n    \"reference\": \"POS-RELOAD-1042\",\n    \"amount\": 25,\n    \"balance_after\": 75,\n    \"type\": \"reload\",\n    \"type_label\": \"Reload\",\n    \"is_credit\": true,\n    \"currency_id\": 1,\n    \"notes\": \"Top-up at register.\",\n    \"source_type\": null,\n    \"source_id\": null,\n    \"created_by_user_id\": 7,\n    \"created_by_name\": \"Jane Cashier\",\n    \"created_at\": \"2026-07-06T15:10:00+00:00\"\n  },\n  \"balance\": 75,\n  \"message\": \"Gift card reloaded.\"\n}"}]},{"name":"Void Gift Card","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/gift-cards/{giftCard}/void","host":["{{baseUrl}}"],"path":["api","gift-cards",":giftCard","void"],"variable":[{"key":"giftCard","value":"15","description":"The gift card id to void."}]},"description":"Void a gift card. Drains any remaining balance (via a void ledger entry) and marks the card `voided` so it can no longer be redeemed. No request body.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\nPermission: `pos.manage`.\n\nReturns 200 with `{ data: <gift card>, message: \"Gift card voided.\" }`."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/gift-cards/{giftCard}/void","host":["{{baseUrl}}"],"path":["api","gift-cards",":giftCard","void"],"variable":[{"key":"giftCard","value":"15","description":"The gift card id to void."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 15,\n    \"code\": \"GC-7F3A9C2E4B10\",\n    \"status\": \"voided\",\n    \"status_label\": \"Voided\",\n    \"balance\": 0,\n    \"currency_id\": 1,\n    \"customer_id\": 842,\n    \"is_digital\": true,\n    \"recipient_name\": \"Alex Rivera\",\n    \"recipient_email\": \"alex.rivera@example.com\",\n    \"expires_at\": \"2027-07-06T00:00:00+00:00\",\n    \"activated_at\": \"2026-07-06T14:30:00+00:00\",\n    \"created_at\": \"2026-07-06T14:30:00+00:00\"\n  },\n  \"message\": \"Gift card voided.\"\n}"}]}]},{"name":"House Accounts","item":[{"name":"Get House Account Balance","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/house-account","host":["{{baseUrl}}"],"path":["api","customers",":customer","house-account"],"variable":[{"key":"customer","value":"842","description":"The customer id (path parameter for every house-account endpoint)."}]},"description":"Return the customer's house-account balance owed, credit limit, and remaining headroom.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (PAT).\n\nResponse fields (under `data`):\n- `customer_id` (integer) — the customer.\n- `house_account_enabled` (boolean) — whether on-account charging is enabled for this customer.\n- `balance` (float) — the current balance owed on the account.\n- `credit_limit` (float | null) — the customer's credit limit, or null when uncapped.\n- `available_credit` (float | null) — remaining headroom (credit_limit − balance), or null when there is no limit.\n\nReturns 200 with `{ data: { ... } }`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/{customer}/house-account","host":["{{baseUrl}}"],"path":["api","customers",":customer","house-account"],"variable":[{"key":"customer","value":"842","description":"The customer id (path parameter for every house-account endpoint)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"customer_id\": 842,\n    \"house_account_enabled\": true,\n    \"balance\": 250,\n    \"credit_limit\": 1000,\n    \"available_credit\": 750\n  }\n}"}]},{"name":"Get House Account History","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/house-account/history","host":["{{baseUrl}}"],"path":["api","customers",":customer","house-account","history"],"variable":[{"key":"customer","value":"842","description":"The customer id (path parameter for every house-account endpoint)."}]},"description":"Paginated house-account ledger history for the customer, newest first. Each entry records a value movement (charge, payment, adjustment, or write-off), the resulting balance, and who created it.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (PAT).\n\nEntry `type` values: `charge`, `payment`, `adjustment`, `write_off`. `is_charge` is true when the entry's amount is positive (increases the balance owed). `reference` falls back to an `HA-000123`-style identifier when no explicit reference was recorded.\n\nPagination: `per_page` (default 10), `page` (default 1). Response includes standard Laravel pagination metadata."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/{customer}/house-account/history","host":["{{baseUrl}}"],"path":["api","customers",":customer","house-account","history"],"variable":[{"key":"customer","value":"842","description":"The customer id (path parameter for every house-account endpoint)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 88,\n      \"customer_id\": 842,\n      \"reference\": \"CHK-4021\",\n      \"amount\": -100,\n      \"balance_after\": 250,\n      \"type\": \"payment\",\n      \"type_label\": \"Payment\",\n      \"is_charge\": false,\n      \"currency_id\": 1,\n      \"notes\": \"Check #4021 applied.\",\n      \"source_type\": null,\n      \"source_id\": null,\n      \"created_by_user_id\": 7,\n      \"created_by_name\": \"Jane Cashier\",\n      \"created_at\": \"2026-07-06T15:10:00+00:00\"\n    },\n    {\n      \"id\": 84,\n      \"customer_id\": 842,\n      \"reference\": \"HA-000084\",\n      \"amount\": 250,\n      \"balance_after\": 350,\n      \"type\": \"charge\",\n      \"type_label\": \"Charge\",\n      \"is_charge\": true,\n      \"currency_id\": 1,\n      \"notes\": null,\n      \"source_type\": \"App\\\\Models\\\\PosTransaction\",\n      \"source_id\": 1001,\n      \"created_by_user_id\": 7,\n      \"created_by_name\": \"Jane Cashier\",\n      \"created_at\": \"2026-07-06T14:45:00+00:00\"\n    },\n    {\n      \"id\": 80,\n      \"customer_id\": 842,\n      \"reference\": \"HA-000080\",\n      \"amount\": -25,\n      \"balance_after\": 100,\n      \"type\": \"adjustment\",\n      \"type_label\": \"Adjustment\",\n      \"is_charge\": false,\n      \"currency_id\": 1,\n      \"notes\": \"Goodwill credit.\",\n      \"source_type\": null,\n      \"source_id\": null,\n      \"created_by_user_id\": 7,\n      \"created_by_name\": \"Jane Cashier\",\n      \"created_at\": \"2026-07-06T14:30:00+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/customers/842/house-account/history?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/customers/842/house-account/history?page=1\",\n  \"next_page_url\": null,\n  \"path\": \"{{protocol}}{{domain}}/api/customers/842/house-account/history\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 3,\n  \"total\": 3\n}"}]},{"name":"Charge House Account","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/house-account/charge","host":["{{baseUrl}}"],"path":["api","customers",":customer","house-account","charge"],"variable":[{"key":"customer","value":"842","description":"The customer id (path parameter for every house-account endpoint)."}]},"description":"Manually charge an amount to the customer's house account, increasing the balance owed. Writes a `charge` ledger entry and returns it with the account's new balance.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (PAT).\nPermission: `customers.update`.\n\nRequest body fields:\n- `amount` (numeric, required, > 0) — the amount to charge. Error message when not > 0: \"The charge amount must be greater than zero.\"\n- `notes` (string, nullable, max 2000) — internal note.\n- `reference` (string, nullable, max 255) — external reference (e.g. a PO or receipt number).\n\nThe charge is rejected with 422 when the customer has no house account enabled, or when it would push the balance owed past the customer's credit limit.\n\nReturns 201 with `{ data: <ledger entry>, balance: <float>, message: \"Charge recorded.\" }`.","body":{"mode":"raw","raw":"{\n  \"amount\": 150,\n  \"notes\": \"On-account purchase at the register.\",\n  \"reference\": \"PO-4821\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/customers/{customer}/house-account/charge","host":["{{baseUrl}}"],"path":["api","customers",":customer","house-account","charge"],"variable":[{"key":"customer","value":"842","description":"The customer id (path parameter for every house-account endpoint)."}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 90,\n    \"customer_id\": 842,\n    \"reference\": \"PO-4821\",\n    \"amount\": 150,\n    \"balance_after\": 400,\n    \"type\": \"charge\",\n    \"type_label\": \"Charge\",\n    \"is_charge\": true,\n    \"currency_id\": 1,\n    \"notes\": \"On-account purchase at the register.\",\n    \"source_type\": null,\n    \"source_id\": null,\n    \"created_by_user_id\": 7,\n    \"created_by_name\": \"Jane Cashier\",\n    \"created_at\": \"2026-07-06T15:30:00+00:00\"\n  },\n  \"balance\": 400,\n  \"message\": \"Charge recorded.\"\n}"}]},{"name":"Record House Account Payment","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/house-account/pay","host":["{{baseUrl}}"],"path":["api","customers",":customer","house-account","pay"],"variable":[{"key":"customer","value":"842","description":"The customer id (path parameter for every house-account endpoint)."}]},"description":"Record a payment against the customer's house account, reducing the balance owed. Writes a `payment` ledger entry and returns it with the account's new balance.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (PAT).\nPermission: `customers.update`.\n\nRequest body fields:\n- `amount` (numeric, required, > 0) — the payment amount. Error message when not > 0: \"The payment amount must be greater than zero.\"\n- `notes` (string, nullable, max 2000) — internal note.\n- `reference` (string, nullable, max 255) — external reference (e.g. a cheque or payment reference).\n\nReturns 201 with `{ data: <ledger entry>, balance: <float>, message: \"Payment recorded.\" }`.","body":{"mode":"raw","raw":"{\n  \"amount\": 100,\n  \"notes\": \"Check #4021.\",\n  \"reference\": \"CHK-4021\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/customers/{customer}/house-account/pay","host":["{{baseUrl}}"],"path":["api","customers",":customer","house-account","pay"],"variable":[{"key":"customer","value":"842","description":"The customer id (path parameter for every house-account endpoint)."}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 91,\n    \"customer_id\": 842,\n    \"reference\": \"CHK-4021\",\n    \"amount\": -100,\n    \"balance_after\": 300,\n    \"type\": \"payment\",\n    \"type_label\": \"Payment\",\n    \"is_charge\": false,\n    \"currency_id\": 1,\n    \"notes\": \"Check #4021.\",\n    \"source_type\": null,\n    \"source_id\": null,\n    \"created_by_user_id\": 7,\n    \"created_by_name\": \"Jane Cashier\",\n    \"created_at\": \"2026-07-06T15:40:00+00:00\"\n  },\n  \"balance\": 300,\n  \"message\": \"Payment recorded.\"\n}"}]},{"name":"Adjust House Account","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/house-account/adjust","host":["{{baseUrl}}"],"path":["api","customers",":customer","house-account","adjust"],"variable":[{"key":"customer","value":"842","description":"The customer id (path parameter for every house-account endpoint)."}]},"description":"Manually adjust or write off the customer's house-account balance. Writes an `adjustment` or `write_off` ledger entry and returns it with the account's new balance.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (PAT).\nPermission: `customers.update`.\n\nRequest body fields:\n- `amount` (numeric, required, non-zero) — signed for an adjustment (+ increases the balance owed, − decreases it); magnitude for a write-off (always reduces the balance). Error message when 0: \"The adjustment amount must be non-zero.\"\n- `type` (string, nullable, one of `adjustment`, `write_off`) — defaults to `adjustment` when omitted. Error message on invalid value: \"The type must be an adjustment or a write-off.\"\n- `notes` (string, nullable, max 2000) — internal note.\n\nReturns 201 with `{ data: <ledger entry>, balance: <float>, message: \"House account adjusted.\" }`.","body":{"mode":"raw","raw":"{\n  \"amount\": -50,\n  \"type\": \"write_off\",\n  \"notes\": \"Bad-debt write-off approved by finance.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/customers/{customer}/house-account/adjust","host":["{{baseUrl}}"],"path":["api","customers",":customer","house-account","adjust"],"variable":[{"key":"customer","value":"842","description":"The customer id (path parameter for every house-account endpoint)."}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 92,\n    \"customer_id\": 842,\n    \"reference\": \"HA-000092\",\n    \"amount\": -50,\n    \"balance_after\": 250,\n    \"type\": \"write_off\",\n    \"type_label\": \"Write-off\",\n    \"is_charge\": false,\n    \"currency_id\": 1,\n    \"notes\": \"Bad-debt write-off approved by finance.\",\n    \"source_type\": null,\n    \"source_id\": null,\n    \"created_by_user_id\": 7,\n    \"created_by_name\": \"Jane Cashier\",\n    \"created_at\": \"2026-07-06T15:50:00+00:00\"\n  },\n  \"balance\": 250,\n  \"message\": \"House account adjusted.\"\n}"}]}]},{"name":"Export","item":[{"name":"Download Export File","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/download?file=export-file.csv","host":["{{baseUrl}}"],"path":["api","export","download"],"query":[{"key":"file","value":"export-file.csv","description":"Filename of the completed export (from create-download-file). Required.","disabled":true}]},"description":"Downloads a completed export file from the model-exports disk by filename (the `file` returned from create-download-file). Streams the file as a CSV download (Content-Type: text/csv).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\nQuery parameters:\n- file (required): Filename of the completed export. Returns 404 if omitted.\n\nResponses:\n- 200: File download (text/csv).\n- 404: The `file` param is missing, or the export file no longer exists / has expired / its metadata can't be read. Regenerate the export.\n\nNote: Response is a file download, not JSON."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/export/download?file=export-file.csv","host":["{{baseUrl}}"],"path":["api","export","download"],"query":[{"key":"file","value":"export-file.csv","description":"Filename of the completed export (from create-download-file). Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"id,name\n1,Widget\n2,Gadget"}]},{"name":"Export Initial Inventory","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/initial-inventory/{warehouse}/export","host":["{{baseUrl}}"],"path":["api","export","initial-inventory",":warehouse","export"],"variable":[{"key":"warehouse","value":"1","description":"Warehouse ID"}]},"description":"Exports initial inventory for a specific warehouse as a CSV file.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\nResponse: text/csv file download"}},{"name":"Export Sales Order Lines","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/sales-orders/{id}/export-lines","host":["{{baseUrl}}"],"path":["api","export","sales-orders",":id","export-lines"],"variable":[{"key":"id","value":"100","description":"Sales Order ID"}]},"description":"Exports lines for a specific sales order as a CSV file.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\nResponse: text/csv file download\n\nColumns: sales_order_number, sku, description, quantity, price, warehouse_id, weight, weight_unit, length, width, height, dimension_unit, weight_extended (quantity x unit weight), volume_extended (quantity x unit volume). Weight and dimension values are sourced from the line's product. The header row is always returned even when the order has no lines."}},{"name":"Export Sales Order Line Financials (V2)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/v2/sales-order-line-financials","host":["{{baseUrl}}"],"path":["api","export","v2","sales-order-line-financials"]},"description":"Exports sales order line financials (v2) as a CSV file.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\nResponse: text/csv file download"}},{"name":"Export Sales Order Packing Slips","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/sales-orders/packing-slips","host":["{{baseUrl}}"],"path":["api","export","sales-orders","packing-slips"]},"description":"Exports packing slips for multiple sales orders.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\nResponse: file download"}},{"name":"Download Sales Order Invoice PDF","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/sales-orders/{sales_order}/invoice/pdf","host":["{{baseUrl}}"],"path":["api","export","sales-orders",":sales_order","invoice","pdf"],"variable":[{"key":"sales_order","value":"100","description":"Sales Order ID"}]},"description":"Downloads the invoice PDF for a specific sales order.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\nResponse: application/pdf file download"}},{"name":"Download Sales Order Packing Slip PDF","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/sales-orders/{sales_order}/packing-slip/pdf","host":["{{baseUrl}}"],"path":["api","export","sales-orders",":sales_order","packing-slip","pdf"],"variable":[{"key":"sales_order","value":"100","description":"Sales Order ID"}]},"description":"Downloads the packing slip PDF for a specific sales order.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\nResponse: application/pdf file download"}},{"name":"Export Sales Credits","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/sales-credits","host":["{{baseUrl}}"],"path":["api","export","sales-credits"]},"description":"Exports sales credits as a CSV file.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\nResponse: text/csv file download"}},{"name":"Export Sales Order Fulfillment Packing Slips","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/sales-order-fulfillments/packing-slips","host":["{{baseUrl}}"],"path":["api","export","sales-order-fulfillments","packing-slips"]},"description":"Exports packing slips for multiple sales order fulfillments.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\nResponse: file download"}},{"name":"Export Purchase Order Lines","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/purchase-orders/{id}/export-lines","host":["{{baseUrl}}"],"path":["api","export","purchase-orders",":id","export-lines"],"variable":[{"key":"id","value":"50","description":"Purchase Order ID"}]},"description":"Exports lines for a specific purchase order as a CSV file.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\nResponse: text/csv file download"}},{"name":"Download Purchase Order Invoice CSV","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/purchase-orders/{purchase_order}/invoice/csv","host":["{{baseUrl}}"],"path":["api","export","purchase-orders",":purchase_order","invoice","csv"],"variable":[{"key":"purchase_order","value":"50","description":"Purchase Order ID"}]},"description":"Downloads the invoice CSV for a specific purchase order.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\nResponse: text/csv file download"}},{"name":"Download Purchase Order Invoice PDF","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/purchase-orders/{purchase_order}/invoice/pdf","host":["{{baseUrl}}"],"path":["api","export","purchase-orders",":purchase_order","invoice","pdf"],"variable":[{"key":"purchase_order","value":"50","description":"Purchase Order ID"}]},"description":"Downloads the invoice PDF for a specific purchase order.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\nResponse: application/pdf file download"}},{"name":"Download Purchase Order Picklist PDF","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/purchase-orders/{purchase_order}/picklist/pdf","host":["{{baseUrl}}"],"path":["api","export","purchase-orders",":purchase_order","picklist","pdf"],"variable":[{"key":"purchase_order","value":"50","description":"Purchase Order ID"}]},"description":"Downloads the picklist PDF for a specific purchase order.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\nResponse: application/pdf file download"}},{"name":"Export Stock Take Lines","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/stock-takes/{stock_take}/export-lines","host":["{{baseUrl}}"],"path":["api","export","stock-takes",":stock_take","export-lines"],"variable":[{"key":"stock_take","value":"1","description":"Stock Take ID"}]},"description":"Exports lines for a specific stock take as a CSV file.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\nResponse: text/csv file download"}},{"name":"Export Warehouse Transfer Lines","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/warehouse-transfers/{id}/export-lines","host":["{{baseUrl}}"],"path":["api","export","warehouse-transfers",":id","export-lines"],"variable":[{"key":"id","value":"1","description":"Warehouse Transfer ID"}]},"description":"Exports lines for a specific warehouse transfer as a CSV file.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\nResponse: text/csv file download"}},{"name":"Export Ship By Schedule","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/ship-by-schedule","host":["{{baseUrl}}"],"path":["api","export","ship-by-schedule"]},"description":"Exports ship-by schedule data as a CSV/JSON file.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token\n\nResponse: file download"}},{"name":"Export Financial Alerts (V2)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/v2/financial-alerts","host":["{{baseUrl}}"],"path":["api","export","v2","financial-alerts"]},"description":"Exports all financial alerts using the V2 API. Returns JSON array for client-side CSV generation.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/export/v2/financial-alerts","host":["{{baseUrl}}"],"path":["api","export","v2","financial-alerts"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"salesOrder\": \"SO-1001\",\n      \"salesOrderId\": 1001,\n      \"sku\": \"WIDGET-001\",\n      \"product_id\": 42,\n      \"total_revenue\": 150,\n      \"cogs\": 80,\n      \"cogs_returned\": 0,\n      \"cost_allocated\": 10,\n      \"allocated_details\": [\n        {\n          \"description\": \"Shipping\",\n          \"amount\": 10\n        }\n      ],\n      \"total_cost\": 90,\n      \"profit\": 60,\n      \"profit_margin\": 40,\n      \"classification\": \"Low Margin\",\n      \"classification_id\": 2,\n      \"archived_by_user\": null,\n      \"archived_at\": null,\n      \"is_resolved\": \"false\"\n    }\n  ]\n}"}]},{"name":"Export Accounting Transactions (CSV)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/v2/accounting-transactions","host":["{{baseUrl}}"],"path":["api","export","v2","accounting-transactions"]},"description":"Streams a CSV export of accounting transactions with the same filters as the v3/transactions list endpoint. Response is a streaming text/csv file download.\n\nAuthentication: Requires Bearer token\n\nFilters use the same Spatie QueryBuilder parameters as GET /api/accounting/v3/transactions."}},{"name":"Export Profitability Drilldown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/v2/profitability-drilldown?dimension=brand&dimension_value=Nike","host":["{{baseUrl}}"],"path":["api","export","v2","profitability-drilldown"],"query":[{"key":"dimension","value":"brand","description":"Dimension to drilldown: brand | sku | supplier | sales_channel | product_type | sub_sales_channel | customer_tags. customer_tags fans out — an order counts toward each of its customer's tags — and the grand total is deduplicated. customer_tags does not require attribute_id.","disabled":true},{"key":"dimension_value","value":"Nike","description":"Specific dimension value to filter by","disabled":true}]},"description":"Dispatch an asynchronous CSV export of all profitability drilldown orders for a dimension. The drilldown materialises every order and builds the whole file, which can exceed a ~100s edge-proxy limit (Cloudflare 524) — so it runs as a tracked export job. Returns a tracked_job_log_id; progress and the download link surface via the tracked job log endpoints. The finished file is fetched from GET /api/export/download?file={filename}.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nQuery params: dimension (brand|sku|supplier|sales_channel|product_type|sub_sales_channel|customer_tags; customer_tags fans out so an order counts toward each of its customer's tags and the grand total is deduplicated), dimension_value, flt_ids[] (financial line type ids for extra columns)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/export/v2/profitability-drilldown?dimension=brand&dimension_value=Nike","host":["{{baseUrl}}"],"path":["api","export","v2","profitability-drilldown"],"query":[{"key":"dimension","value":"brand","description":"Dimension to drilldown: brand | sku | supplier | sales_channel | product_type | sub_sales_channel | customer_tags. customer_tags fans out — an order counts toward each of its customer's tags — and the grand total is deduplicated. customer_tags does not require attribute_id.","disabled":true},{"key":"dimension_value","value":"Nike","description":"Specific dimension value to filter by","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 5123,\n    \"queued\": true\n  },\n  \"message\": \"Export started — track progress in the job tray\"\n}"}]},{"name":"Export Daily Financials","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/daily-financials","host":["{{baseUrl}}"],"path":["api","export","daily-financials"]},"description":"Returns the full daily financial summary dataset as JSON for export, honoring the same filters and sorting as the daily summary listing (`GET /api/v2/financials/daily-summary`) but without pagination.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nEach row aggregates one day's financial activity for a subject: either a product (units sold, revenue, cost, profit for that SKU on that day) or a financial line type (order-level amounts such as shipping revenue or transaction fees). The `reportable` object identifies the subject.\n\nThe response contains every matching row, so filter by date range to keep payloads manageable on large accounts.\n\nLarge result sets are exported asynchronously: when the filtered dataset exceeds 1000 rows the endpoint dispatches a background CSV export job and responds with a tracked_job_log_id (queued=true) instead of the inline rows. Poll the job by its tracked_job_log_id and fetch the finished file from GET /api/export/download?file={filename}. Smaller result sets return the rows inline as shown below.\n\nAuthentication: Requires Bearer token.\n\n**Response shape - `delivery`.** By default an export of up to 1,000 rows is returned directly in this response, and a larger one runs in the background and returns `{\"tracked_job_log_id\": ..., \"queued\": true}`. Pass `delivery=job` to get the job response every time: a small export still runs immediately (it is never queued) and comes back already finished - `{\"tracked_job_log_id\": ..., \"queued\": false, \"status\": \"completed\", \"results\": {\"download_url\": ..., \"record_count\": ...}}` - while a large one is queued exactly as before. `scope=current_page` always returns the page directly, whatever `delivery` says. Use it when your client expects one flow for every export (start, then check `GET /api/tracked-job-logs/{trackedJobLog}`, then download), such as an ETL connector. Subscribe to the `export.completed` webhook to be told when a queued export is ready instead of polling."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/export/daily-financials","host":["{{baseUrl}}"],"path":["api","export","daily-financials"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5012,\n      \"date\": \"2026-06-15T00:00:00.000000Z\",\n      \"reportable\": {\n        \"id\": 101,\n        \"type\": \"App\\\\Models\\\\Product\",\n        \"product\": {\n          \"id\": 101,\n          \"sku\": \"TSHIRT-BLK-M\",\n          \"brand\": \"Acme\",\n          \"brand_id\": 3\n        }\n      },\n      \"quantity\": 14,\n      \"quantity_returned\": 1,\n      \"num_orders\": 12,\n      \"revenue\": 349.86,\n      \"revenue_allocated\": 0,\n      \"revenue_credits\": -24.99,\n      \"revenue_discount\": -10,\n      \"total_revenue\": 314.87,\n      \"cost\": 126,\n      \"cost_allocated\": 8.4,\n      \"cost_invoices\": 0,\n      \"cost_returned\": -9,\n      \"cost_breakdown\": null,\n      \"total_cost\": 125.4,\n      \"profit\": 189.47,\n      \"profit_margin\": 60.17,\n      \"created_at\": \"2026-06-16T02:10:00.000000Z\",\n      \"updated_at\": \"2026-06-16T02:10:00.000000Z\"\n    },\n    {\n      \"id\": 5013,\n      \"date\": \"2026-06-15T00:00:00.000000Z\",\n      \"reportable\": {\n        \"id\": 7,\n        \"type\": \"App\\\\Models\\\\FinancialLineType\",\n        \"financial_line_type\": {\n          \"id\": 7,\n          \"name\": \"Shipping Revenue\",\n          \"classification\": \"revenue\"\n        }\n      },\n      \"quantity\": 0,\n      \"quantity_returned\": 0,\n      \"num_orders\": 12,\n      \"revenue\": 58.8,\n      \"revenue_allocated\": 0,\n      \"revenue_credits\": 0,\n      \"revenue_discount\": 0,\n      \"total_revenue\": 58.8,\n      \"cost\": 0,\n      \"cost_allocated\": 0,\n      \"cost_invoices\": 0,\n      \"cost_returned\": 0,\n      \"cost_breakdown\": null,\n      \"total_cost\": 0,\n      \"profit\": 58.8,\n      \"profit_margin\": 100,\n      \"created_at\": \"2026-06-16T02:10:00.000000Z\",\n      \"updated_at\": \"2026-06-16T02:10:00.000000Z\"\n    }\n  ]\n}"}]},{"name":"Export Vendor Deposits","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/export/v2/vendor-deposits","host":["{{baseUrl}}"],"path":["api","export","v2","vendor-deposits"]},"description":"Streams all vendor deposits matching the given filters as a CSV file download (`vendor-deposits.csv`). Honors the same filters and sorting as the vendor deposit list endpoints.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nCSV columns: Deposit #, Supplier, Purchase Order, Status, Currency, Amount, Amount Paid, Amount Applied, Amount Refunded, Available Balance, Deposit Date, Due Date, External Reference, Notes, Created At. Available Balance is computed as amount paid minus amounts applied and refunded (floored at 0).\n\nArchived deposits are excluded by default; pass `filter[archived]=1` to include them. The response is a file download (`text/csv`), not JSON.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/export/v2/vendor-deposits","host":["{{baseUrl}}"],"path":["api","export","v2","vendor-deposits"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"Deposit #,Supplier,Purchase Order,Status,Currency,Amount,Amount Paid,Amount Applied,Amount Refunded,Available Balance,Deposit Date,Due Date,External Reference,Notes,Created At\nVD-00042,Acme Apparel Co,PO-350,paid,USD,5000.00,5000.00,2500.00,0.00,2500.00,2026-05-01,2026-05-15,WIRE-88213,30% deposit on spring order,2026-05-01 14:22:10\nVD-00043,Global Freight Co,PO-351,partially_paid,USD,1200.00,600.00,0.00,0.00,600.00,2026-05-20,2026-06-01,,,2026-05-20 09:03:41\n"}]}]},{"name":"Inventory Intelligence","item":[{"name":"Build Forecast","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/build","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","build"]},"description":"Build a replenishment forecast for one or more suppliers and return recommended order quantities per supplier, with a full per-line breakdown of how each number was derived.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFORECAST TYPES (forecast_type)\n- with_sales_history: derive demand from historical sales velocity over the chosen window. The most common mode.\n- target_stock_level: order up to a fixed target quantity per product (target_quantity), or up to each product's minimum stock level when use_min_stock_level_as_target is true. Velocity is not used to set the target.\n- fill_backorders: order only what is needed to clear outstanding backorders. Baseline and trend settings do not apply.\n\nBASELINE METHODS (baseline_method) - how the daily velocity baseline is derived from sales history (applies to with_sales_history)\n- flat (default): plain mean over the window (total units / days covered). No trend. Legacy behaviour - stable and predictable. Use when demand is roughly level.\n- recency_weighted: weights recent history more heavily than older history, so a recent shift in demand is reflected faster without projecting a hard trend line. Use for choppy demand that has recently stepped up or down. Tune with trend_half_life_days.\n- damped_trend: fits the recent growth (or decline) trend and projects it forward, attenuated by trend_damping_factor so a short surge does not overshoot into over-ordering. Use for a SKU on a sustained growth or decline curve.\n\nDAMPED-TREND CONTROLS\n- trend_damping_factor (0..1, default 0.85): the damping factor phi. Each period further out keeps only phi of the previous period's growth, so the projected trend flattens instead of compounding forever. Lower = more damping (closer to flat mean); 1.0 = undamped.\n- trend_max_growth_pct (>= 0, default 100): caps how far the trend may lift the baseline above the flat mean. 100 means the trend may at most double the flat mean; 0 disables any lift. When the cap binds, the line reports trend_capped: true.\n- trend_half_life_days (>= 1, nullable, default 21): reserved tuning for recency_weighted - the age at which a day's weight halves.\n\nSPARSE-HISTORY FALLBACK\nA trend needs enough signal to be trustworthy. If a product has fewer than roughly 8 weeks of history or fewer than 8 orders in the window, the trend is skipped and the baseline falls back to the flat mean for that product (trend_factor 1.0), regardless of baseline_method. This stops a couple of early sales from projecting a runaway trend.\n\nANOMALY CLEANSING (exclude_detected_anomalies, default false)\nWhen true, days already resolved as excluded or smoothed anomalies for a product are dropped (excluded) or replaced with their expected value (smoothed) before the baseline is computed. This de-contaminates one-off promo spikes and stockout gaps so they do not distort ordinary velocity. Detect and resolve anomalies first (see the Anomalies folder). Each line reports how many days were affected via inputs.anomaly_days_excluded and inputs.anomaly_days_smoothed.\n\nDEMAND MODIFIERS (demand_modifiers[])\nAn array of adjustments layered onto the baseline. Each entry has a type:\n- percentage: scale demand by (1 + value/100), e.g. value 10 = +10%.\n- seasonal / event: a bounded uplift or reduction over a date range (start_date, end_date) for a known seasonal peak or one-time event; value is the percent change.\n- override: replace the daily average outright with value.\n- promo_lift: re-apply a previously measured promotion. Set promo_window_id to a promo window whose lift has been measured; the server hydrates that window's live measured lift and applies it as a MULTIPLICATIVE factor on the current baseline. Because it multiplies the CURRENT baseline (not a stored absolute number), the same promo automatically re-scales as the business grows. Send value 0 - it is ignored and hydrated server-side.\nOptional per-modifier fields: apply_to (all or a scope key), name (label), period_key, start_date, end_date, product_id (limit to one product), promo_window_id (promo_lift only).\n\nFILTERS (product_filters, sales_filters)\nBoth accept a filter_groups tree: a conjunction (and / or) plus a children array of conditions and nested groups. Each condition has column, operator, and value. product_filters narrows which products are forecast; sales_filters narrows which historical orders feed the velocity baseline. A legacy filters.filterSet shape is also accepted for older saved configurations.\nOperators: is, is_not, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, is_one_of, greater_than, less_than, between.\nExample: {\"filter_groups\": {\"conjunction\": \"and\", \"children\": [{\"type\": \"condition\", \"condition\": {\"column\": \"order_status\", \"operator\": \"is_one_of\", \"value\": [\"shipped\", \"fulfilled\"]}}]}}\n\nDECOMPOSITION RETURNED PER LINE (inputs object)\nEach forecast line's inputs object exposes the multiplicative decomposition:\n- base_flat: the flat mean before any trend.\n- trend_factor: the multiplicative trend baked into the baseline (1.0 = none).\n- baseline_method: which method produced the baseline.\n- trend_capped: true if trend_max_growth_pct clamped the trend.\n- anomaly_days_excluded / anomaly_days_smoothed: how many history days were dropped / replaced by anomaly cleansing.\n- promo_factor: the multiplicative promo lift applied (null when no promo_lift modifier applied).\n- promo_window_name: the promo window that supplied the lift (null if none).\n- promo_current_baseline: the baseline the promo factor multiplied.\nThe effective daily_average is base_flat x trend_factor x (promo_factor or 1), then any percentage/event/override demand modifiers.\n\nSCENARIOS (copy a body variation for each)\n1) Flat baseline (default / legacy): omit baseline_method or send \"flat\". Velocity is the plain window mean; inputs.trend_factor is 1.0 and inputs.base_flat equals daily_average.\n2) Damped-trend for a growing SKU: baseline_method \"damped_trend\", trend_damping_factor 0.85, trend_max_growth_pct 100. Recent growth is projected forward but damped and capped at 2x the flat mean; inputs.trend_factor rises above 1.0 and inputs.trend_capped reports whether the cap bound.\n3) Recency-weighted: baseline_method \"recency_weighted\", trend_half_life_days 21. Recent days count more; no hard trend line is projected.\n4) Anomaly-cleansed baseline: exclude_detected_anomalies true (after resolving anomalies as excluded/smoothed). Promo spikes and stockout gaps are removed before the mean is taken; inputs.anomaly_days_excluded / inputs.anomaly_days_smoothed report the count.\n5) Re-apply a past promotion: add a demand_modifiers entry of type \"promo_lift\" with promo_window_id set to a measured promo window and value 0. The window's live lift multiplies the current baseline, so it auto-scales with growth; inputs.promo_factor, inputs.promo_window_name, and inputs.promo_current_baseline are populated.\n6) Target stock level / fill backorders: forecast_type \"target_stock_level\" (with target_quantity, or use_min_stock_level_as_target true) orders up to a fixed target; forecast_type \"fill_backorders\" clears backorders only. Baseline and trend settings do not apply to fill_backorders.\n\nMINIMUMS\nenforce_minimum_order_value / enforce_minimum_order_quantity: when true, the per-supplier minimums block flags whether the forecast totals fall below the supplier's minimum_purchase_order / minimum_purchase_order_quantity. Display-only here; enforcement is applied at PO creation time (POST /api/inventory-forecasting/purchase-orders).\n\nEach line also carries a confidence object (tier high/medium/low/new, 0-100 score, drivers, reasons, actions), a prediction_interval (point/lower/upper at a service level), and a plain-language rationale.\n\nReturns 422 if too many products are found for any supplier.\n\nPER-LINE FIELDS OF NOTE\n- is_bom_component (boolean): true when the product is a component line on a live bill of materials. Such products have no sales of their own — their demand is driven by finished-goods production — so a 'nothing to order' verdict for them means demand planning is the wrong tool, not that they are well stocked. Plan component requirements through manufacturing planning (MRP) instead.\n\nUNIFIED MAKE / BUY / TRANSFER PLAN\nSend warehouse_ids (or omit supplier_ids, or set include_make=true) to build one plan for everything you sell instead of a per-supplier purchase forecast. Suppliers become an optional filter on purchased products. Products with an active BOM are planned as builds (Make), their ingredients and packaging are sized from those builds (BOM explosion with unit conversion, scrap and yield), warehouses fed by the distribution network get transfers, and products with no BOM or supplier are shown as unsourced. The plan is built by a background job: the response is 202 with run_id and tracked_job_log_id — poll Get Plan for the result.\n- warehouse_ids (integer[]): warehouses to plan. Required when no supplier_ids are sent.\n- horizon_days (integer 7–365, default 90): how far ahead the plan's forecast looks.\n- include_make (boolean): plan products with an active BOM as builds. Omit to decide from whether any active BOM exists.\nA request with supplier_ids and no warehouse_ids (and include_make not true) runs the per-supplier forecast exactly as before. Each line of that forecast now carries has_active_bom (true when the product is made in-house).","body":{"mode":"raw","raw":"{\n  \"supplier_ids\": [\n    1,\n    2\n  ],\n  \"forecast_type\": \"with_sales_history\",\n  \"target_stock_days\": 30,\n  \"sales_history_days\": 90,\n  \"sales_start_date\": null,\n  \"sales_end_date\": null,\n  \"use_leadtime\": true,\n  \"use_measured_lead_time\": false,\n  \"use_moq\": true,\n  \"use_case_pack\": false,\n  \"case_pack_rounding\": \"round_nearest\",\n  \"override_product_target_days\": false,\n  \"rounding_method\": \"round_half_up\",\n  \"destination_warehouse_id\": null,\n  \"pricing_tier_id\": null,\n  \"ignore_min_stock_level\": false,\n  \"baseline_method\": \"damped_trend\",\n  \"trend_damping_factor\": 0.85,\n  \"trend_max_growth_pct\": 100,\n  \"trend_half_life_days\": 21,\n  \"exclude_detected_anomalies\": true,\n  \"product_filters\": {\n    \"filter_groups\": {\n      \"conjunction\": \"and\",\n      \"children\": [\n        {\n          \"type\": \"condition\",\n          \"condition\": {\n            \"column\": \"sku\",\n            \"operator\": \"contains\",\n            \"value\": \"PROD\"\n          }\n        }\n      ]\n    }\n  },\n  \"sales_filters\": {\n    \"filter_groups\": {\n      \"conjunction\": \"and\",\n      \"children\": [\n        {\n          \"type\": \"condition\",\n          \"condition\": {\n            \"column\": \"order_status\",\n            \"operator\": \"is_one_of\",\n            \"value\": [\n              \"shipped\",\n              \"fulfilled\"\n            ]\n          }\n        }\n      ]\n    }\n  },\n  \"demand_modifiers\": [\n    {\n      \"type\": \"event\",\n      \"value\": 15,\n      \"apply_to\": \"all\",\n      \"name\": \"Holiday Boost\",\n      \"period_key\": null,\n      \"start_date\": \"2024-11-01\",\n      \"end_date\": \"2024-12-31\",\n      \"product_id\": null\n    },\n    {\n      \"type\": \"promo_lift\",\n      \"value\": 0,\n      \"apply_to\": \"all\",\n      \"name\": \"Re-apply Black Friday 2024\",\n      \"product_id\": 56,\n      \"promo_window_id\": 12\n    }\n  ],\n  \"enforce_minimum_order_value\": false,\n  \"enforce_minimum_order_quantity\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/build","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","build"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"supplier_id\": 1,\n      \"supplier_name\": \"Acme Supplier Co.\",\n      \"summary\": {\n        \"total_products\": 25,\n        \"products_to_order\": 18,\n        \"total_units\": 512,\n        \"total_value\": 14380\n      },\n      \"lines\": [\n        {\n          \"supplier_product_id\": 101,\n          \"product_id\": 55,\n          \"sku\": \"PROD-001\",\n          \"name\": \"Widget A\",\n          \"recommended_quantity\": 88,\n          \"unit_cost\": 12.5,\n          \"total_cost\": 1100,\n          \"current_stock\": 10,\n          \"days_of_stock\": 2,\n          \"avg_daily_sales\": 5.1618,\n          \"leadtime_days\": 14,\n          \"status\": \"order\",\n          \"is_bom_component\": false,\n          \"inputs\": {\n            \"target_days\": 30,\n            \"target_days_source\": \"config\",\n            \"leadtime\": 14,\n            \"leadtime_source\": \"supplier\",\n            \"supplier_lead_time\": 14,\n            \"effective_days\": 44,\n            \"daily_average\": 5.1618,\n            \"available\": 10,\n            \"inbound\": 0,\n            \"in_transit\": 0,\n            \"backordered\": 0,\n            \"moq\": 25,\n            \"moq_source\": \"supplier\",\n            \"min_stock_level\": 20,\n            \"min_stock_level_ignored\": false,\n            \"order_trigger\": \"velocity\",\n            \"rounding_method\": \"round_half_up\",\n            \"base_daily_average\": 5.1618,\n            \"base_flat\": 3.08,\n            \"trend_factor\": 1.6759,\n            \"baseline_method\": \"damped_trend\",\n            \"trend_capped\": false,\n            \"anomaly_days_excluded\": 2,\n            \"anomaly_days_smoothed\": 0,\n            \"promo_factor\": null,\n            \"promo_window_name\": null,\n            \"promo_current_baseline\": null,\n            \"formula\": \"(daily_average x effective_days) - available - inbound + backordered\"\n          },\n          \"confidence\": {\n            \"tier\": \"medium\",\n            \"score\": 62,\n            \"drivers\": [\n              {\n                \"key\": \"history_length\",\n                \"label\": \"History length\",\n                \"value\": \"90 days\",\n                \"status\": \"ok\"\n              },\n              {\n                \"key\": \"selling_density\",\n                \"label\": \"Selling density\",\n                \"value\": \"71/90 days had sales\",\n                \"status\": \"ok\"\n              },\n              {\n                \"key\": \"variability\",\n                \"label\": \"Demand variability (CV)\",\n                \"value\": \"0.80\",\n                \"status\": \"warn\"\n              },\n              {\n                \"key\": \"recency\",\n                \"label\": \"Last sale\",\n                \"value\": \"1 day ago\",\n                \"status\": \"ok\"\n              }\n            ],\n            \"reasons\": [\n              \"Demand is moderately variable (CV 0.80)\"\n            ],\n            \"actions\": [\n              {\n                \"type\": \"extend_window\",\n                \"label\": \"Extend the sales window\"\n              }\n            ]\n          },\n          \"prediction_interval\": {\n            \"point\": 88,\n            \"lower\": 66,\n            \"upper\": 116,\n            \"service_level\": 90,\n            \"method\": \"cv_heuristic\",\n            \"sigma\": 15.1\n          },\n          \"rationale\": \"Ordering 88: 90 days of history, damped-trend baseline lifting a 3.08/day flat mean to 5.16/day (trend factor 1.68), 2 promo-spike days excluded, 14-day lead time, 10 on hand. Confidence Medium. Likely range 66-116 at 90% service level.\"\n        },\n        {\n          \"supplier_product_id\": 102,\n          \"product_id\": 56,\n          \"sku\": \"PROD-002\",\n          \"name\": \"Widget B\",\n          \"recommended_quantity\": 140,\n          \"unit_cost\": 8,\n          \"total_cost\": 1120,\n          \"current_stock\": 25,\n          \"days_of_stock\": 3,\n          \"avg_daily_sales\": 7.77,\n          \"leadtime_days\": 14,\n          \"status\": \"order\",\n          \"is_bom_component\": false,\n          \"inputs\": {\n            \"target_days\": 30,\n            \"target_days_source\": \"config\",\n            \"leadtime\": 14,\n            \"leadtime_source\": \"supplier\",\n            \"supplier_lead_time\": 14,\n            \"effective_days\": 44,\n            \"daily_average\": 7.77,\n            \"available\": 25,\n            \"inbound\": 0,\n            \"in_transit\": 0,\n            \"backordered\": 0,\n            \"moq\": 50,\n            \"moq_source\": \"supplier\",\n            \"min_stock_level\": 30,\n            \"min_stock_level_ignored\": false,\n            \"order_trigger\": \"velocity\",\n            \"rounding_method\": \"round_half_up\",\n            \"base_daily_average\": 4.2,\n            \"base_flat\": 4.2,\n            \"trend_factor\": 1,\n            \"baseline_method\": \"damped_trend\",\n            \"trend_capped\": false,\n            \"anomaly_days_excluded\": 0,\n            \"anomaly_days_smoothed\": 0,\n            \"promo_factor\": 1.85,\n            \"promo_window_name\": \"Black Friday 2024\",\n            \"promo_current_baseline\": 4.2,\n            \"demand_modifier_description\": \"Promo lift: Black Friday 2024 (x1.85)\",\n            \"formula\": \"(daily_average x effective_days) - available - inbound + backordered\"\n          },\n          \"confidence\": {\n            \"tier\": \"high\",\n            \"score\": 84,\n            \"drivers\": [\n              {\n                \"key\": \"history_length\",\n                \"label\": \"History length\",\n                \"value\": \"90 days\",\n                \"status\": \"ok\"\n              },\n              {\n                \"key\": \"selling_density\",\n                \"label\": \"Selling density\",\n                \"value\": \"88/90 days had sales\",\n                \"status\": \"ok\"\n              },\n              {\n                \"key\": \"variability\",\n                \"label\": \"Demand variability (CV)\",\n                \"value\": \"0.42\",\n                \"status\": \"ok\"\n              },\n              {\n                \"key\": \"recency\",\n                \"label\": \"Last sale\",\n                \"value\": \"today\",\n                \"status\": \"ok\"\n              }\n            ],\n            \"reasons\": [\n              \"Dense, recent, low-variability history\"\n            ],\n            \"actions\": []\n          },\n          \"prediction_interval\": {\n            \"point\": 140,\n            \"lower\": 118,\n            \"upper\": 168,\n            \"service_level\": 90,\n            \"method\": \"cv_heuristic\",\n            \"sigma\": 17.2\n          },\n          \"rationale\": \"Ordering 140: flat 4.2/day baseline re-scaled by the measured Black Friday 2024 lift (x1.85) to 7.77/day, 14-day lead time, 25 on hand. The lift is multiplicative on the current baseline, so it tracks this year's larger demand. Confidence High. Likely range 118-168 at 90% service level.\"\n        }\n      ],\n      \"active_lines_count\": 18,\n      \"skipped_lines_count\": 7,\n      \"pricing_tiers\": [],\n      \"default_pricing_tier_id\": null,\n      \"minimums\": {\n        \"enforce_value\": false,\n        \"enforce_quantity\": false,\n        \"minimum_value\": 1000,\n        \"minimum_quantity\": 100\n      }\n    }\n  ]\n}"}]},{"name":"Build Forecast (Single Supplier)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/build-single","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","build-single"]},"description":"Build a forecast for a single supplier. If the product count is large the request is queued as a background job and the response returns a job_id for polling; otherwise the result is returned synchronously.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nResponse type field: sync (includes data) or async (includes job_id for status polling).\n\nThis endpoint accepts the same forecast controls as Build Forecast, including the baseline decomposition:\n- baseline_method: flat (default), recency_weighted, or damped_trend.\n- trend_damping_factor (0..1, default 0.85): damping factor phi for damped_trend; lower = more damping.\n- trend_max_growth_pct (>= 0, default 100): caps the trend lift above the flat mean (100 = may at most double it).\n- trend_half_life_days (>= 1, nullable, default 21): reserved tuning for recency_weighted.\n- exclude_detected_anomalies (default false): drop/replace resolved anomaly days before computing the baseline.\n- demand_modifiers[]: percentage, seasonal, event, override, or promo_lift (carry promo_window_id and send value 0 - the window's measured lift is applied multiplicatively to the current baseline).\n\nThe sparse-history fallback applies here too: a product with fewer than roughly 8 weeks of history or fewer than 8 orders falls back to the flat mean (trend_factor 1.0). See Build Forecast for the full field guide and the per-line inputs decomposition (base_flat, trend_factor, baseline_method, trend_capped, anomaly_days_excluded, anomaly_days_smoothed, promo_factor, promo_window_name, promo_current_baseline).\n\nenforce_minimum_order_value / enforce_minimum_order_quantity: when true, the minimums block flags whether the forecast totals fall below the supplier's minimum_purchase_order / minimum_purchase_order_quantity. Display-only here; enforcement is applied at PO creation time (POST /api/inventory-forecasting/purchase-orders).\n\nReturns 422 if too many products are found.","body":{"mode":"raw","raw":"{\n  \"supplier_id\": 1,\n  \"product_count\": 120,\n  \"supplier_ids\": [\n    1\n  ],\n  \"forecast_type\": \"with_sales_history\",\n  \"target_stock_days\": 30,\n  \"sales_history_days\": 90,\n  \"use_leadtime\": true,\n  \"use_measured_lead_time\": false,\n  \"use_moq\": true,\n  \"rounding_method\": \"round_half_up\",\n  \"baseline_method\": \"recency_weighted\",\n  \"trend_damping_factor\": 0.85,\n  \"trend_max_growth_pct\": 100,\n  \"trend_half_life_days\": 21,\n  \"exclude_detected_anomalies\": false,\n  \"product_filters\": {},\n  \"sales_filters\": {},\n  \"demand_modifiers\": [\n    {\n      \"type\": \"promo_lift\",\n      \"value\": 0,\n      \"apply_to\": \"all\",\n      \"name\": \"Re-apply Prime Day\",\n      \"product_id\": 55,\n      \"promo_window_id\": 8\n    }\n  ],\n  \"enforce_minimum_order_value\": false,\n  \"enforce_minimum_order_quantity\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/build-single","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","build-single"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"type\": \"sync\",\n    \"data\": {\n      \"supplier_id\": 1,\n      \"supplier_name\": \"Acme Supplier Co.\",\n      \"active_lines_count\": 12,\n      \"skipped_lines_count\": 3,\n      \"minimums\": {\n        \"enforce_value\": false,\n        \"enforce_quantity\": false,\n        \"minimum_value\": 1000,\n        \"minimum_quantity\": 100\n      }\n    }\n  }\n}"}]},{"name":"Get Job Status","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/job/{jobId}/status","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","job",":jobId","status"],"variable":[{"key":"jobId","value":"abc123def456","description":"The job ID returned from build-single when dispatched async"}]},"description":"Poll the status of a background forecast job dispatched by build-single.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nStatus values: queued, processing, completed, failed.\n\nWhen status is 'completed', the result field is included with full forecast data.\n\nReturns 404 if job ID not found or has expired."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/job/{jobId}/status","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","job",":jobId","status"],"variable":[{"key":"jobId","value":"abc123def456","description":"The job ID returned from build-single when dispatched async"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"status\": \"processing\",\n    \"progress\": 45,\n    \"message\": \"Processing products 45 of 120\",\n    \"supplier_id\": 1,\n    \"product_count\": 120\n  }\n}"}]},{"name":"Get Sales History","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/sales-history","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","sales-history"]},"description":"Get aggregated sales history for products grouped by time period.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPeriod values: week, month, quarter, year (defaults to week).\n\nDate format: YYYY-MM-DD. start_date must be <= end_date.\n\nReturns an array keyed by product_id, each with periods array containing quantity and value per period.","body":{"mode":"raw","raw":"{\n  \"product_ids\": [\n    55,\n    56,\n    57\n  ],\n  \"start_date\": \"2023-10-01\",\n  \"end_date\": \"2024-10-01\",\n  \"period\": \"week\",\n  \"warehouse_id\": null,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/sales-history","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","sales-history"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 55,\n      \"periods\": [\n        {\n          \"period\": \"2024-W01\",\n          \"quantity\": 12,\n          \"value\": 150\n        },\n        {\n          \"period\": \"2024-W02\",\n          \"quantity\": 8,\n          \"value\": 100\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"List Sales Orders Behind a Forecast Cell","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/sales-orders?product_id=55&start_date=2023-10-01&end_date=2024-10-01&per_page=10","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","sales-orders"],"query":[{"key":"product_id","value":"55","description":"Required. Product ID to get sales orders for.","disabled":true},{"key":"start_date","value":"2023-10-01","description":"Required. Start date (YYYY-MM-DD).","disabled":true},{"key":"end_date","value":"2024-10-01","description":"Required. End date (YYYY-MM-DD).","disabled":true},{"key":"per_page","value":"10","description":"Optional. Items per page (1-100, default 10).","disabled":true}]},"description":"Get individual sales orders for a product (drill-down view). Used when clicking into a product's sales period in the forecast UI.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nDate format: YYYY-MM-DD. Returns paginated sales orders."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/sales-orders?product_id=55&start_date=2023-10-01&end_date=2024-10-01&per_page=10","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","sales-orders"],"query":[{"key":"product_id","value":"55","description":"Required. Product ID to get sales orders for.","disabled":true},{"key":"start_date","value":"2023-10-01","description":"Required. Start date (YYYY-MM-DD).","disabled":true},{"key":"end_date","value":"2024-10-01","description":"Required. End date (YYYY-MM-DD).","disabled":true},{"key":"per_page","value":"10","description":"Optional. Items per page (1-100, default 10).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1001,\n      \"order_number\": \"SO-1001\",\n      \"order_date\": \"2024-03-15\",\n      \"quantity\": 10,\n      \"unit_price\": 25,\n      \"total\": 250,\n      \"status\": \"fulfilled\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 10,\n  \"total\": 28\n}"}]},{"name":"Create Purchase Orders from Forecast","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/purchase-orders","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","purchase-orders"]},"description":"Create purchase orders from forecast results. Creates a ForecastScheduleRun to track the creation.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\napproval_status: draft (default) or approved — if approved, auto-approves the PO after creation.\n\nsubmit_to_supplier: if true, submits the PO to the supplier after creation.\n\nforecast_config is optional but recommended for tracking — stores a snapshot of the config used.\n\nAlso syncs backorder coverage for all new PO lines.\n\nReturns 422 if no valid orders provided.\n\nSupplier minimum enforcement: when forecast_config.enforce_minimum_order_value or forecast_config.enforce_minimum_order_quantity is true and the order totals fall below the supplier's minimum_purchase_order or minimum_purchase_order_quantity, the request is rejected with 422 unless minimums_acknowledged is true on that order.\n\nforecast_type accepts: target_stock_level, with_sales_history, fill_backorders.\n\nNullable forecast_config snapshot keys: the snapshot is sent verbatim from the saved forecast configuration, which persists null for any setting the selected forecast type does not use (e.g. target_stock_days is null on a target_stock_level run that targets a minimum stock level, and on a fill_backorders run). Every snapshot key therefore accepts null as well as its documented type: supplier_ids (array|null), forecast_type (string|null), target_stock_days (integer|null), override_product_target_days (boolean|null), use_leadtime (boolean|null), use_measured_lead_time (boolean|null), use_moq (boolean|null), enforce_minimum_order_value (boolean|null), enforce_minimum_order_quantity (boolean|null), rounding_method (string|null), target_quantity (integer|null), sales_history_days (integer|null), sales_start_date (string|null), sales_end_date (string|null), destination_warehouse_id (integer|null), pricing_tier_id (integer|null), product_filters (array|null), sales_filters (array|null), demand_modifiers (array|null), created_by_user_id (integer|null), created_by_user_name (string|null), manual_forecast_run_id (integer|null), post_forecast_adjustments (array|null), ignore_min_stock_level (boolean|null). A null is stored as-is in the snapshot; type validation still applies to non-null values.","body":{"mode":"raw","raw":"{\n  \"orders\": [\n    {\n      \"supplier_id\": 1,\n      \"pricing_tier_id\": null,\n      \"destination_warehouse_id\": 2,\n      \"approval_status\": \"draft\",\n      \"submit_to_supplier\": false,\n      \"lines\": [\n        {\n          \"supplier_product_id\": 101,\n          \"quantity\": 50,\n          \"unit_cost\": 12.5\n        },\n        {\n          \"supplier_product_id\": 102,\n          \"quantity\": 25,\n          \"unit_cost\": null\n        }\n      ],\n      \"forecast_config\": {\n        \"supplier_ids\": [\n          1\n        ],\n        \"forecast_type\": \"target_stock_level\",\n        \"target_stock_days\": null,\n        \"use_leadtime\": true,\n        \"use_measured_lead_time\": false,\n        \"use_moq\": true,\n        \"rounding_method\": \"round_half_up\",\n        \"product_filters\": {},\n        \"sales_filters\": {},\n        \"demand_modifiers\": [],\n        \"enforce_minimum_order_value\": false,\n        \"enforce_minimum_order_quantity\": false,\n        \"override_product_target_days\": false,\n        \"target_quantity\": 100\n      },\n      \"minimums_acknowledged\": false\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/purchase-orders","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","purchase-orders"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 500,\n      \"purchase_order_number\": \"PO-500\",\n      \"supplier_id\": 1,\n      \"supplier_name\": \"Acme Supplier Co.\",\n      \"order_status\": \"draft\",\n      \"total_cost\": 850,\n      \"created_at\": \"2024-10-15T09:00:00+00:00\"\n    }\n  ]\n}"}]},{"name":"Recalculate Costs","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/recalculate-costs","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","recalculate-costs"]},"description":"Recalculate supplier product costs using a different pricing tier. Used when the user switches pricing tier in the forecast UI.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\npriceing_tier_id: optional; pass null to use default pricing.\n\nReturns a map of supplier_product_id → new unit cost.","body":{"mode":"raw","raw":"{\n  \"supplier_product_ids\": [\n    101,\n    102,\n    103\n  ],\n  \"pricing_tier_id\": 5\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/recalculate-costs","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","recalculate-costs"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"costs\": {\n      \"101\": 11.5,\n      \"102\": 8.75,\n      \"103\": 22\n    }\n  }\n}"}]},{"name":"Get Supplier Summary","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/suppliers/summary","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","suppliers","summary"]},"description":"Get a summary of suppliers with product counts. Used for lazy-loading supplier tabs in the forecast UI before running the full forecast.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nforecast_type: optional — if provided, filters product counts based on type compatibility.\n\nproduct_filters: optional filter_groups structure.","body":{"mode":"raw","raw":"{\n  \"supplier_ids\": [\n    1,\n    2,\n    3\n  ],\n  \"product_filters\": {},\n  \"forecast_type\": \"sales_history\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/suppliers/summary","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","suppliers","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"supplier_id\": 1,\n      \"supplier_name\": \"Acme Supplier Co.\",\n      \"product_count\": 120,\n      \"active_product_count\": 105\n    },\n    {\n      \"supplier_id\": 2,\n      \"supplier_name\": \"Beta Parts Ltd.\",\n      \"product_count\": 45,\n      \"active_product_count\": 42\n    }\n  ]\n}"}]},{"name":"List Forecast Run Lines","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/{runId}/lines","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs",":runId","lines"],"variable":[{"key":"runId","value":"","description":"The run ID."}]},"description":"List the stored per-line breakdown for a forecast run, paginated. Each line captures the recommended quantity, confidence tier and drivers, and the inventory/sales snapshots that fed the calculation.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPath parameter: runId - the forecast run to read lines for.\n\nPagination: page (default 1), per_page (default 15)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/{runId}/lines","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs",":runId","lines"],"variable":[{"key":"runId","value":"","description":"The run ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 8801,\n      \"forecast_schedule_run_id\": 512,\n      \"product_id\": 55,\n      \"supplier_id\": 1,\n      \"final_quantity\": 50,\n      \"confidence_tier\": \"high\",\n      \"confidence_drivers\": {\n        \"history_days\": 742,\n        \"selling_density\": 0.86,\n        \"coefficient_of_variation\": 0.22,\n        \"days_with_sales\": 78,\n        \"from_fallback\": false,\n        \"reason\": \"Long, dense, stable sales history\"\n      },\n      \"inputs\": {\n        \"forecast_type\": \"sales_history\",\n        \"target_stock_days\": 30,\n        \"sales_history_days\": 90,\n        \"avg_daily_sales\": 2.5,\n        \"leadtime_days\": 14\n      },\n      \"inventory_snapshot\": {\n        \"current_stock\": 10,\n        \"incoming\": 0,\n        \"allocated\": 3\n      },\n      \"sales_snapshot\": {\n        \"total_sold\": 225,\n        \"period_days\": 90,\n        \"avg_daily_sales\": 2.5\n      },\n      \"forecast_method\": \"sales_history\",\n      \"baseline_method\": \"moving_average\",\n      \"product\": {\n        \"id\": 55,\n        \"sku\": \"PROD-001\",\n        \"name\": \"Widget A\"\n      },\n      \"created_at\": \"2024-10-15T09:00:00+00:00\",\n      \"updated_at\": \"2024-10-15T09:00:00+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 18\n}"}]},{"name":"Persist Forecast Run Lines","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/{runId}/lines","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs",":runId","lines"],"variable":[{"key":"runId","value":"","description":"The run ID."}]},"description":"Build a forecast from the supplied configuration and store its per-line breakdown against an existing forecast run. The work runs in the background; the response returns immediately with a job reference to poll for progress.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPath parameter: runId - the forecast run to attach the persisted lines to.\n\nBody: configuration (required) - the full forecast configuration (forecast type, stock targets, filters, and demand modifiers) to build from.\n\nReturns 202 Accepted with a job reference (tracked_job_log_id).","body":{"mode":"raw","raw":"{\n  \"configuration\": {\n    \"supplier_ids\": [\n      1\n    ],\n    \"forecast_type\": \"sales_history\",\n    \"target_stock_days\": 30,\n    \"sales_history_days\": 90,\n    \"use_leadtime\": true,\n    \"use_moq\": true,\n    \"rounding_method\": \"round_half_up\",\n    \"product_filters\": {},\n    \"sales_filters\": {},\n    \"demand_modifiers\": []\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/{runId}/lines","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs",":runId","lines"],"variable":[{"key":"runId","value":"","description":"The run ID."}]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 12345\n  },\n  \"message\": \"Forecast run persistence started\"\n}"}]},{"name":"Get Forecast Run Line","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/{runId}/lines/{productId}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs",":runId","lines",":productId"],"variable":[{"key":"runId","value":"","description":"The run ID."},{"key":"productId","value":"","description":"The product ID."}]},"description":"Get the stored forecast run line for a single product within a run, including the full confidence drivers and the inventory and sales snapshots taken at build time.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPath parameters: runId - the forecast run; productId - the product whose line to read.\n\nReturns 404 if no line exists for that run and product."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/{runId}/lines/{productId}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs",":runId","lines",":productId"],"variable":[{"key":"runId","value":"","description":"The run ID."},{"key":"productId","value":"","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 8801,\n    \"forecast_schedule_run_id\": 512,\n    \"product_id\": 55,\n    \"supplier_id\": 1,\n    \"final_quantity\": 50,\n    \"confidence_tier\": \"high\",\n    \"confidence_drivers\": {\n      \"history_days\": 742,\n      \"selling_density\": 0.86,\n      \"coefficient_of_variation\": 0.22,\n      \"days_with_sales\": 78,\n      \"from_fallback\": false,\n      \"reason\": \"Long, dense, stable sales history\"\n    },\n    \"inputs\": {\n      \"forecast_type\": \"sales_history\",\n      \"target_stock_days\": 30,\n      \"sales_history_days\": 90,\n      \"avg_daily_sales\": 2.5,\n      \"leadtime_days\": 14\n    },\n    \"inventory_snapshot\": {\n      \"current_stock\": 10,\n      \"incoming\": 0,\n      \"allocated\": 3\n    },\n    \"sales_snapshot\": {\n      \"total_sold\": 225,\n      \"period_days\": 90,\n      \"avg_daily_sales\": 2.5\n    },\n    \"forecast_method\": \"sales_history\",\n    \"baseline_method\": \"moving_average\",\n    \"product\": {\n      \"id\": 55,\n      \"sku\": \"PROD-001\",\n      \"name\": \"Widget A\"\n    },\n    \"created_at\": \"2024-10-15T09:00:00+00:00\",\n    \"updated_at\": \"2024-10-15T09:00:00+00:00\"\n  }\n}"}]},{"name":"List Configurations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations?supplier_id=1&forecast_type=sales_history&search=&created_by=","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations"],"query":[{"key":"supplier_id","value":"1","description":"Optional. Filter by supplier ID.","disabled":true},{"key":"forecast_type","value":"sales_history","description":"Optional. Filter by forecast type: sales_history, target_stock_level, fill_backorders.","disabled":true},{"key":"search","value":"","description":"Optional. Search by name.","disabled":true},{"key":"created_by","value":"","description":"Optional. Filter by creator user ID.","disabled":true}]},"description":"List all forecast configurations (non-paginated). Includes schedule counts.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns all configurations matching filters as a collection.\n\nEach configuration carries an `is_default` boolean: true when it is the **calling user's** default. The default is per user and per tenant, so the same configuration can be `is_default: true` for one user and false for another.\n\nMake / buy / transfer plans: warehouse_ids (integer[]), horizon_days (integer 7–365, default 90) and include_make (boolean) save a unified plan's scope. A configuration with warehouses (or no suppliers, or include_make true) runs as a make / buy / transfer plan — supplier_ids then becomes an optional filter and may be empty. One with suppliers and no warehouses stays a per-supplier purchase forecast. Schedules running a plan configuration build the plan and, when they auto-create, raise purchase orders only."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations?supplier_id=1&forecast_type=sales_history&search=&created_by=","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations"],"query":[{"key":"supplier_id","value":"1","description":"Optional. Filter by supplier ID.","disabled":true},{"key":"forecast_type","value":"sales_history","description":"Optional. Filter by forecast type: sales_history, target_stock_level, fill_backorders.","disabled":true},{"key":"search","value":"","description":"Optional. Search by name.","disabled":true},{"key":"created_by","value":"","description":"Optional. Filter by creator user ID.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Weekly Replenishment - Supplier A\",\n      \"description\": \"Standard weekly reorder for Supplier A products\",\n      \"supplier_ids\": [\n        1\n      ],\n      \"forecast_type\": \"sales_history\",\n      \"target_quantity\": null,\n      \"sales_history_days\": 90,\n      \"sales_start_date\": null,\n      \"sales_end_date\": null,\n      \"target_stock_days\": 30,\n      \"use_leadtime\": true,\n      \"use_measured_lead_time\": false,\n      \"use_moq\": true,\n      \"rounding_method\": \"round_half_up\",\n      \"pricing_tier_id\": null,\n      \"product_filters\": [],\n      \"sales_filters\": [],\n      \"demand_modifiers\": [],\n      \"schedules_count\": 2,\n      \"created_by\": {\n        \"id\": 10,\n        \"name\": \"Jane Smith\"\n      },\n      \"updated_by\": null,\n      \"is_default\": true,\n      \"created_at\": \"2024-01-15T08:00:00+00:00\",\n      \"updated_at\": \"2024-06-20T14:30:00+00:00\",\n      \"warehouse_ids\": [],\n      \"horizon_days\": null,\n      \"include_make\": null,\n      \"is_unified_plan\": false\n    }\n  ]\n}"}]},{"name":"Delete Configuration","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/{configuration}?force=false","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations",":configuration"],"query":[{"key":"force","value":"false","description":"Optional. If true, force delete even if in use by schedules.","disabled":true}],"variable":[{"key":"configuration","value":"1","description":"Configuration ID"}]},"description":"Delete a forecast configuration.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 204 on success. Returns 422 if the configuration is in use by schedules and force is not set.\n\nMake / buy / transfer plans: warehouse_ids (integer[]), horizon_days (integer 7–365, default 90) and include_make (boolean) save a unified plan's scope. A configuration with warehouses (or no suppliers, or include_make true) runs as a make / buy / transfer plan — supplier_ids then becomes an optional filter and may be empty. One with suppliers and no warehouses stays a per-supplier purchase forecast. Schedules running a plan configuration build the plan and, when they auto-create, raise purchase orders only."},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/{configuration}?force=false","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations",":configuration"],"query":[{"key":"force","value":"false","description":"Optional. If true, force delete even if in use by schedules.","disabled":true}],"variable":[{"key":"configuration","value":"1","description":"Configuration ID"}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":""}]},{"name":"Check Configuration Usage","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/{configuration}/usage","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations",":configuration","usage"],"variable":[{"key":"configuration","value":"1","description":"Configuration ID"}]},"description":"Check if a configuration is currently in use by any schedules.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nMake / buy / transfer plans: warehouse_ids (integer[]), horizon_days (integer 7–365, default 90) and include_make (boolean) save a unified plan's scope. A configuration with warehouses (or no suppliers, or include_make true) runs as a make / buy / transfer plan — supplier_ids then becomes an optional filter and may be empty. One with suppliers and no warehouses stays a per-supplier purchase forecast. Schedules running a plan configuration build the plan and, when they auto-create, raise purchase orders only."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/{configuration}/usage","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations",":configuration","usage"],"variable":[{"key":"configuration","value":"1","description":"Configuration ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"in_use\": true,\n    \"schedules_count\": 2\n  }\n}"}]},{"name":"Get My Default Configuration","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/default","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations","default"]},"description":"Get the current user's default forecast configuration, if one has been set.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe demand-planning UI calls this on initial load and pre-applies the returned configuration.\n\nReturns `{ \"data\": null }` (200) when the user has no default set.\n\nAuthentication: Requires Bearer token.\n\nMake / buy / transfer plans: warehouse_ids (integer[]), horizon_days (integer 7–365, default 90) and include_make (boolean) save a unified plan's scope. A configuration with warehouses (or no suppliers, or include_make true) runs as a make / buy / transfer plan — supplier_ids then becomes an optional filter and may be empty. One with suppliers and no warehouses stays a per-supplier purchase forecast. Schedules running a plan configuration build the plan and, when they auto-create, raise purchase orders only."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/default","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations","default"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Weekly Replenishment - Supplier A\",\n    \"forecast_type\": \"sales_history\",\n    \"supplier_ids\": [\n      1\n    ],\n    \"is_default\": true,\n    \"created_at\": \"2024-10-15T10:00:00+00:00\",\n    \"updated_at\": \"2024-10-15T10:00:00+00:00\",\n    \"warehouse_ids\": [],\n    \"horizon_days\": null,\n    \"include_make\": null,\n    \"is_unified_plan\": false\n  }\n}"}]},{"name":"Clear My Default Configuration","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/default","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations","default"]},"description":"Unset the current user's default configuration. Subsequent visits to demand planning load with a blank slate.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns 204 No Content on success. Safe to call when no default is set.\n\nAuthentication: Requires Bearer token.\n\nMake / buy / transfer plans: warehouse_ids (integer[]), horizon_days (integer 7–365, default 90) and include_make (boolean) save a unified plan's scope. A configuration with warehouses (or no suppliers, or include_make true) runs as a make / buy / transfer plan — supplier_ids then becomes an optional filter and may be empty. One with suppliers and no warehouses stays a per-supplier purchase forecast. Schedules running a plan configuration build the plan and, when they auto-create, raise purchase orders only."},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/default","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations","default"]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":""}]},{"name":"Set as My Default Configuration","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/{configuration}/set-default","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations",":configuration","set-default"],"variable":[{"key":"configuration","value":"1","description":"Configuration ID"}]},"description":"Mark this configuration as the current user's default. The default is loaded automatically in the demand-planning UI on page load.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAt most one default per user. Calling this on a different configuration replaces the previous default (no need to clear first).\n\nThe response's `is_default` flag will reflect the new state.\n\nAuthentication: Requires Bearer token.\n\nMake / buy / transfer plans: warehouse_ids (integer[]), horizon_days (integer 7–365, default 90) and include_make (boolean) save a unified plan's scope. A configuration with warehouses (or no suppliers, or include_make true) runs as a make / buy / transfer plan — supplier_ids then becomes an optional filter and may be empty. One with suppliers and no warehouses stays a per-supplier purchase forecast. Schedules running a plan configuration build the plan and, when they auto-create, raise purchase orders only."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/configurations/{configuration}/set-default","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","configurations",":configuration","set-default"],"variable":[{"key":"configuration","value":"1","description":"Configuration ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Weekly Replenishment - Supplier A\",\n    \"forecast_type\": \"sales_history\",\n    \"supplier_ids\": [\n      1\n    ],\n    \"is_default\": true,\n    \"created_at\": \"2024-10-15T10:00:00+00:00\",\n    \"updated_at\": \"2024-10-15T10:00:00+00:00\",\n    \"warehouse_ids\": [],\n    \"horizon_days\": null,\n    \"include_make\": null,\n    \"is_unified_plan\": false\n  }\n}"}]},{"name":"List Schedules","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules?is_enabled=&supplier_id=&forecast_type=&search=","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules"],"query":[{"key":"is_enabled","value":"","description":"Optional. Filter by enabled status (1 or 0).","disabled":true},{"key":"supplier_id","value":"","description":"Optional. Filter by supplier ID.","disabled":true},{"key":"forecast_type","value":"","description":"Optional. Filter by forecast type.","disabled":true},{"key":"search","value":"","description":"Optional. Search by name.","disabled":true}]},"description":"List all forecast schedules (non-paginated).\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules?is_enabled=&supplier_id=&forecast_type=&search=","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules"],"query":[{"key":"is_enabled","value":"","description":"Optional. Filter by enabled status (1 or 0).","disabled":true},{"key":"supplier_id","value":"","description":"Optional. Filter by supplier ID.","disabled":true},{"key":"forecast_type","value":"","description":"Optional. Filter by forecast type.","disabled":true},{"key":"search","value":"","description":"Optional. Search by name.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Weekly Monday Run\",\n      \"is_enabled\": true,\n      \"schedule_time\": \"06:00:00\",\n      \"days_of_week\": [\n        1\n      ],\n      \"timezone\": \"America/New_York\",\n      \"forecast_configuration_id\": 1,\n      \"alert_channels\": [\n        \"email\"\n      ],\n      \"alert_email_recipients\": [\n        \"ops@company.com\"\n      ],\n      \"slack_webhook_url\": null,\n      \"auto_create_po\": true,\n      \"po_status\": \"approved\",\n      \"separate_po_by_brand\": false,\n      \"auto_receive_po\": false,\n      \"misfire_policy\": \"run_once\",\n      \"misfire_policy_label\": \"Run Once\",\n      \"starting_deadline_minutes\": 60,\n      \"last_run_at\": \"2024-10-14T06:05:00+00:00\",\n      \"next_run_at\": \"2024-10-21T10:00:00+00:00\",\n      \"created_at\": \"2024-01-15T08:00:00+00:00\",\n      \"updated_at\": \"2024-06-20T14:30:00+00:00\"\n    }\n  ]\n}"}]},{"name":"Create Schedule","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules"]},"description":"Create a new forecast schedule.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nschedule_time: HH:MM:SS format.\n\ndays_of_week: array of integers 0-6 (0=Sunday, 1=Monday, ..., 6=Saturday).\n\nalert_channels: array of 'email' and/or 'slack'.\n\nPO settings:\n- auto_create_po (boolean): create POs automatically from the forecast results.\n- po_status: draft, approved, or approved_sent.\n- separate_po_by_brand (boolean): split POs per brand instead of one per supplier.\n- auto_receive_po (boolean): automatically mark the created PO as received once it has been created and approved. Ignored when po_status is draft (a draft PO cannot be received).\n\nReturns 201.","body":{"mode":"raw","raw":"{\n  \"name\": \"Weekly Monday Run\",\n  \"is_enabled\": true,\n  \"schedule_time\": \"06:00:00\",\n  \"days_of_week\": [\n    1\n  ],\n  \"timezone\": \"America/New_York\",\n  \"forecast_configuration_id\": 1,\n  \"alert_channels\": [\n    \"email\"\n  ],\n  \"alert_email_recipients\": [\n    \"ops@company.com\"\n  ],\n  \"slack_webhook_url\": null,\n  \"auto_create_po\": true,\n  \"po_status\": \"approved\",\n  \"separate_po_by_brand\": false,\n  \"auto_receive_po\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Weekly Monday Run\",\n    \"is_enabled\": true,\n    \"schedule_time\": \"06:00:00\",\n    \"days_of_week\": [\n      1\n    ],\n    \"timezone\": \"America/New_York\",\n    \"auto_create_po\": true,\n    \"po_status\": \"approved\",\n    \"separate_po_by_brand\": false,\n    \"auto_receive_po\": false,\n    \"next_run_at\": \"2024-10-21T10:00:00+00:00\",\n    \"created_at\": \"2024-10-15T09:00:00+00:00\"\n  }\n}"}]},{"name":"Get Schedule","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/{schedule}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules",":schedule"],"variable":[{"key":"schedule","value":"1","description":"Schedule ID"}]},"description":"Get a single forecast schedule with full details.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nLoads configuration, createdByUser, updatedByUser, latestRun relationships."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/{schedule}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules",":schedule"],"variable":[{"key":"schedule","value":"1","description":"Schedule ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Weekly Monday Run\",\n    \"is_enabled\": true,\n    \"schedule_time\": \"06:00:00\",\n    \"days_of_week\": [\n      1\n    ],\n    \"timezone\": \"America/New_York\",\n    \"forecast_configuration_id\": 1,\n    \"configuration\": {\n      \"id\": 1,\n      \"name\": \"Weekly Replenishment - Supplier A\"\n    },\n    \"auto_create_po\": true,\n    \"po_status\": \"approved\",\n    \"separate_po_by_brand\": false,\n    \"auto_receive_po\": false,\n    \"last_run_at\": \"2024-10-14T06:05:00+00:00\",\n    \"next_run_at\": \"2024-10-21T10:00:00+00:00\",\n    \"latest_run\": {\n      \"id\": 50,\n      \"status\": \"completed\",\n      \"started_at\": \"2024-10-14T06:00:12+00:00\",\n      \"completed_at\": \"2024-10-14T06:05:44+00:00\"\n    }\n  }\n}"}]},{"name":"Update Schedule","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/{schedule}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules",":schedule"],"variable":[{"key":"schedule","value":"1","description":"Schedule ID"}]},"description":"Update a forecast schedule. All fields are optional; merges with existing data.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPO settings (all optional):\n- auto_create_po (boolean), po_status (draft|approved|approved_sent), separate_po_by_brand (boolean), auto_receive_po (boolean). auto_receive_po is ignored when po_status is draft.","body":{"mode":"raw","raw":"{\n  \"schedule_time\": \"07:00:00\",\n  \"days_of_week\": [\n    1,\n    4\n  ],\n  \"auto_create_po\": true,\n  \"po_status\": \"approved\",\n  \"auto_receive_po\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/{schedule}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules",":schedule"],"variable":[{"key":"schedule","value":"1","description":"Schedule ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Weekly Monday Run\",\n    \"schedule_time\": \"07:00:00\",\n    \"days_of_week\": [\n      1,\n      4\n    ],\n    \"updated_at\": \"2024-10-16T11:00:00+00:00\"\n  }\n}"}]},{"name":"Delete Schedule","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/{schedule}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules",":schedule"],"variable":[{"key":"schedule","value":"1","description":"Schedule ID"}]},"description":"Delete a forecast schedule.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 204 on success."},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/{schedule}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules",":schedule"],"variable":[{"key":"schedule","value":"1","description":"Schedule ID"}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":""}]},{"name":"Toggle Schedule","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/{schedule}/toggle","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules",":schedule","toggle"],"variable":[{"key":"schedule","value":"1","description":"Schedule ID"}]},"description":"Toggle a schedule's enabled/disabled status.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/{schedule}/toggle","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules",":schedule","toggle"],"variable":[{"key":"schedule","value":"1","description":"Schedule ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Weekly Monday Run\",\n    \"is_enabled\": false,\n    \"updated_at\": \"2024-10-16T11:00:00+00:00\"\n  }\n}"}]},{"name":"Run Schedule Now","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/{schedule}/run","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules",":schedule","run"],"variable":[{"key":"schedule","value":"1","description":"Schedule ID"}]},"description":"Manually trigger a forecast schedule. The run is queued and executes in the background, because a large supplier forecast can take longer than a single HTTP request allows.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns 202 Accepted with a tracked_job_log_id. The finished run (status, results_summary, linked purchase orders) appears in the schedule's run history — GET /api/inventory-forecasting/schedules/{schedule}/runs.\n\nAuthentication: Requires Bearer token.\n\nReturns 404 if the schedule does not exist."},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/{schedule}/run","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules",":schedule","run"],"variable":[{"key":"schedule","value":"1","description":"Schedule ID"}]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 48213\n  },\n  \"message\": \"Schedule run started\"\n}"}]},{"name":"Get Schedule Runs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/{schedule}/runs","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules",":schedule","runs"],"variable":[{"key":"schedule","value":"1","description":"Schedule ID"}]},"description":"Get paginated run history for a specific schedule.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/{schedule}/runs","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules",":schedule","runs"],"variable":[{"key":"schedule","value":"1","description":"Schedule ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 55,\n      \"status\": \"completed\",\n      \"run_type\": \"Scheduled\",\n      \"started_at\": \"2024-10-14T06:00:12+00:00\",\n      \"completed_at\": \"2024-10-14T06:05:44+00:00\",\n      \"duration_seconds\": 332\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 2,\n  \"per_page\": 15,\n  \"total\": 22\n}"}]},{"name":"Get Schedule Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/{schedule}/activity-log?per_page=25","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules",":schedule","activity-log"],"query":[{"key":"per_page","value":"25","description":"Optional. Items per page (default 25).","disabled":true}],"variable":[{"key":"schedule","value":"1","description":"Schedule ID"}]},"description":"Get the Spatie activity log for a specific schedule showing all changes.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/{schedule}/activity-log?per_page=25","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules",":schedule","activity-log"],"query":[{"key":"per_page","value":"25","description":"Optional. Items per page (default 25).","disabled":true}],"variable":[{"key":"schedule","value":"1","description":"Schedule ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1001,\n      \"description\": \"updated\",\n      \"event\": \"updated\",\n      \"properties\": {\n        \"old\": {\n          \"is_enabled\": true\n        },\n        \"attributes\": {\n          \"is_enabled\": false\n        }\n      },\n      \"causer_name\": \"Jane Smith\",\n      \"created_at\": \"2024-10-15T09:00:00+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 5\n}"}]},{"name":"Get Schedule Limits","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/limits?supplier_ids[]=1&date=2024-10-15","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules","limits"],"query":[{"key":"supplier_ids[]","value":"1","description":"Required. Array of supplier IDs to check limits for.","disabled":true},{"key":"date","value":"2024-10-15","description":"Optional. Date to check limits for (defaults to today).","disabled":true}]},"description":"Get supplier run limits status. Shows how many runs are scheduled for each supplier on a given date.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/limits?supplier_ids[]=1&date=2024-10-15","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules","limits"],"query":[{"key":"supplier_ids[]","value":"1","description":"Required. Array of supplier IDs to check limits for.","disabled":true},{"key":"date","value":"2024-10-15","description":"Optional. Date to check limits for (defaults to today).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"supplier_id\": 1,\n      \"supplier_name\": \"Acme Supplier Co.\",\n      \"runs_today\": 1,\n      \"limit\": 3,\n      \"can_run\": true\n    }\n  ]\n}"}]},{"name":"Get Schedule Statistics","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/statistics","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules","statistics"]},"description":"Get aggregate statistics for all forecast schedules.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`success_rate` is the percentage of scheduled runs that completed successfully this week, over `runs_scored_this_week` (completed + failed runs). It is **null** when no run has been scored yet — an empty set has no success rate, so render an explicit \"no runs yet\" state rather than treating null as 0% or 100%.\n\nMonetary totals are in the account's base currency.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/statistics","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules","statistics"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_schedules\": 5,\n    \"enabled_schedules\": 4,\n    \"runs_today\": 2,\n    \"runs_this_week\": 9,\n    \"success_rate\": 88.89,\n    \"runs_scored_this_week\": 9,\n    \"pos_created_today\": 3,\n    \"pos_created_this_week\": 11,\n    \"total_value_today\": 4820.5,\n    \"total_value_this_week\": 23140.75\n  }\n}"}]},{"name":"Get Schedule Timeline","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/timeline?start_date=2024-10-15&end_date=2024-10-22","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules","timeline"],"query":[{"key":"start_date","value":"2024-10-15","description":"Optional. Start date for timeline (defaults to today).","disabled":true},{"key":"end_date","value":"2024-10-22","description":"Optional. End date for timeline (defaults to today + 7 days).","disabled":true}]},"description":"Get upcoming schedule runs for a date range.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/timeline?start_date=2024-10-15&end_date=2024-10-22","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules","timeline"],"query":[{"key":"start_date","value":"2024-10-15","description":"Optional. Start date for timeline (defaults to today).","disabled":true},{"key":"end_date","value":"2024-10-22","description":"Optional. End date for timeline (defaults to today + 7 days).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"date\": \"2024-10-21\",\n      \"schedules\": [\n        {\n          \"id\": 1,\n          \"name\": \"Weekly Monday Run\",\n          \"schedule_time\": \"06:00:00\"\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"Get Today's Schedules","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/today?date=2024-10-15","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules","today"],"query":[{"key":"date","value":"2024-10-15","description":"Optional. Date to get schedules for (defaults to today).","disabled":true}]},"description":"Get schedules that run on the given day of the week.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedules/today?date=2024-10-15","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedules","today"],"query":[{"key":"date","value":"2024-10-15","description":"Optional. Date to get schedules for (defaults to today).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Weekly Monday Run\",\n      \"is_enabled\": true,\n      \"schedule_time\": \"06:00:00\",\n      \"next_run_at\": \"2024-10-21T10:00:00+00:00\"\n    }\n  ]\n}"}]},{"name":"List All Runs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs"]},"description":"Get all forecast runs across all schedules (both manual and scheduled), paginated. Loads the schedule, triggering user, and linked purchase orders.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nText search uses filter[search] (matches run ID, schedule name, or user name). Advanced filtering uses filter_groups — a base64-encoded tree of AND/OR condition groups over: id, status, run_type (manual or scheduled), started_at, purge_at, created_at, updated_at. Sortable: id, started_at, status, run_type, purge_at (default -started_at, newest first).\n\nMake / buy / transfer plan runs: `results_summary.mode` is `unified` and `results_summary.sources` holds the plan summary per source (make, buy, transfer, unsourced: lines, actionable, units, value, suppliers). `manufacturing_order_ids` and `warehouse_transfer_ids` list the drafts a plan created, alongside `purchase_order_ids`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 55,\n      \"forecast_schedule_id\": 1,\n      \"user_id\": 10,\n      \"is_manual_run\": false,\n      \"is_scheduled_run\": true,\n      \"run_type\": \"Scheduled\",\n      \"status\": \"completed\",\n      \"started_at\": \"2024-10-14T06:00:12+00:00\",\n      \"completed_at\": \"2024-10-14T06:05:44+00:00\",\n      \"duration_seconds\": 332,\n      \"duration_human\": \"5m 32s\",\n      \"results_summary\": {\n        \"suppliers_processed\": 1,\n        \"skus_to_order\": 18,\n        \"purchase_orders_created\": 1,\n        \"total_value\": 12500\n      },\n      \"error_message\": null,\n      \"purchase_order_ids\": [\n        500\n      ],\n      \"has_purchase_orders\": true,\n      \"schedule\": {\n        \"id\": 1,\n        \"name\": \"Weekly Monday Run\"\n      },\n      \"user\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 4,\n  \"per_page\": 15,\n  \"total\": 48\n}"}]},{"name":"Get Recent Runs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/recent?limit=5","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs","recent"],"query":[{"key":"limit","value":"5","description":"Optional. Number of recent runs to return (default 5, max 20).","disabled":true}]},"description":"Get recent runs (not paginated). Useful for dashboard widgets.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/recent?limit=5","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs","recent"],"query":[{"key":"limit","value":"5","description":"Optional. Number of recent runs to return (default 5, max 20).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 55,\n      \"status\": \"completed\",\n      \"run_type\": \"Scheduled\",\n      \"started_at\": \"2024-10-14T06:00:12+00:00\",\n      \"completed_at\": \"2024-10-14T06:05:44+00:00\"\n    }\n  ]\n}"}]},{"name":"Get Run Details","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/{runId}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs",":runId"],"variable":[{"key":"runId","value":"55","description":"Run ID"}]},"description":"Get full details for a single forecast run.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nLoads schedule, user, supplierRuns, linkedPurchaseOrders.\n\nReturns 404 if run not found."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/{runId}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs",":runId"],"variable":[{"key":"runId","value":"55","description":"Run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 55,\n    \"forecast_schedule_id\": 1,\n    \"status\": \"completed\",\n    \"run_type\": \"Scheduled\",\n    \"started_at\": \"2024-10-14T06:00:12+00:00\",\n    \"completed_at\": \"2024-10-14T06:05:44+00:00\",\n    \"duration_seconds\": 332,\n    \"results_summary\": {\n      \"purchase_orders_created\": 1\n    },\n    \"linked_purchase_orders\": [\n      {\n        \"id\": 500,\n        \"purchase_order_number\": \"PO-500\",\n        \"supplier_name\": \"Acme Supplier Co.\",\n        \"total_cost\": 12500,\n        \"order_status\": \"draft\"\n      }\n    ]\n  }\n}"}]},{"name":"Create Manual Run","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/manual","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs","manual"]},"description":"Create and persist a manual (ad-hoc) forecast run record. Called when users run forecasts manually from the UI.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nconfiguration_snapshot: required — stores the forecast config used.\n\nresults_summary: required — stores aggregate results.\n\npurchase_order_ids: optional — IDs of POs created from this run.\n\nReturns 201.","body":{"mode":"raw","raw":"{\n  \"configuration_snapshot\": {\n    \"supplier_ids\": [\n      1\n    ],\n    \"forecast_type\": \"sales_history\",\n    \"target_stock_days\": 30\n  },\n  \"results_summary\": {\n    \"suppliers_processed\": 1,\n    \"skus_to_order\": 18,\n    \"purchase_orders_created\": 0,\n    \"total_value\": 12500\n  },\n  \"purchase_order_ids\": []\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/manual","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs","manual"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 60,\n    \"is_manual_run\": true,\n    \"status\": \"completed\",\n    \"run_type\": \"Manual\",\n    \"results_summary\": {\n      \"suppliers_processed\": 1,\n      \"skus_to_order\": 18\n    },\n    \"created_at\": \"2024-10-15T09:00:00+00:00\"\n  }\n}"}]},{"name":"Link Purchase Orders to Run","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/{runId}/link-purchase-orders","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs",":runId","link-purchase-orders"],"variable":[{"key":"runId","value":"60","description":"Run ID"}]},"description":"Link purchase orders to an existing manual forecast run. Can only be used for manual runs.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 404 if run not found. Returns 400 if not a manual run.","body":{"mode":"raw","raw":"{\n  \"purchase_order_ids\": [\n    500,\n    501\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/{runId}/link-purchase-orders","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs",":runId","link-purchase-orders"],"variable":[{"key":"runId","value":"60","description":"Run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 60,\n    \"purchase_order_ids\": [\n      500,\n      501\n    ],\n    \"has_purchase_orders\": true\n  }\n}"}]},{"name":"Re-run Forecast from Previous Run","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/{runId}/rerun","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs",":runId","rerun"],"variable":[{"key":"runId","value":"55","description":"Run ID"}]},"description":"Re-run a forecast using the configuration from a previous run.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFor scheduled runs with an active schedule, the schedule is queued for execution in the background and the endpoint returns 202 Accepted with type = queued and a tracked_job_log_id; the new run appears in the schedule's run history when it finishes. For manual runs or runs whose schedule was deleted, returns 200 with the configuration_snapshot for manual re-execution.\n\nRequest body fields:\n- preview (boolean, optional, default false): When true, computes the forecast for inspection only. Skips PO creation (even when the schedule has auto_create_po enabled), skips alerts, and does not advance the schedule's next_run_at. The resulting run is recorded with results_summary.is_preview = true. Has no effect on manual runs (configuration_only path).\n\nAuthentication: Requires Bearer token.\n\nReturns 404 if run not found.","body":{"mode":"raw","raw":"{\n  \"preview\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/{runId}/rerun","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs",":runId","rerun"],"variable":[{"key":"runId","value":"55","description":"Run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"type\": \"configuration_only\",\n    \"message\": \"This run's schedule no longer exists. Use the configuration to run a new forecast manually.\",\n    \"configuration_snapshot\": {\n      \"supplier_ids\": [\n        1\n      ],\n      \"forecast_type\": \"sales_history\"\n    }\n  }\n}"}]},{"name":"Export Forecast","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/export","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","export"]},"description":"Build a forecast and export the results to a file. Returns a download URL/filename.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nformat: csv (default) or excel.\n\ninclude_calculation_details: include the per-product calculation breakdown (default true).\n\ninclude_sales_history: include historical sales totals (default false).\n\nconfig: the same forecast settings used for the build (forecast_type, sales window, lead time, MOQ, rounding, filters, demand_modifiers, and so on).\n\nBASELINE DECOMPOSITION COLUMNS\nWith include_calculation_details on (the default), the export adds the baseline-decomposition columns that mirror each line's inputs object:\n- Baseline Method\n- Base Velocity (the flat mean before trend, base_flat)\n- Trend Factor\n- Trend Capped\n- Promo Factor\n- Promo Window\n- Promo Current Baseline\n- Anomaly Days Excluded\n- Anomaly Days Smoothed\nSend the same baseline_method / trend / exclude_detected_anomalies / demand_modifiers settings inside config that you would pass to the build endpoint so the exported numbers match.\n\ninclude_confidence (boolean, default true) adds the confidence tier/score, the prediction interval, and the plain-language rationale columns to the export.\n\nReturns 422 if too many products.","body":{"mode":"raw","raw":"{\n  \"supplier_ids\": [\n    1,\n    2\n  ],\n  \"format\": \"csv\",\n  \"include_calculation_details\": true,\n  \"include_sales_history\": false,\n  \"config\": {\n    \"forecast_type\": \"sales_history\",\n    \"sales_history_days\": 90,\n    \"target_stock_days\": 30,\n    \"use_leadtime\": true,\n    \"use_measured_lead_time\": false,\n    \"use_moq\": true,\n    \"rounding_method\": \"round_half_up\",\n    \"product_filters\": {},\n    \"sales_filters\": {},\n    \"demand_modifiers\": []\n  },\n  \"include_confidence\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/export","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"filename\": \"forecast_export_20241015_090000.csv\",\n    \"download_url\": \"/api/inventory-forecasting/download/forecast_export_20241015_090000.csv\"\n  }\n}"}]},{"name":"Download Export File","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/download/{filename}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","download",":filename"],"variable":[{"key":"filename","value":"forecast_export_20241015_090000.csv","description":"Filename returned from export endpoint"}]},"description":"Download an exported forecast file. The filename is returned from the POST /export endpoint.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nResponse is a file download (text/csv or application/vnd.openxmlformats-officedocument.spreadsheetml.sheet).\n\nReturns 404 if file not found or has expired."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/download/{filename}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","download",":filename"],"variable":[{"key":"filename","value":"forecast_export_20241015_090000.csv","description":"Filename returned from export endpoint"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"sku,name,recommended_quantity,unit_cost,total_cost\nPROD-001,Widget A,50,12.50,625.00"}]},{"name":"Detect Demand Anomalies","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/anomalies/detect","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","anomalies","detect"]},"description":"Detect one-off demand anomalies (spikes, drops, and stockout gaps) in the daily sales history of one or more products. Runs synchronously, persists each flagged point, and returns them.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nThe detector scores each day with a robust modified z-score built from the median and median absolute deviation, so a handful of extreme days cannot mask the rest. The k threshold is expressed in robust standard deviations; lower values flag more points. Each day is classified as:\n- spike_up: demand far above the local norm (often a promotion).\n- drop_down: demand far below the local norm.\n- stockout_zero: a zero-sales gap consistent with being out of stock.\n\nDetected points feed two workflows:\n1. Resolve them (see Resolve / Bulk Resolve Demand Anomalies) as excluded, smoothed, or kept_tagged. Points resolved as excluded or smoothed are the ones a build with exclude_detected_anomalies true removes or replaces before computing the baseline, de-contaminating the velocity.\n2. Cluster spike_up points into candidate promotions via Suggest Promo Windows. A spike can also be linked to a promo_window_id when resolved as kept_tagged.\n\nBody fields:\n- product_ids (required): array of product IDs, at least one.\n- k (optional): sensitivity threshold in robust standard deviations, 0.1-20 (default 3.0). Lower flags more points.\n- start_date / end_date (optional, YYYY-MM-DD): detection window. Defaults to the trailing 365 days.\n- sales_filters (optional): object narrowing which sales feed the series.\n- warehouse_id (optional): restrict the series to one warehouse.\n\nEach flagged point has a direction of spike_up, drop_down, or stockout_zero and a status of detected until it is resolved.","body":{"mode":"raw","raw":"{\n  \"product_ids\": [\n    55,\n    56\n  ],\n  \"k\": 3,\n  \"start_date\": \"2023-07-01\",\n  \"end_date\": \"2024-07-01\",\n  \"sales_filters\": {},\n  \"warehouse_id\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/anomalies/detect","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","anomalies","detect"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 55,\n      \"anomaly_date\": \"2024-07-04\",\n      \"direction\": \"spike_up\",\n      \"actual_value\": 145,\n      \"expected_value\": 22,\n      \"residual_score\": 8.7,\n      \"status\": \"detected\",\n      \"is_stockout\": false\n    },\n    {\n      \"product_id\": 55,\n      \"anomaly_date\": \"2024-09-02\",\n      \"direction\": \"stockout_zero\",\n      \"actual_value\": 0,\n      \"expected_value\": 18,\n      \"residual_score\": 5.1,\n      \"status\": \"detected\",\n      \"is_stockout\": true\n    }\n  ]\n}"}]},{"name":"List Demand Anomalies","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/anomalies?product_id=55","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","anomalies"],"query":[{"key":"product_id","value":"55","description":"Required. Product to list anomalies for.","disabled":true}]},"description":"List a product's detected demand anomalies, most recent first, optionally scoped to a single resolution status.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nQuery parameters:\n- product_id (required): the product to list anomalies for.\n- status (optional): filter by resolution status.\n  - detected: flagged, not yet actioned.\n  - excluded: dropped from the baseline (a build with exclude_detected_anomalies true removes these days).\n  - smoothed: replaced with the expected value before the baseline is computed.\n  - kept_tagged: kept in the baseline, optionally attributed to a promotion via promo_window_id."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/anomalies?product_id=55","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","anomalies"],"query":[{"key":"product_id","value":"55","description":"Required. Product to list anomalies for.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3001,\n      \"product_id\": 55,\n      \"anomaly_date\": \"2024-07-04\",\n      \"direction\": \"spike_up\",\n      \"actual_value\": 145,\n      \"expected_value\": 22,\n      \"residual_score\": 8.7,\n      \"method\": \"mad_zscore\",\n      \"status\": \"detected\",\n      \"promo_window_id\": null,\n      \"resolved_by\": null,\n      \"resolved_at\": null\n    }\n  ]\n}"}]},{"name":"Resolve Demand Anomaly","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/anomalies/{id}/resolve","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","anomalies",":id","resolve"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Record a decision for a single demand anomaly so future forecasts treat the point correctly.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nHow each status affects a subsequent build (only when that build sets exclude_detected_anomalies true):\n- excluded: drop the day from the history before the baseline is computed. Use for a true one-off that should not count.\n- smoothed: replace the day with its expected (model) value, keeping the day but removing the spike/gap. Use when the day should contribute a normal amount.\n- kept_tagged: keep the day as-is, optionally attributed to a promotion via promo_window_id. Excluded and smoothed days are the ones anomaly cleansing acts on; kept_tagged days stay in the baseline. detected resets the point to unresolved.\n\nPath parameter: id - the anomaly to resolve.\n\nBody fields:\n- status (required): excluded, smoothed, kept_tagged, or detected.\n- promo_window_id (optional): link the point to a promo window when kept_tagged, associating the spike with a known promotion.","body":{"mode":"raw","raw":"{\n  \"status\": \"kept_tagged\",\n  \"promo_window_id\": 12\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/anomalies/{id}/resolve","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","anomalies",":id","resolve"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3001,\n    \"product_id\": 55,\n    \"anomaly_date\": \"2024-07-04\",\n    \"direction\": \"spike_up\",\n    \"actual_value\": 145,\n    \"expected_value\": 22,\n    \"residual_score\": 8.7,\n    \"method\": \"mad_zscore\",\n    \"status\": \"kept_tagged\",\n    \"promo_window_id\": 12,\n    \"resolved_by\": 10,\n    \"resolved_at\": \"2024-10-15T09:30:00+00:00\"\n  }\n}"}]},{"name":"Bulk Resolve Demand Anomalies","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/anomalies/bulk-resolve","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","anomalies","bulk-resolve"]},"description":"Apply the same resolution decision to many demand anomalies at once - the fast path after Detect Demand Anomalies. Unknown IDs are skipped; the response reports how many were actually updated.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nStatus meanings match Resolve Demand Anomaly: excluded (drop from baseline), smoothed (replace with expected value), kept_tagged (keep, optionally tagged to a promotion), detected (reset). Only excluded and smoothed points are removed/replaced when a build runs with exclude_detected_anomalies true.\n\nBody fields:\n- anomaly_ids (required): array of anomaly IDs, at least one.\n- status (required): excluded, smoothed, kept_tagged, or detected.\n- promo_window_id (optional): link the points to a promo window when kept_tagged.","body":{"mode":"raw","raw":"{\n  \"anomaly_ids\": [\n    3001,\n    3002,\n    3003\n  ],\n  \"status\": \"excluded\",\n  \"promo_window_id\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/anomalies/bulk-resolve","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","anomalies","bulk-resolve"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"resolved_count\": 3\n  }\n}"}]},{"name":"List Promo Windows","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/promo-windows","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","promo-windows"]},"description":"List all promo windows, including any realised lift (measured_lift_pct) and post-promo dip (post_promo_dip_pct) already measured for each. Use a window's id as the promo_window_id of a promo_lift demand modifier to re-apply its lift on a build.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nsuggested_modifiers: the forward Event modifiers built from the window's persisted measurement (the same shape Measure Promo Lift returns), so a previously measured window can be applied without re-measuring. Empty until the window has been measured. The second modifier is named '&lt;window> post-promo dip' when post_promo_dip_pct is negative and '&lt;window> post-promo carryover' when demand stayed elevated after the promo (positive)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/promo-windows","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","promo-windows"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"name\": \"Black Friday 2024\",\n      \"start_date\": \"2024-11-25\",\n      \"end_date\": \"2024-11-30\",\n      \"order_tag\": \"black-friday\",\n      \"product_scope\": [\n        55,\n        56\n      ],\n      \"channel_scope\": [\n        \"shopify\"\n      ],\n      \"recurrence\": \"annual\",\n      \"measured_lift_pct\": 34.78,\n      \"post_promo_dip_pct\": -11.2,\n      \"measured_at\": \"2024-12-05T08:00:00+00:00\",\n      \"suggested_modifiers\": [\n        {\n          \"type\": \"event\",\n          \"value\": 35,\n          \"apply_to\": \"all\",\n          \"name\": \"Black Friday 2024 lift\",\n          \"period_key\": null,\n          \"start_date\": \"2025-11-25\",\n          \"end_date\": \"2025-11-30\",\n          \"product_id\": null\n        },\n        {\n          \"type\": \"event\",\n          \"value\": -11,\n          \"apply_to\": \"all\",\n          \"name\": \"Black Friday 2024 post-promo dip\",\n          \"period_key\": null,\n          \"start_date\": \"2025-12-01\",\n          \"end_date\": \"2025-12-14\",\n          \"product_id\": null\n        }\n      ],\n      \"created_by\": 10,\n      \"created_at\": \"2024-11-01T12:00:00+00:00\",\n      \"updated_at\": \"2024-12-05T08:00:00+00:00\"\n    }\n  ]\n}"}]},{"name":"Create Promo Window","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/promo-windows","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","promo-windows"]},"description":"Create a promo window - a named date range, optionally scoped to an order tag, a set of products, and sales channels. A promo window is the anchor for measuring a promotion's demand lift and later re-applying it.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nTypical flow:\n1. Create the window over the promotion's date range (optionally scoped by order_tag, product_scope, channel_scope).\n2. Call Measure Promo Lift for a product to compute the realised in-window lift and the post-promo dip; the measured lift is stored on the window.\n3. Reference the window from a build request as a demand_modifiers entry of type promo_lift (promo_window_id). The stored lift is applied MULTIPLICATIVELY to the current baseline, so a window measured last year automatically re-scales to this year's larger baseline.\n\nBody fields:\n- name (required): label for the window, up to 255 characters.\n- start_date (required, YYYY-MM-DD): must be on or before end_date.\n- end_date (required, YYYY-MM-DD): must be on or after start_date.\n- order_tag (optional): the order tag that marks orders belonging to this promotion.\n- recurrence (optional): one_off (a single-date promotion) or annual (repeats on the same dates each year, so its lift can be re-applied to the next occurrence). Default one_off.\n- product_scope (optional): array of product IDs the window applies to.\n- channel_scope (optional): array of sales channels the window applies to.\n\nsuggested_modifiers: the forward Event modifiers built from the window's persisted measurement (the same shape Measure Promo Lift returns), so a previously measured window can be applied without re-measuring. Empty until the window has been measured. The second modifier is named '&lt;window> post-promo dip' when post_promo_dip_pct is negative and '&lt;window> post-promo carryover' when demand stayed elevated after the promo (positive).","body":{"mode":"raw","raw":"{\n  \"name\": \"Black Friday 2024\",\n  \"start_date\": \"2024-11-25\",\n  \"end_date\": \"2024-11-30\",\n  \"order_tag\": \"black-friday\",\n  \"recurrence\": \"annual\",\n  \"product_scope\": [\n    55,\n    56\n  ],\n  \"channel_scope\": [\n    \"shopify\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/promo-windows","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","promo-windows"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"name\": \"Black Friday 2024\",\n    \"start_date\": \"2024-11-25\",\n    \"end_date\": \"2024-11-30\",\n    \"order_tag\": \"black-friday\",\n    \"product_scope\": [\n      55,\n      56\n    ],\n    \"channel_scope\": [\n      \"shopify\"\n    ],\n    \"recurrence\": \"annual\",\n    \"measured_lift_pct\": null,\n    \"post_promo_dip_pct\": null,\n    \"measured_at\": null,\n    \"suggested_modifiers\": [],\n    \"created_by\": 10,\n    \"created_at\": \"2024-11-01T12:00:00+00:00\",\n    \"updated_at\": \"2024-12-05T08:00:00+00:00\"\n  }\n}"}]},{"name":"Suggest Promo Windows","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/promo-windows/suggest","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","promo-windows","suggest"]},"description":"Suggest candidate promo windows for a product by clustering its detected upward demand spikes into contiguous date ranges, so a promotion can be confirmed instead of typed by hand.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nThis consumes the same spike_up points produced by Detect Demand Anomalies: it clusters unresolved upward spikes into candidate date ranges. Downward and stockout anomalies are never suggested. Candidates are ranked by peak spike severity and capped at 10. A confirmed suggestion can be created as a promo window (Create Promo Window), measured (Measure Promo Lift), and then re-applied on a future build as a promo_lift demand modifier.\n\nBody fields:\n- product_id (required): the product to analyse.\n- min_gap_days (optional): a gap larger than this many days splits two spike clusters into separate windows, 0-60 (default 3).\n- lookback_days (optional): how far back to scan for spikes, 1-1095 (default 365).\n\nEach suggestion reports the cluster's start_date and end_date, the peak spike score, the total excess units above expectation, the number of flagged days, and whether an existing promo window already overlaps the range.","body":{"mode":"raw","raw":"{\n  \"product_id\": 55,\n  \"min_gap_days\": 3,\n  \"lookback_days\": 365\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/promo-windows/suggest","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","promo-windows","suggest"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"start_date\": \"2024-11-22\",\n      \"end_date\": \"2024-11-29\",\n      \"peak_residual_score\": 6.1,\n      \"total_excess_units\": 420,\n      \"day_count\": 8,\n      \"already_has_window\": false\n    },\n    {\n      \"start_date\": \"2024-07-03\",\n      \"end_date\": \"2024-07-06\",\n      \"peak_residual_score\": 4.2,\n      \"total_excess_units\": 180,\n      \"day_count\": 4,\n      \"already_has_window\": true\n    }\n  ]\n}"}]},{"name":"Get Promo Window","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/promo-windows/{id}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","promo-windows",":id"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Get a single promo window by ID.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPath parameter: id - the promo window to read.\n\nReturns 404 if the promo window does not exist.\n\nsuggested_modifiers: the forward Event modifiers built from the window's persisted measurement (the same shape Measure Promo Lift returns), so a previously measured window can be applied without re-measuring. Empty until the window has been measured. The second modifier is named '&lt;window> post-promo dip' when post_promo_dip_pct is negative and '&lt;window> post-promo carryover' when demand stayed elevated after the promo (positive)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/promo-windows/{id}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","promo-windows",":id"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"name\": \"Black Friday 2024\",\n    \"start_date\": \"2024-11-25\",\n    \"end_date\": \"2024-11-30\",\n    \"order_tag\": \"black-friday\",\n    \"product_scope\": [\n      55,\n      56\n    ],\n    \"channel_scope\": [\n      \"shopify\"\n    ],\n    \"recurrence\": \"annual\",\n    \"measured_lift_pct\": 34.78,\n    \"post_promo_dip_pct\": -11.2,\n    \"measured_at\": \"2024-12-05T08:00:00+00:00\",\n    \"suggested_modifiers\": [\n      {\n        \"type\": \"event\",\n        \"value\": 35,\n        \"apply_to\": \"all\",\n        \"name\": \"Black Friday 2024 lift\",\n        \"period_key\": null,\n        \"start_date\": \"2025-11-25\",\n        \"end_date\": \"2025-11-30\",\n        \"product_id\": null\n      },\n      {\n        \"type\": \"event\",\n        \"value\": -11,\n        \"apply_to\": \"all\",\n        \"name\": \"Black Friday 2024 post-promo dip\",\n        \"period_key\": null,\n        \"start_date\": \"2025-12-01\",\n        \"end_date\": \"2025-12-14\",\n        \"product_id\": null\n      }\n    ],\n    \"created_by\": 10,\n    \"created_at\": \"2024-11-01T12:00:00+00:00\",\n    \"updated_at\": \"2024-12-05T08:00:00+00:00\"\n  }\n}"}]},{"name":"Update Promo Window","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/promo-windows/{id}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","promo-windows",":id"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Update a promo window. A full representation is expected, mirroring create.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nEditing the date range, order_tag, or scope changes what a future Measure Promo Lift call will consider; re-measure afterwards so any promo_lift demand modifier that references this window applies the refreshed lift. Because the lift is applied multiplicatively to the current baseline, the same window keeps re-scaling as demand grows.\n\nPath parameter: id - the promo window to update.\n\nBody fields:\n- name (required): label for the window, up to 255 characters.\n- start_date (required, YYYY-MM-DD): must be on or before end_date.\n- end_date (required, YYYY-MM-DD): must be on or after start_date.\n- order_tag (optional): the order tag that marks orders belonging to this promotion.\n- recurrence (optional): one_off or annual (default one_off).\n- product_scope (optional): array of product IDs the window applies to.\n- channel_scope (optional): array of sales channels the window applies to.\n\nReturns 404 if the promo window does not exist.\n\nsuggested_modifiers: the forward Event modifiers built from the window's persisted measurement (the same shape Measure Promo Lift returns), so a previously measured window can be applied without re-measuring. Empty until the window has been measured. The second modifier is named '&lt;window> post-promo dip' when post_promo_dip_pct is negative and '&lt;window> post-promo carryover' when demand stayed elevated after the promo (positive).","body":{"mode":"raw","raw":"{\n  \"name\": \"Black Friday 2024\",\n  \"start_date\": \"2024-11-25\",\n  \"end_date\": \"2024-11-30\",\n  \"order_tag\": \"black-friday\",\n  \"recurrence\": \"annual\",\n  \"product_scope\": [\n    55,\n    56\n  ],\n  \"channel_scope\": [\n    \"shopify\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/promo-windows/{id}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","promo-windows",":id"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"name\": \"Black Friday 2024\",\n    \"start_date\": \"2024-11-25\",\n    \"end_date\": \"2024-11-30\",\n    \"order_tag\": \"black-friday\",\n    \"product_scope\": [\n      55,\n      56\n    ],\n    \"channel_scope\": [\n      \"shopify\"\n    ],\n    \"recurrence\": \"annual\",\n    \"measured_lift_pct\": 34.78,\n    \"post_promo_dip_pct\": -11.2,\n    \"measured_at\": \"2024-12-05T08:00:00+00:00\",\n    \"suggested_modifiers\": [\n      {\n        \"type\": \"event\",\n        \"value\": 35,\n        \"apply_to\": \"all\",\n        \"name\": \"Black Friday 2024 lift\",\n        \"period_key\": null,\n        \"start_date\": \"2025-11-25\",\n        \"end_date\": \"2025-11-30\",\n        \"product_id\": null\n      },\n      {\n        \"type\": \"event\",\n        \"value\": -11,\n        \"apply_to\": \"all\",\n        \"name\": \"Black Friday 2024 post-promo dip\",\n        \"period_key\": null,\n        \"start_date\": \"2025-12-01\",\n        \"end_date\": \"2025-12-14\",\n        \"product_id\": null\n      }\n    ],\n    \"created_by\": 10,\n    \"created_at\": \"2024-11-01T12:00:00+00:00\",\n    \"updated_at\": \"2024-12-05T08:00:00+00:00\"\n  }\n}"}]},{"name":"Delete Promo Window","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/promo-windows/{id}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","promo-windows",":id"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Delete a promo window.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPath parameter: id - the promo window to delete.\n\nReturns 204 on success, or 404 if the promo window does not exist."},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/promo-windows/{id}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","promo-windows",":id"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":""}]},{"name":"Measure Promo Lift","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/promo-windows/{id}/measure","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","promo-windows",":id","measure"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Measure the realised demand lift during a promo window and the dip in the days after it, for one product. The result is persisted onto the window and returned alongside suggested forward demand modifiers for next year's occurrence.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nThe measured lift is what a promo_lift demand modifier applies at build time. It is reported as a percentage but is applied MULTIPLICATIVELY to whatever the current baseline is, so it stays correct as demand grows - a promotion that lifted demand 85% last year still lifts this year's larger baseline by 85%. Re-measure whenever you want the applied lift refreshed (for example after editing the window or accruing more sales).\n\nPath parameter: id - the promo window to measure.\n\nBody fields:\n- product_id (required): the product to measure.\n- dip_days (optional): number of days after the window to measure the post-promo dip, 1-365 (default 14).\n- sales_filters (optional): object narrowing which sales are counted.\n- warehouse_id (optional): restrict the measurement to one warehouse.\n\nThe response reports measured_lift_pct, post_promo_dip_pct, the in-window and expected daily rates, and suggested_modifiers - event demand modifiers pre-dated to the same window one year forward, ready to add to a build or configuration.\n\nThe second suggested modifier is named '&lt;window> post-promo dip' when post_promo_dip_pct is negative and '&lt;window> post-promo carryover' when demand stayed elevated after the promo.","body":{"mode":"raw","raw":"{\n  \"product_id\": 55,\n  \"dip_days\": 14,\n  \"sales_filters\": {},\n  \"warehouse_id\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/promo-windows/{id}/measure","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","promo-windows",":id","measure"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"measured_lift_pct\": 34.7821,\n    \"post_promo_dip_pct\": -11.2,\n    \"in_window_daily\": 8.5,\n    \"expected_daily\": 6.3,\n    \"suggested_modifiers\": [\n      {\n        \"type\": \"event\",\n        \"value\": 35,\n        \"apply_to\": \"all\",\n        \"name\": \"Black Friday 2024 lift\",\n        \"period_key\": null,\n        \"start_date\": \"2025-11-25\",\n        \"end_date\": \"2025-11-30\",\n        \"product_id\": null\n      },\n      {\n        \"type\": \"event\",\n        \"value\": -11,\n        \"apply_to\": \"all\",\n        \"name\": \"Black Friday 2024 post-promo dip\",\n        \"period_key\": null,\n        \"start_date\": \"2025-12-01\",\n        \"end_date\": \"2025-12-14\",\n        \"product_id\": null\n      }\n    ]\n  }\n}"}]},{"name":"List Forecast Accuracy","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/accuracy?product_id=55","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","accuracy"],"query":[{"key":"product_id","value":"55","description":"Required. Product to list accuracy history for.","disabled":true}]},"description":"List a product's forecast-accuracy history across runs, paginated. Each entry compares a run's projected demand against the actual quantity sold in the evaluation window.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nQuery parameters:\n- product_id (required): the product to read accuracy for.\n- page (default 1), per_page (default 15).\n\nEach entry reports ape (absolute percentage error), signed_bias with a bias_direction of over, under, or none, a tracking_signal, and is_drifting when the forecast is trending off.\n\nforecast_quantity is the demand the run projected for the evaluation window: the stored line's inputs.daily_average multiplied by the window's days (not the line's order quantity). Saved scheduled runs are scored automatically by the nightly sku:forecasting:evaluate-accuracy sweep once 30 days have elapsed, over the 30 days after the run.\n\n`run_link` is the app path of the scored forecast run, or null once that run has been purged (runs that created no purchase orders are deleted 37 days after they run; their accuracy scores are kept)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/accuracy?product_id=55","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","accuracy"],"query":[{"key":"product_id","value":"55","description":"Required. Product to list accuracy history for.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 700,\n      \"forecast_schedule_run_id\": 512,\n      \"run_link\": \"/inventory/demand-planning/runs/512\",\n      \"product_id\": 55,\n      \"product_link\": \"/products/55\",\n      \"product_sku\": \"PROD-001\",\n      \"product_name\": \"Widget A\",\n      \"evaluation_start\": \"2024-09-01\",\n      \"evaluation_end\": \"2024-09-30\",\n      \"forecast_quantity\": 75,\n      \"actual_quantity\": 68,\n      \"ape\": 10.29,\n      \"signed_bias\": 7,\n      \"bias_direction\": \"over\",\n      \"tracking_signal\": 1.8,\n      \"is_drifting\": false,\n      \"computed_at\": \"2024-10-15T09:00:00+00:00\",\n      \"created_at\": \"2024-10-15T09:00:00+00:00\",\n      \"updated_at\": \"2024-10-15T09:00:00+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 6\n}"}]},{"name":"Evaluate Forecast Accuracy","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/accuracy/evaluate","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","accuracy","evaluate"]},"description":"Backtest a completed forecast run by comparing its projected demand against actual sales, and store the resulting accuracy metrics per product. The work runs in the background; the response returns immediately with a job reference to poll for progress.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody fields:\n- run_id (required): the forecast run to evaluate.\n- eval_start (optional, YYYY-MM-DD): start of the evaluation window; must be on or before eval_end.\n- eval_end (optional, YYYY-MM-DD): end of the evaluation window.\n\nReturns a job reference (tracked_job_log_id).\n\nforecast_quantity is the demand the run projected for the evaluation window: the stored line's inputs.daily_average multiplied by the window's days (not the line's order quantity). Saved scheduled runs are scored automatically by the nightly sku:forecasting:evaluate-accuracy sweep once 30 days have elapsed, over the 30 days after the run.","body":{"mode":"raw","raw":"{\n  \"run_id\": 512,\n  \"eval_start\": \"2024-09-01\",\n  \"eval_end\": \"2024-09-30\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/accuracy/evaluate","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","accuracy","evaluate"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 12345\n  },\n  \"message\": \"Forecast accuracy evaluation started.\"\n}"}]},{"name":"Get Run Forecast Accuracy","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/{runId}/accuracy","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs",":runId","accuracy"],"variable":[{"key":"runId","value":"","description":"The run ID."}]},"description":"Get all forecast-accuracy metrics computed for a single forecast run - one entry per evaluated product.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPath parameter: runId - the forecast run to read accuracy for.\n\nEach entry reports ape, signed_bias with a bias_direction, a tracking_signal, and is_drifting. Returns an empty list if the run has not been evaluated yet.\n\nforecast_quantity is the demand the run projected for the evaluation window: the stored line's inputs.daily_average multiplied by the window's days (not the line's order quantity). Saved scheduled runs are scored automatically by the nightly sku:forecasting:evaluate-accuracy sweep once 30 days have elapsed, over the 30 days after the run.\n\n`run_link` is the app path of the scored forecast run, or null once that run has been purged (runs that created no purchase orders are deleted 37 days after they run; their accuracy scores are kept)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/schedule-runs/{runId}/accuracy","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","schedule-runs",":runId","accuracy"],"variable":[{"key":"runId","value":"","description":"The run ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 700,\n      \"forecast_schedule_run_id\": 512,\n      \"run_link\": \"/inventory/demand-planning/runs/512\",\n      \"product_id\": 55,\n      \"product_link\": \"/products/55\",\n      \"product_sku\": \"PROD-001\",\n      \"product_name\": \"Widget A\",\n      \"evaluation_start\": \"2024-09-01\",\n      \"evaluation_end\": \"2024-09-30\",\n      \"forecast_quantity\": 75,\n      \"actual_quantity\": 68,\n      \"ape\": 10.29,\n      \"signed_bias\": 7,\n      \"bias_direction\": \"over\",\n      \"tracking_signal\": 1.8,\n      \"is_drifting\": false,\n      \"computed_at\": \"2024-10-15T09:00:00+00:00\",\n      \"created_at\": \"2024-10-15T09:00:00+00:00\",\n      \"updated_at\": \"2024-10-15T09:00:00+00:00\"\n    }\n  ]\n}"}]},{"name":"Get Measured Lead Time Coverage Summary","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/lead-time-coverage/summary","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","lead-time-coverage","summary"]},"description":"Coverage counts and averages for the measured (purchase-order-derived) lead times available across a forecast scope. Answers, for the selected suppliers and optional destination warehouse, how many supplier-products have a measured lead time versus fall back to the configured value.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- supplier_ids (required, integer array) — suppliers in scope.\n- destination_warehouse_id (optional, integer) — when set, the destination-scoped measured actual is preferred over the blended per-supplier-product average.\n- product_filters (optional, object) — filter_groups structure to narrow the products in scope.","body":{"mode":"raw","raw":"{\n  \"supplier_ids\": [\n    1,\n    2,\n    3\n  ],\n  \"destination_warehouse_id\": 4,\n  \"product_filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/lead-time-coverage/summary","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","lead-time-coverage","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_products\": 310,\n    \"with_measured\": 142,\n    \"falls_back\": 168,\n    \"avg_measured_days\": 34,\n    \"avg_on_time_pct\": 78\n  }\n}"}]},{"name":"List Measured Lead Time Coverage Products","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/lead-time-coverage/products","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","lead-time-coverage","products"]},"description":"Paginated per-product breakdown of configured versus measured lead time for a forecast scope, including which value would drive the forecast when measured lead times are enabled.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- supplier_ids (required, integer array) — suppliers in scope.\n- destination_warehouse_id (optional, integer) — prefers the destination-scoped measured actual.\n- product_filters (optional, object) — filter_groups structure to narrow the products.\n- search (optional, string) — matches product SKU or name.\n- measured (optional, string) — one of 'has_measured' or 'falls_back' to only return products that do or do not have measured data.\n- page (optional, integer, default 1).\n- per_page (optional, integer, default 10, max 100).\n\nResponse fields per product:\n- configured_lead_time / configured_source — the configured lead time and where it comes from.\n- measured_avg_days, measured_sample_size, measured_reliability_pct, measured_variance — the measured evidence (null when none exists).\n- measured_scope — 'warehouse' (destination-scoped), 'blended' (across all warehouses), or null.\n- resolved_lead_time / resolved_source / uses_measured — the value that would drive the forecast and whether measured data won.","body":{"mode":"raw","raw":"{\n  \"supplier_ids\": [\n    1,\n    2,\n    3\n  ],\n  \"destination_warehouse_id\": 4,\n  \"product_filters\": {},\n  \"search\": \"widget\",\n  \"measured\": \"has_measured\",\n  \"page\": 1,\n  \"per_page\": 10\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/lead-time-coverage/products","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","lead-time-coverage","products"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"supplier_product_id\": 8842,\n      \"product_id\": 55,\n      \"sku\": \"WIDGET-BLK\",\n      \"name\": \"Widget - Black\",\n      \"supplier_id\": 1,\n      \"supplier_name\": \"Acme Supplier Co.\",\n      \"configured_lead_time\": 30,\n      \"configured_source\": \"product\",\n      \"configured_source_label\": \"Product Override\",\n      \"measured_avg_days\": 19,\n      \"measured_sample_size\": 11,\n      \"measured_reliability_pct\": 91,\n      \"measured_variance\": 2.1,\n      \"measured_scope\": \"warehouse\",\n      \"measured_computed_at\": \"2026-07-20T02:00:00+00:00\",\n      \"has_measured\": true,\n      \"resolved_lead_time\": 19,\n      \"resolved_source\": \"measured\",\n      \"resolved_source_label\": \"Measured Actual\",\n      \"uses_measured\": true,\n      \"product_route\": \"/products/55\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 15,\n  \"per_page\": 10,\n  \"total\": 142,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"List Measured Lead Time Contributing Purchase Orders","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/lead-time-coverage/{supplierProduct}/purchase-orders","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","lead-time-coverage",":supplierProduct","purchase-orders"],"variable":[{"key":"supplierProduct","value":"8842","description":"Supplier-product id whose measured lead time you want the evidence for."}]},"description":"The closed purchase orders a supplier-product's measured lead time is derived from: the same closed-PO population (last 12 months, with submitted, received and estimated delivery dates present) that the measured average is computed over. Each row shows the submitted-to-received span and whether it landed on or before its estimated delivery date.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPath parameter:\n- supplierProduct (required, integer) — the supplier-product id.\n\nQuery parameter:\n- warehouse_id (optional, integer) — restrict to POs delivered to a specific destination warehouse.\n\nOrdered by received date, most recent first."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/lead-time-coverage/{supplierProduct}/purchase-orders","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","lead-time-coverage",":supplierProduct","purchase-orders"],"variable":[{"key":"supplierProduct","value":"8842","description":"Supplier-product id whose measured lead time you want the evidence for."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"purchase_order_id\": 1042,\n      \"purchase_order_number\": \"PO-1042\",\n      \"submitted_at\": \"2026-03-03T09:00:00+00:00\",\n      \"received_at\": \"2026-03-22T14:30:00+00:00\",\n      \"estimated_delivery_date\": \"2026-03-24T00:00:00+00:00\",\n      \"lead_time_days\": 19,\n      \"on_time\": true,\n      \"late_days\": 0,\n      \"destination_warehouse_id\": 4,\n      \"destination_warehouse_name\": \"Main Warehouse\",\n      \"link_route\": \"/orders/purchase-orders/1042\"\n    },\n    {\n      \"purchase_order_id\": 1051,\n      \"purchase_order_number\": \"PO-1051\",\n      \"submitted_at\": \"2026-03-17T09:00:00+00:00\",\n      \"received_at\": \"2026-04-09T16:00:00+00:00\",\n      \"estimated_delivery_date\": \"2026-04-06T00:00:00+00:00\",\n      \"lead_time_days\": 23,\n      \"on_time\": false,\n      \"late_days\": 3,\n      \"destination_warehouse_id\": 4,\n      \"destination_warehouse_name\": \"Main Warehouse\",\n      \"link_route\": \"/orders/purchase-orders/1051\"\n    }\n  ]\n}"}]},{"name":"Get Latest Plan","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/runs/latest","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","runs","latest"]},"description":"Get the most recently completed make / buy / transfer plan, with its warehouses. Returns `data: null` when no plan has been built yet. MRP runs started with forecast_source=demand_planning and no run id read this plan.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nQuery: sales_forecast=1 returns the newest plan built from sales history (Sales Based) — the only kind that carries a demand forecast, and the one MRP forecasts from."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/runs/latest","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","runs","latest"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1482,\n    \"mode\": \"unified\",\n    \"status\": \"completed\",\n    \"started_at\": \"2026-10-08T14:02:11+00:00\",\n    \"completed_at\": \"2026-10-08T14:02:19+00:00\",\n    \"created_at\": \"2026-10-08T14:02:10+00:00\",\n    \"error_message\": null,\n    \"is_stale\": false,\n    \"warehouse_ids\": [\n      1\n    ],\n    \"warehouses\": [\n      {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      }\n    ],\n    \"horizon_days\": 90,\n    \"include_make\": true,\n    \"supplier_ids\": [],\n    \"summary\": null,\n    \"configuration_snapshot\": {\n      \"mode\": \"unified\",\n      \"warehouse_ids\": [\n        1\n      ],\n      \"horizon_days\": 90,\n      \"include_make\": true,\n      \"forecast_type\": \"with_sales_history\",\n      \"target_stock_days\": 30\n    },\n    \"purchase_order_ids\": [],\n    \"manufacturing_order_ids\": [],\n    \"warehouse_transfer_ids\": [],\n    \"created_by\": \"Jane Planner\"\n  }\n}"}]},{"name":"Get Plan","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/runs/{runId}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","runs",":runId"],"variable":[{"key":"runId","value":"1482","description":"Unified plan run id (returned by Build Forecast in unified mode)"}]},"description":"Get a make / buy / transfer plan run. While the build job runs, `status` is pending or running and `summary` is null — poll until it is completed or failed. Once completed, `summary` is computed live from the plan's lines: lines and still-to-order counts per source (make, buy, transfer, unsourced), units and value, the number of draft POs (one per supplier), MOs and transfers the plan implies, and how many lines are at risk (late or order-now). Lines already turned into a draft document no longer count as still to order. `is_stale` is true once the plan is more than 14 days old.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/runs/{runId}","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","runs",":runId"],"variable":[{"key":"runId","value":"1482","description":"Unified plan run id (returned by Build Forecast in unified mode)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1482,\n    \"mode\": \"unified\",\n    \"status\": \"completed\",\n    \"started_at\": \"2026-10-08T14:02:11+00:00\",\n    \"completed_at\": \"2026-10-08T14:02:19+00:00\",\n    \"created_at\": \"2026-10-08T14:02:10+00:00\",\n    \"error_message\": null,\n    \"is_stale\": false,\n    \"warehouse_ids\": [\n      1\n    ],\n    \"warehouses\": [\n      {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      }\n    ],\n    \"horizon_days\": 90,\n    \"include_make\": true,\n    \"supplier_ids\": [],\n    \"summary\": {\n      \"mode\": \"unified\",\n      \"warehouse_ids\": [\n        1\n      ],\n      \"total_lines\": 11,\n      \"at_risk\": 3,\n      \"released\": 0,\n      \"sources\": {\n        \"make\": {\n          \"lines\": 4,\n          \"actionable\": 3,\n          \"units\": 815,\n          \"value\": 4012.2,\n          \"suppliers\": 0\n        },\n        \"buy\": {\n          \"lines\": 6,\n          \"actionable\": 5,\n          \"units\": 2063,\n          \"value\": 18420,\n          \"suppliers\": 4\n        },\n        \"transfer\": {\n          \"lines\": 1,\n          \"actionable\": 1,\n          \"units\": 50,\n          \"value\": 90,\n          \"suppliers\": 0\n        },\n        \"unsourced\": {\n          \"lines\": 1,\n          \"actionable\": 0,\n          \"units\": 0,\n          \"value\": 0,\n          \"suppliers\": 0\n        }\n      },\n      \"purchase_orders\": 4,\n      \"manufacturing_orders\": 3,\n      \"warehouse_transfers\": 1\n    },\n    \"configuration_snapshot\": {\n      \"mode\": \"unified\",\n      \"warehouse_ids\": [\n        1\n      ],\n      \"horizon_days\": 90,\n      \"include_make\": true,\n      \"forecast_type\": \"with_sales_history\",\n      \"target_stock_days\": 30\n    },\n    \"purchase_order_ids\": [],\n    \"manufacturing_order_ids\": [],\n    \"warehouse_transfer_ids\": [],\n    \"created_by\": \"Jane Planner\"\n  }\n}"}]},{"name":"List Plan Lines","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/runs/{runId}/lines","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","runs",":runId","lines"],"variable":[{"key":"runId","value":"1482","description":"Unified plan run id (returned by Build Forecast in unified mode)"}]},"description":"List the lines of a plan, one per product and warehouse, paginated.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nEach line carries its source (make = built from its active BOM, buy = purchased, transfer = moved from another warehouse, unsourced = no BOM, supplier or transfer lane yet), the stock position (on_hand, incoming, backordered), the forecast (daily_average, forecast_30, independent_quantity over the plan horizon), the demand from builds that consume it (dependent_quantity), the suggested quantity after lot size / case pack / MOQ, the order-by date and status (covered, order_soon, stockout_risk, late), and `driven_by` — the structured parts the quantity is made of — with `driven_by_text`, the same as one sentence. All quantities are in the product's stock unit of measure. `is_orderable` is false for unsourced, covered or already-ordered lines; `released_document` names the draft document a line was turned into — its `type`, `id` and `reference` (the document number, e.g. PO-0066).\n\nFilters: filter[source], filter[supplier_id], filter[warehouse_id], filter[brand_id], filter[status] (each accepts a single value or comma-separated list), filter[needs_action] (1 = only lines that still need an order), filter[search] (SKU or product name).\n\nOrdering: group_by = source (default; make, buy, transfer, unsourced) | supplier | brand | none orders each page into contiguous groups; within a group, late lines come first, then by SKU. No sort parameter.\n\nPagination: page, per_page (default 10)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/runs/{runId}/lines","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","runs",":runId","lines"],"variable":[{"key":"runId","value":"1482","description":"Unified plan run id (returned by Build Forecast in unified mode)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 9812,\n      \"forecast_schedule_run_id\": 1482,\n      \"product\": {\n        \"id\": 41,\n        \"sku\": \"RM-AVO-BULK\",\n        \"name\": \"Avocado Oil, bulk\",\n        \"stock_uom\": \"LB\",\n        \"brand\": {\n          \"id\": 3,\n          \"name\": \"Grove Oils\"\n        }\n      },\n      \"warehouse\": {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      },\n      \"warehouse_id\": 1,\n      \"source\": \"buy\",\n      \"supplier\": {\n        \"id\": 7,\n        \"name\": \"Pacific Oils Co.\"\n      },\n      \"supplier_product_id\": 118,\n      \"bom_id\": null,\n      \"bom\": null,\n      \"source_warehouse\": null,\n      \"is_outsourced\": false,\n      \"also_purchasable_from\": null,\n      \"on_hand\": 310,\n      \"incoming\": 0,\n      \"backordered\": 0,\n      \"daily_average\": 0,\n      \"forecast_30\": 0,\n      \"independent_quantity\": 0,\n      \"dependent_quantity\": 750,\n      \"suggested_quantity\": 800,\n      \"final_quantity\": 800,\n      \"lead_time_days\": 14,\n      \"lead_time_source\": \"supplier_product\",\n      \"target_days\": 30,\n      \"days_of_cover\": null,\n      \"order_by_date\": \"2026-10-10\",\n      \"status\": \"stockout_risk\",\n      \"driven_by\": [\n        {\n          \"type\": \"dependent\",\n          \"parent_product_id\": 12,\n          \"parent_sku\": \"AVO-8OZ\",\n          \"bom_id\": 4,\n          \"quantity\": 600,\n          \"per_unit\": 0.5,\n          \"uom\": \"LB\",\n          \"needed_by\": \"2026-10-24\"\n        },\n        {\n          \"type\": \"dependent\",\n          \"parent_product_id\": 19,\n          \"parent_sku\": \"GIFT-TRIO\",\n          \"bom_id\": 9,\n          \"quantity\": 150,\n          \"per_unit\": 1.5,\n          \"uom\": \"LB\",\n          \"needed_by\": \"2026-10-24\"\n        },\n        {\n          \"type\": \"netted\",\n          \"quantity\": 310\n        },\n        {\n          \"type\": \"moq\",\n          \"quantity\": 800\n        }\n      ],\n      \"driven_by_text\": \"Builds 750 LB (AVO-8OZ, GIFT-TRIO) · less 310 available · MOQ 800\",\n      \"issues\": [],\n      \"unit_cost\": 7.3,\n      \"line_total\": 5840,\n      \"is_orderable\": true,\n      \"released_document\": null\n    }\n  ],\n  \"first_page_url\": \"https://acme.sku.io/api/inventory-forecasting/runs/1482/lines?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://acme.sku.io/api/inventory-forecasting/runs/1482/lines?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://acme.sku.io/api/inventory-forecasting/runs/1482/lines\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"Get Plan Line Breakdown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/runs/{runId}/lines/{lineId}/explosion","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","runs",":runId","lines",":lineId","explosion"],"variable":[{"key":"runId","value":"1482","description":"Unified plan run id (returned by Build Forecast in unified mode)"},{"key":"lineId","value":"9812","description":"Plan line id"}]},"description":"Get the BOM breakdown behind a plan line.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFor a Make line, `components` lists every ingredient its build consumes: quantity per unit and in total in the component's stock unit (converted from the recipe's unit — source_quantity_per_unit and source_uom show what the recipe says), stock on hand at the build warehouse, the shortfall, and the plan line that supplies it (`shared` = several builds draw on it). Expected scrap and yield are applied and contractor-supplied lines are excluded. For any line driven by builds or transfers, `parents` lists them. `issues` reports recipe problems such as a missing unit conversion (that component is left out of the plan) or a BOM cycle."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/runs/{runId}/lines/{lineId}/explosion","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","runs",":runId","lines",":lineId","explosion"],"variable":[{"key":"runId","value":"1482","description":"Unified plan run id (returned by Build Forecast in unified mode)"},{"key":"lineId","value":"9812","description":"Plan line id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"line_id\": 9801,\n    \"source\": \"make\",\n    \"build_quantity\": 1500,\n    \"components\": [\n      {\n        \"product\": {\n          \"id\": 41,\n          \"sku\": \"RM-AVO-BULK\",\n          \"name\": \"Avocado Oil, bulk\"\n        },\n        \"per_unit\": 0.5,\n        \"uom\": \"LB\",\n        \"source_quantity_per_unit\": 8,\n        \"source_uom\": \"OZ\",\n        \"needed\": 750,\n        \"on_hand\": 310,\n        \"short\": 440,\n        \"line\": {\n          \"id\": 9812,\n          \"source\": \"buy\",\n          \"suggested_quantity\": 800,\n          \"supplier\": \"Pacific Oils Co.\",\n          \"shared\": true\n        }\n      },\n      {\n        \"product\": {\n          \"id\": 42,\n          \"sku\": \"PK-BTL-8\",\n          \"name\": \"8oz amber glass bottle\"\n        },\n        \"per_unit\": 1,\n        \"uom\": \"EACH\",\n        \"source_quantity_per_unit\": null,\n        \"source_uom\": \"EACH\",\n        \"needed\": 1500,\n        \"on_hand\": 200,\n        \"short\": 1300,\n        \"line\": {\n          \"id\": 9813,\n          \"source\": \"buy\",\n          \"suggested_quantity\": 1500,\n          \"supplier\": \"Westpack Glass\",\n          \"shared\": true\n        }\n      }\n    ],\n    \"parents\": [\n      {\n        \"type\": \"dependent\",\n        \"product\": {\n          \"id\": 19,\n          \"sku\": \"GIFT-TRIO\"\n        },\n        \"quantity\": 300,\n        \"per_unit\": 3,\n        \"line_id\": 9805,\n        \"needed_by\": \"2026-10-20\"\n      }\n    ],\n    \"issues\": []\n  }\n}"}]},{"name":"Generate Orders from Plan","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-forecasting/runs/{runId}/orders","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","runs",":runId","orders"],"variable":[{"key":"runId","value":"1482","description":"Unified plan run id (returned by Build Forecast in unified mode)"}]},"description":"Create draft documents from selected plan lines: one draft manufacturing order per Make line, one draft purchase order per supplier and warehouse for the Buy lines, and one draft warehouse transfer per Transfer line. Everything is created as a draft; nothing is sent to a supplier. With preview=true nothing is created and the grouped result comes back.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRecompute: when a Make line is left out of line_ids (or its quantity is overridden), the ingredients it drove are re-sized from the builds that are being created; an ingredient that drops to zero is returned in `dropped` and not ordered, a resized one in `adjusted`. Builds already turned into a manufacturing order still count.\n\nSafety: each document is created on its own, so one that fails is reported in `skipped` with its reason without affecting the rest. A line that already has a document is skipped (\"Already created (PO-1204)\"), so repeating the request never creates duplicates. `documents` lists every draft created, with its type, id and document number (`reference`). A Make entry carries `batches`: one draft MO per batch when its Make policy sets an order maximum. The preview's `also_needed` lists covered ingredients the selection now runs short of (a build raised above its suggestion) — line_id, sku, source, quantity — so the client can add them to line_ids; nothing is created for them unless they are sent. An outsourced build's ingredients are planned (and bought) at the contractor's site warehouse. Make lines need Manufacturing access and Transfer lines need warehouse transfer create access; without it those lines are skipped with the reason.\n\nSupplier minimums: when the plan enforces supplier minimum order value or quantity and a PO falls below it, the request returns 422 with `violations` unless that supplier is in acknowledged_supplier_ids or minimums_acknowledged is true. The preview always lists `violations`.\n\nBody: line_ids (required, integer[]), quantities (object line_id => quantity override, optional, ≥ 0), approval_status (draft | approved — purchase orders only, default draft), acknowledged_supplier_ids (integer[], optional), minimums_acknowledged (boolean, optional), preview (boolean, optional).","body":{"mode":"raw","raw":"{\n  \"line_ids\": [\n    9801,\n    9812,\n    9813\n  ],\n  \"quantities\": {\n    \"9801\": 1500\n  },\n  \"approval_status\": \"draft\",\n  \"acknowledged_supplier_ids\": [\n    7\n  ],\n  \"minimums_acknowledged\": false,\n  \"preview\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-forecasting/runs/{runId}/orders","host":["{{baseUrl}}"],"path":["api","inventory-forecasting","runs",":runId","orders"],"variable":[{"key":"runId","value":"1482","description":"Unified plan run id (returned by Build Forecast in unified mode)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"manufacturing_order_ids\": [\n      3021\n    ],\n    \"purchase_order_ids\": [\n      8810,\n      8811\n    ],\n    \"warehouse_transfer_ids\": [],\n    \"documents\": [\n      {\n        \"type\": \"manufacturing_order\",\n        \"id\": 3021,\n        \"reference\": \"MO-000412\"\n      },\n      {\n        \"type\": \"purchase_order\",\n        \"id\": 8810,\n        \"reference\": \"PO-1204\"\n      },\n      {\n        \"type\": \"purchase_order\",\n        \"id\": 8811,\n        \"reference\": \"PO-1205\"\n      }\n    ],\n    \"skipped\": [],\n    \"violations\": []\n  },\n  \"message\": \"Created 1 draft manufacturing order and 2 draft purchase orders\"\n}"}]},{"name":"Get Inventory Aging Report","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-aging?view=product_summary","host":["{{baseUrl}}"],"path":["api","inventory-aging"],"query":[{"key":"view","value":"product_summary","description":"product_summary (one row per product, enriched) or fifo_detail (raw cost layers). Default fifo_detail","disabled":true}]},"description":"Paginated inventory aging report.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nTwo views:\n- `view=product_summary` — one row per product aggregated across its FIFO layers, enriched with listings, sales velocity (7/14/30/90 days), days of supply, channel sales, margin, bracket-scoped `aged_value` (dollars in the active bracket, or 181+ days when no bracket filter is set), an estimated 90-day `holding_cost_90d`, a rule-based `suggested_action`, and the recorded `action` state.\n- `view=fifo_detail` — raw cost layers (one row per layer) with age bracket, layer date, source document and value.\n\nSorting (product summary): pass `sort_by` + `sort_dir`; sorting runs server-side over the full filtered dataset. Allowed sorts: sku, total_quantity, avg_unit_cost, total_value, aged_value, max_days_old, holding_cost, velocity_30d.\n\nAction scope (product summary): `action_scope` partitions rows by their recorded action state; metrics and distribution deliberately ignore it so totals keep counting accepted/resolved stock.\n\nAll other filters apply identically to this endpoint, the metrics, the distribution and the CSV export. `sales_period` governs every sales-window filter (has_sales, channel sold-on / no-sales-on, margin)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-aging?view=product_summary","host":["{{baseUrl}}"],"path":["api","inventory-aging"],"query":[{"key":"view","value":"product_summary","description":"product_summary (one row per product, enriched) or fifo_detail (raw cost layers). Default fifo_detail","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 1935,\n      \"sku\": \"SZ909\",\n      \"product_name\": \"Brand New Hayg OEM Fuel Pump\",\n      \"brand_name\": \"Hayg\",\n      \"brand_id\": 12,\n      \"supplier_name\": \"Hayg Distribution\",\n      \"age_bracket\": \"365+\",\n      \"max_days_old\": 412,\n      \"total_quantity\": 115,\n      \"avg_unit_cost\": 13.51,\n      \"total_value\": 1553.2,\n      \"warehouses\": [\n        \"FBA US\",\n        \"ShipMyOrders Warehouse\"\n      ],\n      \"aged_value\": 1553.2,\n      \"aged_quantity\": 115,\n      \"aged_value_label\": \"181+ days\",\n      \"holding_cost_90d\": 97.08,\n      \"listings\": [\n        {\n          \"id\": 881,\n          \"sales_channel_name\": \"AMZ partusa\",\n          \"integration_name\": \"amazon\",\n          \"listing_price\": 39.95,\n          \"default_tier_price\": 39.95,\n          \"discount_percentage\": 0,\n          \"listing_sku\": \"SZ909-FBA\",\n          \"listing_type\": null,\n          \"listing_status\": null,\n          \"condition\": null\n        }\n      ],\n      \"listing_count\": 1,\n      \"has_listings\": true,\n      \"all_full_price\": true,\n      \"has_fba_layers\": true,\n      \"sold_7d\": 0,\n      \"sold_14d\": 0,\n      \"sold_30d\": 0,\n      \"sold_90d\": 5,\n      \"days_of_supply\": null,\n      \"channel_sales_30d\": [],\n      \"total_channel_sales_30d\": 0,\n      \"total_channel_revenue_30d\": 0,\n      \"default_retail_price\": 39.95,\n      \"avg_selling_price_30d\": null,\n      \"avg_cogs_per_unit_30d\": null,\n      \"total_revenue_30d\": null,\n      \"total_cogs_30d\": null,\n      \"margin_pct_30d\": null,\n      \"suggested_action\": {\n        \"action\": \"expand_channels\",\n        \"label\": \"Expand channels\",\n        \"reason\": \"Listed on 1 of 4 active channels\"\n      },\n      \"action\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 8,\n  \"per_page\": 50,\n  \"total\": 382,\n  \"from\": 1,\n  \"to\": 50\n}"}]},{"name":"Get Inventory Aging Metrics","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-aging/metrics","host":["{{baseUrl}}"],"path":["api","inventory-aging","metrics"]},"description":"Summary metrics for the aging report: total inventory value at cost, value-weighted average age, product counts (total, slow-moving 180+, dead stock 365+), value at risk (181+ days), and per-bracket value/quantity/percentage breakdowns.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nHonors every filter the report honors (warehouse, brand, supplier, search, quantity, age bracket, listings, sales, channels, margin), so these figures always describe the same population as the filtered report. `action_scope` is deliberately ignored — accepted/resolved stock stays counted."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-aging/metrics","host":["{{baseUrl}}"],"path":["api","inventory-aging","metrics"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"total_inventory_value\": 716964.57,\n  \"average_age\": 279,\n  \"total_skus\": 569,\n  \"slow_moving_skus\": 382,\n  \"dead_stock_skus\": 177,\n  \"value_at_risk\": 366573.77,\n  \"value_by_age_bracket\": {\n    \"0-30\": 249322.11,\n    \"31-60\": 54210.09,\n    \"61-90\": 46858.6,\n    \"91-180\": 101243.4,\n    \"181-365\": 158218.9,\n    \"365+\": 208354.87\n  },\n  \"quantity_by_age_bracket\": {\n    \"0-30\": 1204,\n    \"31-60\": 310,\n    \"61-90\": 280,\n    \"91-180\": 642,\n    \"181-365\": 801,\n    \"365+\": 1388\n  },\n  \"percentage_by_age_bracket\": {\n    \"0-30\": 34.8,\n    \"31-60\": 7.6,\n    \"61-90\": 6.5,\n    \"91-180\": 14.1,\n    \"181-365\": 22.1,\n    \"365+\": 29.1\n  }\n}"}]},{"name":"Get Inventory Aging Distribution","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-aging/distribution","host":["{{baseUrl}}"],"path":["api","inventory-aging","distribution"]},"description":"Per-bracket distribution for the value-at-risk funnel: quantity, value, percentage of total value and a display color per age bracket. Always returns all six brackets (zeroed when empty).\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nHonors the same filters as the report and metrics, so the funnel always reconciles with both."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-aging/distribution","host":["{{baseUrl}}"],"path":["api","inventory-aging","distribution"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"[\n  {\n    \"age_bracket\": \"0-30\",\n    \"quantity\": 1204,\n    \"value\": 249322.11,\n    \"percentage\": 34.8,\n    \"color\": \"#4CAF50\"\n  },\n  {\n    \"age_bracket\": \"31-60\",\n    \"quantity\": 310,\n    \"value\": 54210.09,\n    \"percentage\": 7.6,\n    \"color\": \"#8BC34A\"\n  },\n  {\n    \"age_bracket\": \"61-90\",\n    \"quantity\": 280,\n    \"value\": 46858.6,\n    \"percentage\": 6.5,\n    \"color\": \"#FFEB3B\"\n  },\n  {\n    \"age_bracket\": \"91-180\",\n    \"quantity\": 642,\n    \"value\": 101243.4,\n    \"percentage\": 14.1,\n    \"color\": \"#FF9800\"\n  },\n  {\n    \"age_bracket\": \"181-365\",\n    \"quantity\": 801,\n    \"value\": 158218.9,\n    \"percentage\": 22.1,\n    \"color\": \"#FF5722\"\n  },\n  {\n    \"age_bracket\": \"365+\",\n    \"quantity\": 1388,\n    \"value\": 208354.87,\n    \"percentage\": 29.1,\n    \"color\": \"#F44336\"\n  }\n]"}]},{"name":"Get Inventory Aging Trend","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-aging/trend","host":["{{baseUrl}}"],"path":["api","inventory-aging","trend"]},"description":"Aged-value trend series from nightly snapshots: per-bracket inventory value and quantity per snapshot date, for charting whether aged value is shrinking over time.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nWithout `warehouse_id` the tenant-wide rollup series is returned. Snapshots are recorded nightly going forward — history begins the day the feature is enabled."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-aging/trend","host":["{{baseUrl}}"],"path":["api","inventory-aging","trend"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"snapshot_date\": \"2026-07-12\",\n      \"age_bracket\": \"181-365\",\n      \"total_value\": 158218.9,\n      \"total_quantity\": 801\n    },\n    {\n      \"snapshot_date\": \"2026-07-12\",\n      \"age_bracket\": \"365+\",\n      \"total_value\": 208354.87,\n      \"total_quantity\": 1388\n    },\n    {\n      \"snapshot_date\": \"2026-07-13\",\n      \"age_bracket\": \"181-365\",\n      \"total_value\": 155102.4,\n      \"total_quantity\": 788\n    },\n    {\n      \"snapshot_date\": \"2026-07-13\",\n      \"age_bracket\": \"365+\",\n      \"total_value\": 206220.11,\n      \"total_quantity\": 1371\n    }\n  ]\n}"}]},{"name":"Export Inventory Aging to CSV","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-aging/export?view=product_summary","host":["{{baseUrl}}"],"path":["api","inventory-aging","export"],"query":[{"key":"view","value":"product_summary","description":"product_summary (one row per product, enriched) or fifo_detail (raw cost layers). Default fifo_detail","disabled":true}]},"description":"Stream the aging report as a CSV download. `view=product_summary` exports enriched product rows (including aged value, holding cost, suggested action and recorded action state); the default view exports raw FIFO layer detail.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nHonors every report filter plus `sort_by`/`sort_dir` and `action_scope`, so the file matches what the filtered report shows. Response is a `text/csv` attachment, not JSON."}},{"name":"List Sales Channels (Aging Report Filter Values)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-aging/sales-channels","host":["{{baseUrl}}"],"path":["api","inventory-aging","sales-channels"]},"description":"Sales channels that have had sales in the last 90 days — the option list for the sold-on / no-sales-on / not-listed-on channel filters.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-aging/sales-channels","host":["{{baseUrl}}"],"path":["api","inventory-aging","sales-channels"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"[\n  {\n    \"id\": 2,\n    \"channelName\": \"AMZ partusa\",\n    \"integrationName\": \"amazon\"\n  },\n  {\n    \"id\": 5,\n    \"channelName\": \"EBAY fuelpumpsusa\",\n    \"integrationName\": \"ebay\"\n  }\n]"}]},{"name":"Record Product Aging Action","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-aging/actions/{product}","host":["{{baseUrl}}"],"path":["api","inventory-aging","actions",":product"],"variable":[{"key":"product","value":"","description":"The product ID."}]},"description":"Create or update the aging action state for a product. One state per product:\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n- `needs_action` — default; nothing recorded yet\n- `in_progress` — a clearance action was recorded (`action_type` required: price_reduced, listed_on_channel, promotion, bundled, transferred, removal_ordered, written_down, other)\n- `resolved` — the aged stock was dealt with (`resolution` required: sold_through, cleared, written_off)\n- `accepted` — deliberate long-tail stock; optional `snooze_until` (today or later) keeps it out of the needs-attention scope until that date\n\n`note` (max 1000 chars) and `assigned_to` (an active user belonging to the current account) are optional on every status; `action_type` is rejected for accepted/needs_action and `resolution` outside resolved. Each write stamps who acted and when.\n\nWhen granular permissions are enabled for the account, this endpoint requires the `inventory.manage_aging` permission.","body":{"mode":"raw","raw":"{\n  \"status\": \"in_progress\",\n  \"action_type\": \"price_reduced\",\n  \"note\": \"Dropped price 15% on Amazon and eBay\",\n  \"assigned_to\": 4\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/inventory-aging/actions/{product}","host":["{{baseUrl}}"],"path":["api","inventory-aging","actions",":product"],"variable":[{"key":"product","value":"","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"product_id\": 1935,\n  \"status\": \"in_progress\",\n  \"action_type\": \"price_reduced\",\n  \"resolution\": null,\n  \"note\": \"Dropped price 15% on Amazon and eBay\",\n  \"assigned_to\": {\n    \"id\": 4,\n    \"name\": \"Jo Merchant\"\n  },\n  \"snooze_until\": null,\n  \"acted_at\": \"2026-07-13T14:05:22+00:00\",\n  \"updated_by\": {\n    \"id\": 1,\n    \"name\": \"Administrator\"\n  }\n}"}]},{"name":"Clear Product Aging Action","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-aging/actions/{product}","host":["{{baseUrl}}"],"path":["api","inventory-aging","actions",":product"],"variable":[{"key":"product","value":"","description":"The product ID."}]},"description":"Remove a product's recorded aging action state, returning it to needs-action.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nWhen granular permissions are enabled for the account, this endpoint requires the `inventory.manage_aging` permission."},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/inventory-aging/actions/{product}","host":["{{baseUrl}}"],"path":["api","inventory-aging","actions",":product"],"variable":[{"key":"product","value":"","description":"The product ID."}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":""}]},{"name":"Bulk Record Product Aging Actions","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-aging/actions/bulk","host":["{{baseUrl}}"],"path":["api","inventory-aging","actions","bulk"]},"description":"Record the same aging action state for up to 50 products in one call. Field rules match the single-product endpoint (`action_type` required for in_progress, `resolution` required for resolved, `snooze_until` only for accepted).\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns a per-product result list; a failure on one product does not roll back the others.\n\nWhen granular permissions are enabled for the account, this endpoint requires the `inventory.manage_aging` permission.","body":{"mode":"raw","raw":"{\n  \"product_ids\": [\n    1935,\n    1994,\n    2017\n  ],\n  \"status\": \"accepted\",\n  \"note\": \"Evergreen replacement-part stock\",\n  \"snooze_until\": \"2026-10-01\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-aging/actions/bulk","host":["{{baseUrl}}"],"path":["api","inventory-aging","actions","bulk"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"results\": [\n    {\n      \"product_id\": 1935,\n      \"ok\": true\n    },\n    {\n      \"product_id\": 1994,\n      \"ok\": true\n    },\n    {\n      \"product_id\": 2017,\n      \"ok\": true\n    }\n  ]\n}"}]},{"name":"Generate AI Aging Suggestions","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-aging/actions/generate-ai-suggestions","host":["{{baseUrl}}"],"path":["api","inventory-aging","actions","generate-ai-suggestions"]},"description":"Start a background process that scores aged products with AI and caches a recommended next action on each. Returns a tracked job id you can poll for completion; when the job finishes, the aging report shows the AI-scored suggestions in place of the rule-based ones. If AI is disabled or unavailable for the account, the job completes immediately and rows keep their rule-based suggestions.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nTakes no request body.\n\nWhen granular permissions are enabled for the account, this endpoint requires the `inventory.manage_aging` permission."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-aging/actions/generate-ai-suggestions","host":["{{baseUrl}}"],"path":["api","inventory-aging","actions","generate-ai-suggestions"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 84213\n  },\n  \"message\": \"AI aging suggestions are being generated.\"\n}"}]}]},{"name":"Inventory Expiry & Shelf-Life","item":[{"name":"Get Expiry & Shelf-Life Report","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-expiry","host":["{{baseUrl}}"],"path":["api","inventory-expiry"]},"description":"Paginated Inventory Expiry & Shelf-Life report. Blends internal lot-tracked FIFO layers with Amazon FBA/AWD layers, bucketed by days-to-expiry. Defaults to the lot_detail view (one row per lot/FIFO layer, each carrying a real fifo_layer_id for disposition actions); pass view=product_summary for one row per product rolled up to the worst-case (soonest) expiry.\n\nAvailable on both GET and POST (the POST variant mirrors all filters and additionally accepts the filter_groups product advanced-filter tree in the body).\n\nAuthentication: Requires Bearer token. Scope: reports:read.\n\nBuckets: expired, 0-30, 31-60, 61-90, 91-180, 180+, no_expiry.\nSort: fixed — soonest-to-expire first, undated lots last.\nPagination: page (default 1), per_page (default 50, max 500).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThis endpoint accepts the canonical list view param contract used by the application UI: filters as filter[&lt;key>], a `sort` param (see below), `page`/`per_page`, and a base64-encoded `filter_groups` tree — in addition to the flat params shown."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-expiry","host":["{{baseUrl}}"],"path":["api","inventory-expiry"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"fifo_layer_id\": 8842,\n      \"product_id\": 42,\n      \"sku\": \"VIT-D3-1000\",\n      \"product_name\": \"Vitamin D3 1000 IU\",\n      \"brand_name\": \"Acme\",\n      \"brand_id\": 3,\n      \"supplier_name\": \"Wellness Distributors\",\n      \"warehouse_id\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"bucket\": \"0-30\",\n      \"days_to_expiry\": 18,\n      \"expiry_date\": \"2026-07-04T00:00:00.000000Z\",\n      \"manufacture_date\": \"2024-07-04T00:00:00.000000Z\",\n      \"batch_number\": \"LOT-2026-01\",\n      \"shelf_life_remaining_pct\": 2.5,\n      \"link_type\": \"App\\\\Models\\\\PurchaseOrderLine\",\n      \"link_type_name\": \"Purchase Order Line\",\n      \"link_id\": 5521,\n      \"quantity\": 120,\n      \"avg_unit_cost\": 8.5,\n      \"total_value\": 1020,\n      \"valuation_source\": \"internal\",\n      \"is_fba\": false,\n      \"review_status\": \"open\",\n      \"review_status_label\": \"Open\",\n      \"review_notes\": null,\n      \"reviewed_at\": null,\n      \"reviewed_by_name\": null,\n      \"inventory_hold_id\": null\n    }\n  ],\n  \"per_page\": 50,\n  \"total\": 137,\n  \"last_page\": 3\n}"}]},{"name":"Get Expiry & Shelf-Life Report (POST, Advanced Filters)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-expiry","host":["{{baseUrl}}"],"path":["api","inventory-expiry"]},"description":"POST variant of the expiry report. Mirrors every GET filter and additionally carries the larger product advanced-filter tree (filter_groups) in the request body.\n\nAuthentication: Requires Bearer token. Scope: reports:read.\n\nProduct advanced-filter tree (GroupedFilterSupport). POSTed in the body to carry larger filter sets than the query string allows; bridged into the report via a products.id subquery.\n\nStructure: { conjunction: 'and'|'or', children: [ { type: 'condition', condition: { column, operator, value } } | { type: 'group', group: {…} } ] } (groups may nest).\n\nAllowed columns: sku, name, barcode, mpn, type (text); brand_name, supplier_name, category, tag (relationship text); shelf_life_days, near_expiry_threshold_days (numeric).\nText operators: contains, does_not_contain, is, is_not, is_one_of, starts_with, does_not_start_with, ends_with, does_not_end_with, is_empty, is_not_empty.\nNumeric operators: is, is_not, is_one_of, greater_than, less_than, greater_than_or_equal, less_than_or_equal, between, is_empty, is_not_empty.\nLegacy { filters: { conjunction, filterSet } } format is also accepted for backward compatibility.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"view\": \"lot_detail\",\n  \"bucket\": \"0-30\",\n  \"warehouse_id\": 1,\n  \"near_expiry_only\": true,\n  \"filter_groups\": {\n    \"conjunction\": \"and\",\n    \"children\": [\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"brand_name\",\n          \"operator\": \"is\",\n          \"value\": \"Acme\"\n        }\n      },\n      {\n        \"type\": \"group\",\n        \"group\": {\n          \"conjunction\": \"or\",\n          \"children\": [\n            {\n              \"type\": \"condition\",\n              \"condition\": {\n                \"column\": \"shelf_life_days\",\n                \"operator\": \"less_than\",\n                \"value\": 365\n              }\n            },\n            {\n              \"type\": \"condition\",\n              \"condition\": {\n                \"column\": \"category\",\n                \"operator\": \"contains\",\n                \"value\": \"Supplement\"\n              }\n            }\n          ]\n        }\n      }\n    ]\n  },\n  \"page\": 1,\n  \"per_page\": 50\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-expiry","host":["{{baseUrl}}"],"path":["api","inventory-expiry"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"fifo_layer_id\": 8842,\n      \"product_id\": 42,\n      \"sku\": \"VIT-D3-1000\",\n      \"product_name\": \"Vitamin D3 1000 IU\",\n      \"brand_name\": \"Acme\",\n      \"brand_id\": 3,\n      \"supplier_name\": \"Wellness Distributors\",\n      \"warehouse_id\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"bucket\": \"0-30\",\n      \"days_to_expiry\": 18,\n      \"expiry_date\": \"2026-07-04T00:00:00.000000Z\",\n      \"manufacture_date\": \"2024-07-04T00:00:00.000000Z\",\n      \"batch_number\": \"LOT-2026-01\",\n      \"shelf_life_remaining_pct\": 2.5,\n      \"link_type\": \"App\\\\Models\\\\PurchaseOrderLine\",\n      \"link_type_name\": \"Purchase Order Line\",\n      \"link_id\": 5521,\n      \"quantity\": 120,\n      \"avg_unit_cost\": 8.5,\n      \"total_value\": 1020,\n      \"valuation_source\": \"internal\",\n      \"is_fba\": false,\n      \"review_status\": \"open\",\n      \"review_status_label\": \"Open\",\n      \"review_notes\": null,\n      \"reviewed_at\": null,\n      \"reviewed_by_name\": null,\n      \"inventory_hold_id\": null\n    }\n  ],\n  \"per_page\": 50,\n  \"total\": 137,\n  \"last_page\": 3\n}"}]},{"name":"Get Expiry Metrics","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-expiry/metrics","host":["{{baseUrl}}"],"path":["api","inventory-expiry","metrics"]},"description":"Summary metric cards for the expiry dashboard: total tracked value, expired value, value expiring within 30/60/90 days, at-risk and expired lot counts, the Amazon auto-disposal-risk value (FBA lots within Amazon's 50-day disposal window), and per-bucket value/quantity/percentage breakdowns.\n\nHonours the same filters as the report (GET query string or POST filter_groups body).\n\nAuthentication: Requires Bearer token. Scope: reports:read.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-expiry/metrics","host":["{{baseUrl}}"],"path":["api","inventory-expiry","metrics"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"total_tracked_value\": 184920.5,\n  \"expired_value\": 4210,\n  \"value_within_30_days\": 12040,\n  \"value_within_60_days\": 28110,\n  \"value_within_90_days\": 41880,\n  \"at_risk_lot_count\": 63,\n  \"expired_lot_count\": 9,\n  \"amazon_disposal_risk_value\": 7300,\n  \"value_by_bucket\": {\n    \"expired\": 4210,\n    \"0-30\": 12040,\n    \"31-60\": 16070,\n    \"61-90\": 13770,\n    \"91-180\": 38900,\n    \"180+\": 99930.5,\n    \"no_expiry\": 0\n  },\n  \"quantity_by_bucket\": {\n    \"expired\": 540,\n    \"0-30\": 1420,\n    \"31-60\": 1890,\n    \"61-90\": 1620,\n    \"91-180\": 4580,\n    \"180+\": 11760,\n    \"no_expiry\": 0\n  },\n  \"percentage_by_bucket\": {\n    \"expired\": 2.3,\n    \"0-30\": 6.5,\n    \"31-60\": 8.7,\n    \"61-90\": 7.4,\n    \"91-180\": 21,\n    \"180+\": 54,\n    \"no_expiry\": 0\n  }\n}"}]},{"name":"Get Expiry Metrics (POST, Advanced Filters)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-expiry/metrics","host":["{{baseUrl}}"],"path":["api","inventory-expiry","metrics"]},"description":"POST variant of the metric cards — same response as the GET, with the product advanced-filter tree in the body.\n\nAuthentication: Requires Bearer token. Scope: reports:read.\n\nProduct advanced-filter tree (GroupedFilterSupport). POSTed in the body to carry larger filter sets than the query string allows; bridged into the report via a products.id subquery.\n\nStructure: { conjunction: 'and'|'or', children: [ { type: 'condition', condition: { column, operator, value } } | { type: 'group', group: {…} } ] } (groups may nest).\n\nAllowed columns: sku, name, barcode, mpn, type (text); brand_name, supplier_name, category, tag (relationship text); shelf_life_days, near_expiry_threshold_days (numeric).\nText operators: contains, does_not_contain, is, is_not, is_one_of, starts_with, does_not_start_with, ends_with, does_not_end_with, is_empty, is_not_empty.\nNumeric operators: is, is_not, is_one_of, greater_than, less_than, greater_than_or_equal, less_than_or_equal, between, is_empty, is_not_empty.\nLegacy { filters: { conjunction, filterSet } } format is also accepted for backward compatibility.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"filter_groups\": {\n    \"conjunction\": \"and\",\n    \"children\": [\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"brand_name\",\n          \"operator\": \"is\",\n          \"value\": \"Acme\"\n        }\n      },\n      {\n        \"type\": \"group\",\n        \"group\": {\n          \"conjunction\": \"or\",\n          \"children\": [\n            {\n              \"type\": \"condition\",\n              \"condition\": {\n                \"column\": \"shelf_life_days\",\n                \"operator\": \"less_than\",\n                \"value\": 365\n              }\n            },\n            {\n              \"type\": \"condition\",\n              \"condition\": {\n                \"column\": \"category\",\n                \"operator\": \"contains\",\n                \"value\": \"Supplement\"\n              }\n            }\n          ]\n        }\n      }\n    ]\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-expiry/metrics","host":["{{baseUrl}}"],"path":["api","inventory-expiry","metrics"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"total_tracked_value\": 184920.5,\n  \"expired_value\": 4210,\n  \"value_within_30_days\": 12040,\n  \"value_within_60_days\": 28110,\n  \"value_within_90_days\": 41880,\n  \"at_risk_lot_count\": 63,\n  \"expired_lot_count\": 9,\n  \"amazon_disposal_risk_value\": 7300,\n  \"value_by_bucket\": {\n    \"expired\": 4210,\n    \"0-30\": 12040,\n    \"31-60\": 16070,\n    \"61-90\": 13770,\n    \"91-180\": 38900,\n    \"180+\": 99930.5,\n    \"no_expiry\": 0\n  },\n  \"quantity_by_bucket\": {\n    \"expired\": 540,\n    \"0-30\": 1420,\n    \"31-60\": 1890,\n    \"61-90\": 1620,\n    \"91-180\": 4580,\n    \"180+\": 11760,\n    \"no_expiry\": 0\n  },\n  \"percentage_by_bucket\": {\n    \"expired\": 2.3,\n    \"0-30\": 6.5,\n    \"31-60\": 8.7,\n    \"61-90\": 7.4,\n    \"91-180\": 21,\n    \"180+\": 54,\n    \"no_expiry\": 0\n  }\n}"}]},{"name":"Get Expiry Distribution","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-expiry/distribution","host":["{{baseUrl}}"],"path":["api","inventory-expiry","distribution"]},"description":"Expiry bucket distribution for charts — one entry per bucket (in display order) with quantity, value, percentage-of-total-value, label and colour. Honours the same filters as the report.\n\nAuthentication: Requires Bearer token. Scope: reports:read.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-expiry/distribution","host":["{{baseUrl}}"],"path":["api","inventory-expiry","distribution"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"[\n  {\n    \"bucket\": \"expired\",\n    \"label\": \"Expired\",\n    \"quantity\": 540,\n    \"value\": 4210,\n    \"percentage\": 2.3,\n    \"color\": \"#F44336\"\n  },\n  {\n    \"bucket\": \"0-30\",\n    \"label\": \"0–30 days\",\n    \"quantity\": 1420,\n    \"value\": 12040,\n    \"percentage\": 6.5,\n    \"color\": \"#FF5722\"\n  },\n  {\n    \"bucket\": \"31-60\",\n    \"label\": \"31–60 days\",\n    \"quantity\": 1890,\n    \"value\": 16070,\n    \"percentage\": 8.7,\n    \"color\": \"#FF9800\"\n  },\n  {\n    \"bucket\": \"61-90\",\n    \"label\": \"61–90 days\",\n    \"quantity\": 1620,\n    \"value\": 13770,\n    \"percentage\": 7.4,\n    \"color\": \"#FFC107\"\n  },\n  {\n    \"bucket\": \"91-180\",\n    \"label\": \"91–180 days\",\n    \"quantity\": 4580,\n    \"value\": 38900,\n    \"percentage\": 21,\n    \"color\": \"#8BC34A\"\n  },\n  {\n    \"bucket\": \"180+\",\n    \"label\": \"180+ days\",\n    \"quantity\": 11760,\n    \"value\": 99930.5,\n    \"percentage\": 54,\n    \"color\": \"#4CAF50\"\n  },\n  {\n    \"bucket\": \"no_expiry\",\n    \"label\": \"No expiry\",\n    \"quantity\": 0,\n    \"value\": 0,\n    \"percentage\": 0,\n    \"color\": \"#9E9E9E\"\n  }\n]"}]},{"name":"Get Expiry Distribution (POST, Advanced Filters)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-expiry/distribution","host":["{{baseUrl}}"],"path":["api","inventory-expiry","distribution"]},"description":"POST variant of the bucket distribution — same response as the GET, with the product advanced-filter tree in the body.\n\nAuthentication: Requires Bearer token. Scope: reports:read.\n\nProduct advanced-filter tree (GroupedFilterSupport). POSTed in the body to carry larger filter sets than the query string allows; bridged into the report via a products.id subquery.\n\nStructure: { conjunction: 'and'|'or', children: [ { type: 'condition', condition: { column, operator, value } } | { type: 'group', group: {…} } ] } (groups may nest).\n\nAllowed columns: sku, name, barcode, mpn, type (text); brand_name, supplier_name, category, tag (relationship text); shelf_life_days, near_expiry_threshold_days (numeric).\nText operators: contains, does_not_contain, is, is_not, is_one_of, starts_with, does_not_start_with, ends_with, does_not_end_with, is_empty, is_not_empty.\nNumeric operators: is, is_not, is_one_of, greater_than, less_than, greater_than_or_equal, less_than_or_equal, between, is_empty, is_not_empty.\nLegacy { filters: { conjunction, filterSet } } format is also accepted for backward compatibility.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"filter_groups\": {\n    \"conjunction\": \"and\",\n    \"children\": [\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"brand_name\",\n          \"operator\": \"is\",\n          \"value\": \"Acme\"\n        }\n      },\n      {\n        \"type\": \"group\",\n        \"group\": {\n          \"conjunction\": \"or\",\n          \"children\": [\n            {\n              \"type\": \"condition\",\n              \"condition\": {\n                \"column\": \"shelf_life_days\",\n                \"operator\": \"less_than\",\n                \"value\": 365\n              }\n            },\n            {\n              \"type\": \"condition\",\n              \"condition\": {\n                \"column\": \"category\",\n                \"operator\": \"contains\",\n                \"value\": \"Supplement\"\n              }\n            }\n          ]\n        }\n      }\n    ]\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-expiry/distribution","host":["{{baseUrl}}"],"path":["api","inventory-expiry","distribution"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"[\n  {\n    \"bucket\": \"expired\",\n    \"label\": \"Expired\",\n    \"quantity\": 540,\n    \"value\": 4210,\n    \"percentage\": 2.3,\n    \"color\": \"#F44336\"\n  },\n  {\n    \"bucket\": \"0-30\",\n    \"label\": \"0–30 days\",\n    \"quantity\": 1420,\n    \"value\": 12040,\n    \"percentage\": 6.5,\n    \"color\": \"#FF5722\"\n  },\n  {\n    \"bucket\": \"31-60\",\n    \"label\": \"31–60 days\",\n    \"quantity\": 1890,\n    \"value\": 16070,\n    \"percentage\": 8.7,\n    \"color\": \"#FF9800\"\n  },\n  {\n    \"bucket\": \"61-90\",\n    \"label\": \"61–90 days\",\n    \"quantity\": 1620,\n    \"value\": 13770,\n    \"percentage\": 7.4,\n    \"color\": \"#FFC107\"\n  },\n  {\n    \"bucket\": \"91-180\",\n    \"label\": \"91–180 days\",\n    \"quantity\": 4580,\n    \"value\": 38900,\n    \"percentage\": 21,\n    \"color\": \"#8BC34A\"\n  },\n  {\n    \"bucket\": \"180+\",\n    \"label\": \"180+ days\",\n    \"quantity\": 11760,\n    \"value\": 99930.5,\n    \"percentage\": 54,\n    \"color\": \"#4CAF50\"\n  },\n  {\n    \"bucket\": \"no_expiry\",\n    \"label\": \"No expiry\",\n    \"quantity\": 0,\n    \"value\": 0,\n    \"percentage\": 0,\n    \"color\": \"#9E9E9E\"\n  }\n]"}]},{"name":"Export Expiry Report (async)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-expiry/export","host":["{{baseUrl}}"],"path":["api","inventory-expiry","export"]},"description":"Starts an asynchronous CSV export of the expiry report (lot-detail or product-summary view).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe export runs as a tray-tracked job: a full lot-detail export can run for minutes, and a synchronous response that long is terminated by the edge proxy (Cloudflare 524). The CSV is built off the request cycle, written to the model-exports disk, and surfaced as a download via the tracked job log endpoints.\n\nReturns the tracked job log id; poll the tracked job log endpoints (or GET /api/export/download?file=...) for the finished file.\n\nAuthentication: Requires Bearer token (scope reports:read). Accepts the same filters as the report endpoint (view, warehouse_id, search, batch_number, bucket, brand_id, supplier_id, min_quantity, fba_only, near_expiry_only, and the advanced filter_groups tree on the POST variant)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-expiry/export","host":["{{baseUrl}}"],"path":["api","inventory-expiry","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 12345\n  },\n  \"message\": \"Export started — track progress in the job tray.\"\n}"}]},{"name":"Export Expiry Report (async, POST + filter_groups)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-expiry/export","host":["{{baseUrl}}"],"path":["api","inventory-expiry","export"]},"description":"Starts an asynchronous CSV export of the expiry report (lot-detail or product-summary view).\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe export runs as a tray-tracked job: a full lot-detail export can run for minutes, and a synchronous response that long is terminated by the edge proxy (Cloudflare 524). The CSV is built off the request cycle, written to the model-exports disk, and surfaced as a download via the tracked job log endpoints.\n\nReturns the tracked job log id; poll the tracked job log endpoints (or GET /api/export/download?file=...) for the finished file.\n\nAuthentication: Requires Bearer token (scope reports:read). Accepts the same filters as the report endpoint (view, warehouse_id, search, batch_number, bucket, brand_id, supplier_id, min_quantity, fba_only, near_expiry_only, and the advanced filter_groups tree on the POST variant).","body":{"mode":"raw","raw":"{\n  \"view\": \"lot_detail\",\n  \"filter_groups\": {\n    \"conjunction\": \"and\",\n    \"children\": [\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"brand_name\",\n          \"operator\": \"is\",\n          \"value\": \"Acme\"\n        }\n      },\n      {\n        \"type\": \"group\",\n        \"group\": {\n          \"conjunction\": \"or\",\n          \"children\": [\n            {\n              \"type\": \"condition\",\n              \"condition\": {\n                \"column\": \"shelf_life_days\",\n                \"operator\": \"less_than\",\n                \"value\": 365\n              }\n            },\n            {\n              \"type\": \"condition\",\n              \"condition\": {\n                \"column\": \"category\",\n                \"operator\": \"contains\",\n                \"value\": \"Supplement\"\n              }\n            }\n          ]\n        }\n      }\n    ]\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-expiry/export","host":["{{baseUrl}}"],"path":["api","inventory-expiry","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 12345\n  },\n  \"message\": \"Export started — track progress in the job tray.\"\n}"}]},{"name":"Get Actionable Lot Count","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-expiry/alert-count","host":["{{baseUrl}}"],"path":["api","inventory-expiry","alert-count"]},"description":"Lightweight count of lot-tracked lots that are expired or within their near-expiry threshold and still need attention (no review record yet, or one left \"open\"). Acknowledged, quarantined and written-off lots drop out. Powers the navigation badge.\n\nAuthentication: Requires Bearer token. Scope: reports:read.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-expiry/alert-count","host":["{{baseUrl}}"],"path":["api","inventory-expiry","alert-count"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"actionable_lot_count\": 63\n  }\n}"}]},{"name":"Get Near-Expiry Alert Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-expiry/alert-summary","host":["{{baseUrl}}"],"path":["api","inventory-expiry","alert-summary"]},"description":"Near-expiry alert summary for the report-header risk banner and the daily digest: expired and near-expiry lot counts/values (threshold-aware per product), the Amazon auto-disposal-risk value, the actionable (needs-review) lot count, and the highest-value at-risk products (worst-case first).\n\nAuthentication: Requires Bearer token. Scope: reports:read.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-expiry/alert-summary","host":["{{baseUrl}}"],"path":["api","inventory-expiry","alert-summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"expired_lot_count\": 9,\n    \"expired_value\": 4210,\n    \"near_expiry_lot_count\": 54,\n    \"near_expiry_value\": 24080,\n    \"amazon_disposal_risk_value\": 7300,\n    \"actionable_lot_count\": 63,\n    \"top_products\": [\n      {\n        \"product_id\": 42,\n        \"sku\": \"VIT-D3-1000\",\n        \"product_name\": \"Vitamin D3 1000 IU\",\n        \"soonest_days_to_expiry\": 18,\n        \"at_risk_quantity\": 120,\n        \"at_risk_value\": 1020\n      },\n      {\n        \"product_id\": 77,\n        \"sku\": \"FISH-OIL-500\",\n        \"product_name\": \"Fish Oil 500mg\",\n        \"soonest_days_to_expiry\": -3,\n        \"at_risk_quantity\": 80,\n        \"at_risk_value\": 640\n      }\n    ]\n  }\n}"}]},{"name":"Write Off Lot","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-expiry/lots/{fifoLayer}/write-off","host":["{{baseUrl}}"],"path":["api","inventory-expiry","lots",":fifoLayer","write-off"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]},"description":"Write off a lot's remaining (or partial) quantity via an expired-stock DECREASE inventory adjustment (reason: expired). quantity is optional — when omitted the layer's full remaining available quantity is written off; when set, only that portion is consumed (partial write-off).\n\nAuthentication: Requires Bearer token. Scope: scope.rw:inventory (write) + permission inventory.adjust.\n\nPath param: fifoLayer — the FIFO layer (lot) id, route-model-bound.\n\nvoid_confirmations (array, optional) — when the write-off would consume stock that is hard-allocated to a fulfillment with a printed shipping label, list one entry per fulfillment to authorize voiding that label and proceeding. Omit it and the request is rejected with a 422 conflict if hard-allocated stock is in the way.\nvoid_confirmations[].fulfillment_id (integer, required) — the fulfillment whose label may be voided.\nvoid_confirmations[].attempt_void (boolean, required) — true to void the label and consume its stock.\nvoid_confirmations[].reason (string, optional) — free-text audit note for the void.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"quantity\": 50,\n  \"notes\": \"Past expiry — destroyed on site.\",\n  \"void_confirmations\": [\n    {\n      \"fulfillment_id\": 77120,\n      \"attempt_void\": true,\n      \"reason\": \"Customer cancelled after the label was printed\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-expiry/lots/{fifoLayer}/write-off","host":["{{baseUrl}}"],"path":["api","inventory-expiry","lots",":fifoLayer","write-off"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 301,\n    \"fifo_layer_id\": 8842,\n    \"status\": \"written_off\",\n    \"status_label\": \"Written Off\",\n    \"notes\": \"Past expiry — destroyed on site.\",\n    \"inventory_hold_id\": null,\n    \"reviewed_at\": \"2026-06-16T14:32:10+00:00\",\n    \"reviewed_by\": {\n      \"id\": 4,\n      \"name\": \"Dana Ops\"\n    },\n    \"fifo_layer\": {\n      \"id\": 8842,\n      \"available_quantity\": 0,\n      \"batch_number\": \"LOT-2026-01\",\n      \"expiry_date\": \"2026-07-04\"\n    }\n  }\n}"}]},{"name":"Quarantine Lot","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-expiry/lots/{fifoLayer}/quarantine","host":["{{baseUrl}}"],"path":["api","inventory-expiry","lots",":fifoLayer","quarantine"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]},"description":"Quarantine a lot — remove its quantity from available-to-promise via a reversible inventory hold (no stock movement). quantity is optional — when omitted the layer's full remaining available quantity is held. Returns the review record including the inventory_hold_id.\n\nAuthentication: Requires Bearer token. Scope: scope.rw:inventory (write) + permission inventory.adjust.\n\nPath param: fifoLayer — the FIFO layer (lot) id, route-model-bound.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"quantity\": 120,\n  \"notes\": \"Hold pending QA inspection.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-expiry/lots/{fifoLayer}/quarantine","host":["{{baseUrl}}"],"path":["api","inventory-expiry","lots",":fifoLayer","quarantine"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 302,\n    \"fifo_layer_id\": 8842,\n    \"status\": \"quarantined\",\n    \"status_label\": \"Quarantined\",\n    \"notes\": \"Hold pending QA inspection.\",\n    \"inventory_hold_id\": 1190,\n    \"reviewed_at\": \"2026-06-16T14:35:02+00:00\",\n    \"reviewed_by\": {\n      \"id\": 4,\n      \"name\": \"Dana Ops\"\n    },\n    \"fifo_layer\": {\n      \"id\": 8842,\n      \"available_quantity\": 120,\n      \"batch_number\": \"LOT-2026-01\",\n      \"expiry_date\": \"2026-07-04\"\n    }\n  }\n}"}]},{"name":"Release Quarantine","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-expiry/lots/{fifoLayer}/release-quarantine","host":["{{baseUrl}}"],"path":["api","inventory-expiry","lots",":fifoLayer","release-quarantine"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]},"description":"Release a lot from quarantine — return the held quantity to available-to-promise by lifting the inventory hold. No request body. The lot's review status returns to open.\n\nAuthentication: Requires Bearer token. Scope: scope.rw:inventory (write) + permission inventory.adjust.\n\nPath param: fifoLayer — the FIFO layer (lot) id, route-model-bound.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-expiry/lots/{fifoLayer}/release-quarantine","host":["{{baseUrl}}"],"path":["api","inventory-expiry","lots",":fifoLayer","release-quarantine"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 302,\n    \"fifo_layer_id\": 8842,\n    \"status\": \"open\",\n    \"status_label\": \"Open\",\n    \"notes\": \"Hold pending QA inspection.\",\n    \"inventory_hold_id\": null,\n    \"reviewed_at\": \"2026-06-16T15:01:44+00:00\",\n    \"reviewed_by\": {\n      \"id\": 4,\n      \"name\": \"Dana Ops\"\n    },\n    \"fifo_layer\": {\n      \"id\": 8842,\n      \"available_quantity\": 120,\n      \"batch_number\": \"LOT-2026-01\",\n      \"expiry_date\": \"2026-07-04\"\n    }\n  }\n}"}]},{"name":"Set Lot Review Status","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-expiry/lots/{fifoLayer}/review-status","host":["{{baseUrl}}"],"path":["api","inventory-expiry","lots",":fifoLayer","review-status"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]},"description":"Set a lot's non-stock review disposition (acknowledge a near-expiry lot, or re-open it). status is restricted to the bookkeeping states open and acknowledged — the stock-mutating dispositions (written_off / quarantined) are set only by their dedicated endpoints.\n\nAuthentication: Requires Bearer token. Scope: scope.rw:inventory (write) + permission inventory.adjust.\n\nPath param: fifoLayer — the FIFO layer (lot) id, route-model-bound.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"status\": \"acknowledged\",\n  \"notes\": \"Reviewed — will sell through before expiry.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/inventory-expiry/lots/{fifoLayer}/review-status","host":["{{baseUrl}}"],"path":["api","inventory-expiry","lots",":fifoLayer","review-status"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 303,\n    \"fifo_layer_id\": 8842,\n    \"status\": \"acknowledged\",\n    \"status_label\": \"Acknowledged\",\n    \"notes\": \"Reviewed — will sell through before expiry.\",\n    \"inventory_hold_id\": null,\n    \"reviewed_at\": \"2026-06-16T15:10:00+00:00\",\n    \"reviewed_by\": {\n      \"id\": 4,\n      \"name\": \"Dana Ops\"\n    },\n    \"fifo_layer\": {\n      \"id\": 8842,\n      \"available_quantity\": 120,\n      \"batch_number\": \"LOT-2026-01\",\n      \"expiry_date\": \"2026-07-04\"\n    }\n  }\n}"}]},{"name":"Preview Lot Write-Off Impact","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-expiry/lots/{fifoLayer}/write-off-impact","host":["{{baseUrl}}"],"path":["api","inventory-expiry","lots",":fifoLayer","write-off-impact"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]},"description":"Preview the demotion and label-void impact of writing off a lot before committing the write-off. Read-only — mutates nothing.\n\nReturns one row per product the write-off would consume stock for. shortfall_quantity is how much MORE than available the write-off removes (stock goes negative if greater than zero). unallocated_quantity / soft_allocated_quantity / hard_allocated_quantity split the consumed quantity into free / soft-reserved / hard-allocated (label-printed). affected_allocations[] lists the order allocations that would be consumed, highest priority first; allocation_type is soft or hard. For hard rows, fulfillment_id / carrier identify the printed shipping label and carrier_void_supported / voidable_status (voidable, or otherwise) indicate whether it can be voided through the API.\n\nAuthentication: Requires Bearer token. Scope: scope.rw:inventory + permission inventory.adjust.\n\nPath param: fifoLayer — the FIFO layer (lot) id, route-model-bound.\nQuery param: quantity (optional) — the portion to write off; omit to preview writing off the layer's full remaining available quantity.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-expiry/lots/{fifoLayer}/write-off-impact","host":["{{baseUrl}}"],"path":["api","inventory-expiry","lots",":fifoLayer","write-off-impact"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 123,\n      \"product_sku\": \"WIDGET-001\",\n      \"product_name\": \"Blue Widget 500ml\",\n      \"shortfall_quantity\": 0,\n      \"unallocated_quantity\": 4,\n      \"soft_allocated_quantity\": 3,\n      \"hard_allocated_quantity\": 2,\n      \"affected_allocations\": [\n        {\n          \"allocation_id\": 88213,\n          \"allocation_type\": \"soft\",\n          \"sales_order_id\": 4521,\n          \"order_number\": \"SO-10442\",\n          \"customer_name\": \"Acme Retail\",\n          \"fulfillment_id\": null,\n          \"carrier\": null,\n          \"carrier_void_supported\": null,\n          \"voidable_status\": null,\n          \"voidable_detail\": null,\n          \"quantity\": 3\n        },\n        {\n          \"allocation_id\": 88240,\n          \"allocation_type\": \"hard\",\n          \"sales_order_id\": 4530,\n          \"order_number\": \"SO-10450\",\n          \"customer_name\": \"Beta Wholesale\",\n          \"fulfillment_id\": 77120,\n          \"carrier\": \"ups\",\n          \"carrier_void_supported\": true,\n          \"voidable_status\": \"voidable\",\n          \"voidable_detail\": null,\n          \"quantity\": 2\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"Download Lot Backfill Template (CSV)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-expiry/lot-backfill/template","host":["{{baseUrl}}"],"path":["api","inventory-expiry","lot-backfill","template"]},"description":"Download the lot-data backfill CSV template so uploads start from the exact header layout the importer expects (Content-Type: text/csv).\n\nColumns: sku, warehouse, batch, manufacture_date, expiry_date, quantity.\n\nWithout product_id: a header row plus one example row (filename: lot-data-backfill-template.csv).\n\nWith product_id: a header row plus one row per warehouse holding that product's un-lotted stock (no batch, no expiry), pre-filled with sku, warehouse and the un-lotted quantity so only batch and dates remain to fill in (filename: lot-data-backfill-{sku}.csv). A product with nothing left to backfill yields a single row carrying only its SKU. Use GET /api/v2/products/{product}/unlotted-stock to see the same quantities as JSON.\n\nAuthentication: Requires Bearer token. Scope: reports:read.\n\nThis is a file download — the response is CSV text, not JSON.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-expiry/lot-backfill/template","host":["{{baseUrl}}"],"path":["api","inventory-expiry","lot-backfill","template"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"sku,warehouse,batch,manufacture_date,expiry_date,quantity\\nABC-123,Main Warehouse,LOT-2026-01,2026-01-15,2027-01-15,100\\n"}]},{"name":"Import Lot Backfill CSV","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-expiry/lot-backfill","host":["{{baseUrl}}"],"path":["api","inventory-expiry","lot-backfill"]},"description":"Upload a lot-data backfill CSV (multipart/form-data). The file is parsed, then a tray-tracked job is dispatched that splits the named quantities off each product's existing un-lotted FIFO stock into dated lots (cost preserved). Returns the tracked_job_log_id so the frontend can follow progress via the tracked job log endpoints; per-row outcomes (assigned / partial / skipped / failed) are reported in the job summary.\n\nBody: multipart/form-data with a single file field. Accepted mime: csv, txt. Max size: 10 MB.\n\nAuthentication: Requires Bearer token. Scope: scope.rw:inventory (write) + permission inventory.adjust.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-expiry/lot-backfill","host":["{{baseUrl}}"],"path":["api","inventory-expiry","lot-backfill"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 9921\n  },\n  \"message\": \"Lot data backfill started — track progress in the job tray.\"\n}"}]}]},{"name":"Feature Adoption","item":[{"name":"Get Feature Catalog","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/feature-adoption/catalog","host":["{{baseUrl}}"],"path":["api","feature-adoption","catalog"]},"description":"Lists every feature the adoption report measures, with its label, area, group, docs link and what counts as use. `version` changes whenever the catalog changes.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token with the reports scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/feature-adoption/catalog","host":["{{baseUrl}}"],"path":["api","feature-adoption","catalog"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"version\": \"2a88c21ee3be\",\n    \"features\": [\n      {\n        \"key\": \"purchase_orders\",\n        \"label\": \"Purchase Orders\",\n        \"area\": \"purchasing\",\n        \"area_label\": \"Purchasing\",\n        \"description\": \"Raise, send and receive POs against your suppliers.\",\n        \"definition\": \"someone creates or receives a purchase order\",\n        \"docs_url\": \"https://www.sku.io/docs/guides/orders/purchase-orders\",\n        \"cta_route\": \"/orders/purchase-orders\",\n        \"core\": true,\n        \"group\": null,\n        \"view_counts_as_use\": false,\n        \"kind\": \"usage\"\n      }\n    ]\n  }\n}"}]},{"name":"Get Feature Adoption Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/feature-adoption/summary","host":["{{baseUrl}}"],"path":["api","feature-adoption","summary"]},"description":"Returns adoption KPIs and the status of every feature for the window.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nStatuses: active (used in the last 30 days), lapsed (used in the last 180 days but not 30), set_up (configured but never used), not_started, not_for_us (hidden by an admin), not_available (not on the plan / module disabled).\n\n`features_relevant` excludes not_for_us and not_available. `as_of` is when statuses were last computed (nightly); `has_data` is false before the first computation. `auto_cleared` lists hidden features that were restored automatically because they started being used (last 30 days). `can_manage` tells whether the caller may hide/restore features.\n\nAuthentication: Requires Bearer token with the reports scope.\n\nEach feature's `preference` (null unless the feature is marked \"Not for us\") carries `reason`, `reason_label`, `note`, `marked_at`, `marked_by` (user id) and `marked_by_name` — the display name of whoever marked it: \"SKU.io support\" when marked by SKU.io staff, \"Former user\" when the user no longer exists."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/feature-adoption/summary","host":["{{baseUrl}}"],"path":["api","feature-adoption","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"window_days\": 30,\n    \"as_of\": \"2026-10-02T03:15:00+00:00\",\n    \"has_data\": true,\n    \"can_manage\": true,\n    \"kpis\": {\n      \"features_in_use\": 14,\n      \"features_relevant\": 22,\n      \"active_people\": 9,\n      \"seats\": 12,\n      \"most_used\": {\n        \"key\": \"sales_orders\",\n        \"label\": \"Sales Orders\"\n      },\n      \"newly_adopted\": [\n        {\n          \"key\": \"cycle_counts\",\n          \"label\": \"Stock Takes & Cycle Counts\"\n        }\n      ]\n    },\n    \"features\": [\n      {\n        \"key\": \"purchase_orders\",\n        \"label\": \"Purchase Orders\",\n        \"area\": \"purchasing\",\n        \"area_label\": \"Purchasing\",\n        \"group\": null,\n        \"core\": true,\n        \"description\": \"Raise, send and receive POs against your suppliers.\",\n        \"definition\": \"someone creates or receives a purchase order\",\n        \"status\": \"active\",\n        \"configured\": false,\n        \"active_users\": 3,\n        \"system_active\": false,\n        \"active_user_preview\": [\n          {\n            \"id\": 12,\n            \"name\": \"Jane Doe\",\n            \"avatar_url\": null\n          },\n          {\n            \"id\": 31,\n            \"name\": \"Mike Kim\",\n            \"avatar_url\": null\n          }\n        ],\n        \"last_used_at\": \"2026-09-30\",\n        \"first_used_at\": \"2025-11-04\",\n        \"sparkline\": [\n          0,\n          1,\n          2,\n          1,\n          0,\n          0,\n          1,\n          2,\n          3,\n          1,\n          0,\n          0,\n          2,\n          1,\n          1,\n          2,\n          0,\n          0,\n          1,\n          2,\n          2,\n          1,\n          0,\n          0,\n          1,\n          3,\n          2,\n          1,\n          1,\n          2\n        ],\n        \"preference\": null,\n        \"docs_url\": \"https://www.sku.io/docs/guides/orders/purchase-orders\",\n        \"cta_route\": \"/orders/purchase-orders\",\n        \"view_counts_as_use\": false,\n        \"kind\": \"usage\",\n        \"rollup_error\": null,\n        \"stale\": false,\n        \"computed_at\": \"2026-10-02T07:15:00+00:00\"\n      },\n      {\n        \"key\": \"accounting_xero\",\n        \"label\": \"Xero\",\n        \"area\": \"accounting\",\n        \"area_label\": \"Accounting\",\n        \"group\": \"accounting\",\n        \"core\": false,\n        \"description\": \"Sync your books to Xero automatically.\",\n        \"definition\": \"entries sync to Xero\",\n        \"status\": \"not_for_us\",\n        \"configured\": false,\n        \"active_users\": 0,\n        \"system_active\": false,\n        \"active_user_preview\": [],\n        \"last_used_at\": null,\n        \"first_used_at\": null,\n        \"sparkline\": [\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0\n        ],\n        \"preference\": {\n          \"reason\": \"other_tool\",\n          \"reason_label\": \"We use another tool\",\n          \"note\": \"MYOB\",\n          \"marked_at\": \"2026-10-02T14:05:00+00:00\",\n          \"marked_by\": 12,\n          \"marked_by_name\": \"Dana Whitfield\"\n        },\n        \"docs_url\": \"https://www.sku.io/docs/guides/getting-started/connect-accounting\",\n        \"cta_route\": \"/integrations/xero\",\n        \"view_counts_as_use\": false,\n        \"kind\": \"usage\",\n        \"rollup_error\": null,\n        \"stale\": false,\n        \"computed_at\": \"2026-10-02T07:15:00+00:00\"\n      }\n    ],\n    \"auto_cleared\": [\n      {\n        \"key\": \"channel_amazon\",\n        \"label\": \"Amazon\",\n        \"cleared_at\": \"2026-10-01T03:15:12+00:00\"\n      }\n    ]\n  }\n}"}]},{"name":"Get Feature Adoption Detail","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/feature-adoption/features/{featureKey}","host":["{{baseUrl}}"],"path":["api","feature-adoption","features",":featureKey"],"variable":[{"key":"featureKey","value":"purchase_orders","description":"Catalog feature key"}]},"description":"Returns one feature's card, a per-day trend (active users and whether automated/system use happened) and who uses it, most active first. `viewed_only` marks people who opened it without meaningful use; `via_api` marks use through an API token. `is_former` marks a person who has been deactivated or is no longer a member of the account.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token with the reports scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/feature-adoption/features/{featureKey}","host":["{{baseUrl}}"],"path":["api","feature-adoption","features",":featureKey"],"variable":[{"key":"featureKey","value":"purchase_orders","description":"Catalog feature key"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"window_days\": 90,\n    \"feature\": {\n      \"key\": \"purchase_orders\",\n      \"label\": \"Purchase Orders\",\n      \"area\": \"purchasing\",\n      \"area_label\": \"Purchasing\",\n      \"group\": null,\n      \"core\": true,\n      \"description\": \"Raise, send and receive POs against your suppliers.\",\n      \"definition\": \"someone creates or receives a purchase order\",\n      \"status\": \"active\",\n      \"configured\": false,\n      \"active_users\": 3,\n      \"system_active\": false,\n      \"active_user_preview\": [\n        {\n          \"id\": 12,\n          \"name\": \"Jane Doe\",\n          \"avatar_url\": null\n        },\n        {\n          \"id\": 31,\n          \"name\": \"Mike Kim\",\n          \"avatar_url\": null\n        }\n      ],\n      \"last_used_at\": \"2026-09-30\",\n      \"first_used_at\": \"2025-11-04\",\n      \"sparkline\": [\n        0,\n        1,\n        2,\n        1,\n        0,\n        0,\n        1,\n        2,\n        3,\n        1,\n        0,\n        0,\n        2,\n        1,\n        1,\n        2,\n        0,\n        0,\n        1,\n        2,\n        2,\n        1,\n        0,\n        0,\n        1,\n        3,\n        2,\n        1,\n        1,\n        2\n      ],\n      \"preference\": null,\n      \"docs_url\": \"https://www.sku.io/docs/guides/orders/purchase-orders\",\n      \"cta_route\": \"/orders/purchase-orders\",\n      \"view_counts_as_use\": false,\n      \"kind\": \"usage\",\n      \"rollup_error\": null,\n      \"stale\": false,\n      \"computed_at\": \"2026-10-02T07:15:00+00:00\"\n    },\n    \"trend\": [\n      {\n        \"date\": \"2026-10-01\",\n        \"active_users\": 2,\n        \"system_active\": false\n      },\n      {\n        \"date\": \"2026-10-02\",\n        \"active_users\": 3,\n        \"system_active\": false\n      }\n    ],\n    \"users\": [\n      {\n        \"user\": {\n          \"id\": 12,\n          \"name\": \"Jane Doe\",\n          \"avatar_url\": null,\n          \"email\": \"jane@acme-goods.com\"\n        },\n        \"is_former\": false,\n        \"active_days\": 8,\n        \"last_used_at\": \"2026-10-02\",\n        \"last_viewed_at\": \"2026-10-02\",\n        \"viewed_only\": false,\n        \"via_api\": false\n      }\n    ]\n  }\n}"}]},{"name":"List Feature Adoption People","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/feature-adoption/people","host":["{{baseUrl}}"],"path":["api","feature-adoption","people"]},"description":"Paginated list of the team's members (SKU staff excluded) with their activity in the window and a per-feature cell for every relevant feature they touched. `bucket` groups active days: 0 = none, 1 = 1-3, 2 = 4-10, 3 = 11+. Features without a cell had no activity.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFilters: filter[search] (id exact, name, email), filter[role] (raw role name = the row's `role_key`, comma-separated; `role` is the display label), filter[inactive] (true = no active day in the window, false = active only), filter[include_deactivated] (true = also list former users: deactivated, or removed from the account but with recorded activity here; they never count as seats). `last_active_at` is the last active day over all retained history, not just the window. Advanced id/name/email operators are supported.\n\nAllowed sorts: id, name, email, last_active_at, active_days, features_used (prefix with - for descending; default -last_active_at).\n\nPagination: per_page default 10, max 200.\n\nAuthentication: Requires Bearer token with the reports scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/feature-adoption/people","host":["{{baseUrl}}"],"path":["api","feature-adoption","people"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 12,\n      \"name\": \"Jane Doe\",\n      \"email\": \"jane@acme-goods.com\",\n      \"avatar_url\": null,\n      \"role\": \"Admin\",\n      \"role_key\": \"admin\",\n      \"is_deactivated\": false,\n      \"last_active_at\": \"2026-10-02\",\n      \"active_days\": 21,\n      \"features_used\": 11,\n      \"via_api\": false,\n      \"cells\": {\n        \"purchase_orders\": {\n          \"active_days\": 8,\n          \"bucket\": 2,\n          \"last_used_at\": \"2026-10-02\",\n          \"viewed\": true\n        },\n        \"sales_orders\": {\n          \"active_days\": 15,\n          \"bucket\": 3,\n          \"last_used_at\": \"2026-10-01\",\n          \"viewed\": true\n        }\n      }\n    }\n  ],\n  \"first_page_url\": \"https://acme.sku.io/api/feature-adoption/people?page=1\",\n  \"from\": 1,\n  \"last_page\": 2,\n  \"last_page_url\": \"https://acme.sku.io/api/feature-adoption/people?page=2\",\n  \"links\": [],\n  \"next_page_url\": \"https://acme.sku.io/api/feature-adoption/people?page=2\",\n  \"path\": \"https://acme.sku.io/api/feature-adoption/people\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 10,\n  \"total\": 12\n}"}]},{"name":"Get Feature Adoption Person","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/feature-adoption/people/{user}","host":["{{baseUrl}}"],"path":["api","feature-adoption","people",":user"],"variable":[{"key":"user","value":"12","description":"User ID"}]},"description":"One team member's activity: their totals and matrix cells plus every feature they touched in the window, most active first. Returns 404 for users outside the team or SKU staff.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token with the reports scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/feature-adoption/people/{user}","host":["{{baseUrl}}"],"path":["api","feature-adoption","people",":user"],"variable":[{"key":"user","value":"12","description":"User ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"window_days\": 30,\n    \"person\": {\n      \"id\": 12,\n      \"name\": \"Jane Doe\",\n      \"email\": \"jane@acme-goods.com\",\n      \"avatar_url\": null,\n      \"role\": \"Admin\",\n      \"role_key\": \"admin\",\n      \"is_deactivated\": false,\n      \"last_active_at\": \"2026-10-02\",\n      \"active_days\": 21,\n      \"features_used\": 11,\n      \"via_api\": false,\n      \"cells\": {\n        \"purchase_orders\": {\n          \"active_days\": 8,\n          \"bucket\": 2,\n          \"last_used_at\": \"2026-10-02\",\n          \"viewed\": true\n        },\n        \"sales_orders\": {\n          \"active_days\": 15,\n          \"bucket\": 3,\n          \"last_used_at\": \"2026-10-01\",\n          \"viewed\": true\n        }\n      }\n    },\n    \"features\": [\n      {\n        \"key\": \"sales_orders\",\n        \"label\": \"Sales Orders\",\n        \"area\": \"sales\",\n        \"active_days\": 15,\n        \"last_used_at\": \"2026-10-01\",\n        \"last_viewed_at\": \"2026-10-02\",\n        \"viewed_only\": false,\n        \"via_api\": false\n      }\n    ]\n  }\n}"}]},{"name":"Export Feature Adoption","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/feature-adoption/export","host":["{{baseUrl}}"],"path":["api","feature-adoption","export"]},"description":"Exports people x features (active days per relevant feature) as CSV. Up to 1000 user x feature rows the CSV is returned directly (text/csv); larger exports run in the background and return 202 with a tracked job id whose result carries the download URL.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token with the reports scope.","body":{"mode":"raw","raw":"{\n  \"window\": 30\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/feature-adoption/export","host":["{{baseUrl}}"],"path":["api","feature-adoption","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"\"User ID\",Name,Email,Role,\"Last Active\",\"Active Days (30d)\",\"Features Used\",\"Sales Orders\",\"Purchase Orders\"\n12,\"Jane Doe\",jane@acme-goods.com,Admin,2026-10-02,21,11,15,8\n"}]},{"name":"Hide Feature","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/feature-adoption/preferences","host":["{{baseUrl}}"],"path":["api","feature-adoption","preferences"]},"description":"Marks one feature (`feature_key`), a hand-picked list of features (`feature_keys`: 1–200 distinct feature keys), or every not-yet-used feature of a group (`group`: marketplaces, storefronts, 3pl, accounting) as not relevant to the business. Send exactly one of the three. Every feature in `feature_keys` is hidden whatever its status; ones already hidden or not on the plan are returned in `skipped`. A group never hides features that are active; those are returned in `kept_active`. `skipped` lists group members left untouched (lapsed, unavailable or already hidden). `note` is required for other_tool and didnt_work (max 500). A hidden \"maybe_later\" feature returns automatically after 90 days, and any hidden feature returns automatically once someone on the team uses it (automatic activity such as a channel sync does not bring it back).\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequires the feature_adoption.manage permission.\n\nAuthentication: Requires Bearer token with the reports scope.\n\nEach feature's `preference` (null unless the feature is marked \"Not for us\") carries `reason`, `reason_label`, `note`, `marked_at`, `marked_by` (user id) and `marked_by_name` — the display name of whoever marked it: \"SKU.io support\" when marked by SKU.io staff, \"Former user\" when the user no longer exists.","body":{"mode":"raw","raw":"{\n  \"feature_key\": \"accounting_xero\",\n  \"group\": null,\n  \"reason\": \"other_tool\",\n  \"note\": \"MYOB\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/feature-adoption/preferences","host":["{{baseUrl}}"],"path":["api","feature-adoption","preferences"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"hidden\": [\n      \"accounting_xero\"\n    ],\n    \"kept_active\": [],\n    \"skipped\": [],\n    \"features\": [\n      {\n        \"key\": \"accounting_xero\",\n        \"label\": \"Xero\",\n        \"area\": \"accounting\",\n        \"area_label\": \"Accounting\",\n        \"group\": \"accounting\",\n        \"core\": false,\n        \"description\": \"Sync your books to Xero automatically.\",\n        \"definition\": \"entries sync to Xero\",\n        \"status\": \"not_for_us\",\n        \"configured\": false,\n        \"active_users\": 0,\n        \"system_active\": false,\n        \"active_user_preview\": [],\n        \"last_used_at\": null,\n        \"first_used_at\": null,\n        \"sparkline\": [\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0,\n          0\n        ],\n        \"preference\": {\n          \"reason\": \"other_tool\",\n          \"reason_label\": \"We use another tool\",\n          \"note\": \"MYOB\",\n          \"marked_at\": \"2026-10-02T14:05:00+00:00\",\n          \"marked_by\": 12,\n          \"marked_by_name\": \"Dana Whitfield\"\n        },\n        \"docs_url\": \"https://www.sku.io/docs/guides/getting-started/connect-accounting\",\n        \"cta_route\": \"/integrations/xero\",\n        \"view_counts_as_use\": false,\n        \"kind\": \"usage\",\n        \"rollup_error\": null,\n        \"stale\": false,\n        \"computed_at\": \"2026-10-02T07:15:00+00:00\"\n      }\n    ]\n  }\n}"}]},{"name":"Restore Feature","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/feature-adoption/preferences/{featureKey}","host":["{{baseUrl}}"],"path":["api","feature-adoption","preferences",":featureKey"],"variable":[{"key":"featureKey","value":"accounting_xero","description":"Catalog feature key"}]},"description":"Restores a hidden feature. Idempotent — restoring a feature that is not hidden returns its current card.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequires the feature_adoption.manage permission.\n\nAuthentication: Requires Bearer token with the reports scope."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/feature-adoption/preferences/{featureKey}","host":["{{baseUrl}}"],"path":["api","feature-adoption","preferences",":featureKey"],"variable":[{"key":"featureKey","value":"accounting_xero","description":"Catalog feature key"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"key\": \"accounting_xero\",\n    \"label\": \"Xero\",\n    \"area\": \"accounting\",\n    \"area_label\": \"Accounting\",\n    \"group\": \"accounting\",\n    \"core\": false,\n    \"description\": \"Sync your books to Xero automatically.\",\n    \"definition\": \"entries sync to Xero\",\n    \"status\": \"not_started\",\n    \"configured\": false,\n    \"active_users\": 0,\n    \"system_active\": false,\n    \"active_user_preview\": [],\n    \"last_used_at\": null,\n    \"first_used_at\": null,\n    \"sparkline\": [\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0,\n      0\n    ],\n    \"preference\": null,\n    \"docs_url\": \"https://www.sku.io/docs/guides/getting-started/connect-accounting\",\n    \"cta_route\": \"/integrations/xero\",\n    \"view_counts_as_use\": false,\n    \"kind\": \"usage\",\n    \"rollup_error\": null,\n    \"stale\": false,\n    \"computed_at\": \"2026-10-02T07:15:00+00:00\"\n  }\n}"}]}]},{"name":"Setup & Readiness","item":[{"name":"Get Integration Readiness","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/integrations/readiness","host":["{{baseUrl}}"],"path":["api","integrations","readiness"]},"description":"List outstanding integration setup across every connected sales channel, expressed as records the merchant cannot see yet.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nA channel's records reach SKU.io in two hops: they are first downloaded into staging, then turned into usable SKU.io records (catalog products, sales orders). Work that stalls between the two is invisible — a catalog can read empty while listings sit downloaded but unimported. Each item here is one such stall.\n\nPer item:\n- `staged` — records downloaded from the channel\n- `inSku` — of those, how many became usable SKU.io records\n- `missing` — `staged - inSku`, always greater than zero (items with nothing missing are omitted, so a healthy account returns an empty list)\n- `message` — one sentence written for the merchant, safe to render verbatim\n- `actionLabel` / `actionRoute` — the single step that closes it, deep-linked to the specific store via `filter[integration_instance_ids]`\n- `surface` — which SKU.io area it blocks: `products`, `orders` or `locations`\n- `statusBreakdown` — for the `products` surface, the missing listings grouped by channel status (`active`, `draft`, …) so active listings can be told apart from drafts; `null` on other surfaces\n\nEvery count is a local comparison between two tables SKU.io already holds, so this endpoint keeps working when the channel is unreachable or its credentials have been revoked.\n\n**Query parameters:**\n- `surface` (optional, one of `products`, `orders`, `locations`)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/integrations/readiness","host":["{{baseUrl}}"],"path":["api","integrations","readiness"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"items\": [\n      {\n        \"channel\": \"shopify\",\n        \"channelLabel\": \"Shopify\",\n        \"integrationInstanceId\": 6,\n        \"instanceName\": \"Whimsy & Wonders\",\n        \"surface\": \"products\",\n        \"staged\": 7,\n        \"inSku\": 0,\n        \"missing\": 7,\n        \"message\": \"7 Shopify listings have been downloaded but not imported into your catalog yet, so they will not appear in products, inventory or reports.\",\n        \"actionLabel\": \"Review & import 7 products\",\n        \"actionRoute\": \"/integrations/shopify/products?filter[integration_instance_ids]=6&filter[mapped]=false\",\n        \"statusBreakdown\": {\n          \"active\": 5,\n          \"draft\": 2\n        }\n      },\n      {\n        \"channel\": \"shopify\",\n        \"channelLabel\": \"Shopify\",\n        \"integrationInstanceId\": 6,\n        \"instanceName\": \"Whimsy & Wonders\",\n        \"surface\": \"orders\",\n        \"staged\": 2,\n        \"inSku\": 0,\n        \"missing\": 2,\n        \"message\": \"No Shopify location is mapped to a SKU warehouse yet, so SKU is not downloading orders from this store.\",\n        \"actionLabel\": \"Map locations to warehouses\",\n        \"actionRoute\": \"/integrations/shopify/settings?filter[integration_instance_ids]=6&tab=warehouse-mappings\",\n        \"statusBreakdown\": null\n      }\n    ],\n    \"total_missing\": 9\n  }\n}"}]}]},{"name":"Price Sync","item":[{"name":"Start Price Push","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/push","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","push"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]},"description":"Start an outbound price push for a sales channel integration instance, or a dry-run preview of one.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRuns asynchronously and returns a tracked job handle; poll the tracked job for progress and results.\n\nPrices are only sent for listings where SKU.io is the master of price and the price has changed since the last successful push. Every price is checked against the instance's guardrails (floor, ceiling and maximum change) immediately before it is transmitted.\n\nAuthentication: Requires Bearer token.\n\nFields:\n- dry_run (optional, boolean, default false): evaluate every listing and record the outcome without sending anything to the channel.\n- force (optional, boolean, default false): also send prices that have not changed. Does not bypass guardrails.\n- listing_ids (optional, array of integers): restrict the push to these listings. Omit to consider all eligible listings.\n\nRequires the `integrations.sync` permission.\n\nOptional `max_delta_pct_override` (numeric, greater than 0, maximum 1000) raises the maximum-change limit for THIS run only; the saved limit is unchanged. It is raise-only — a value at or below the saved limit is ignored — and it cannot relax the price floor, the price ceiling, or the rule that refuses a zero or negative price.\n\nChannels that rate-gate price transmissions (Walmart accepts 2 price feeds per hour) are checked before the job is created. If the channel would not accept a feed right now, the request is refused with a 422 naming the time the next slot opens, and nothing is dispatched; the scheduled push carries every pending price at its next run. A dry run is never refused for this reason, because it transmits nothing.","body":{"mode":"raw","raw":"{\n  \"dry_run\": false,\n  \"force\": false,\n  \"listing_ids\": [\n    101,\n    102\n  ],\n  \"max_delta_pct_override\": 500\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/push","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","push"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 4821,\n    \"is_dry_run\": false\n  },\n  \"message\": \"Price push started.\"\n}"}]},{"name":"Get Price Push Eligibility","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/eligibility","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","eligibility"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]},"description":"Report what a price push would do, without starting one.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the number of listings that would be sent, the number unchanged, and a breakdown of everything that would be skipped or blocked, grouped by reason.\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.view` permission."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/eligibility","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","eligibility"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"totalListings\": 3521,\n    \"eligible\": 412,\n    \"unchanged\": 3102,\n    \"skippedByReason\": {\n      \"not_master\": 5,\n      \"no_price\": 2\n    },\n    \"blockedByReason\": {\n      \"below_floor\": 2\n    }\n  }\n}"}]},{"name":"Recalculate Channel Prices","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/recompute","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","recompute"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]},"description":"Re-resolve the cached SKU.io price of every listing on a sales channel integration instance from its pricing levels and profiles — the pricing counterpart of the inventory \"recache\" action.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe cache is normally rewritten whenever a price, level or profile changes; use this when a listing's SKU.io price does not match the product's pricing. Nothing is sent to the channel.\n\nRuns asynchronously and returns a tracked job handle; poll the tracked job for progress. The `prices_recalculated_at` field of the price sync status reports when the channel's prices were last recalculated.\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.sync` permission."},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/recompute","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","recompute"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 4822\n  },\n  \"message\": \"Recalculating prices — track progress in the job tray.\"\n}"}]},{"name":"List Price Push Runs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/runs","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","runs"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]},"description":"List the price push history for an integration instance, most recent first.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach run records what triggered it, the guardrail settings that were in force at the time, and a count of every outcome.\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.view` permission.\n\n`status` is one of `running`, `completed`, `blocked`, `failed` or `deferred`. `blocked` is a whole-run abort by the maximum-change breaker before anything was sent. `deferred` is a run that had prices to send and sent none because the channel would not accept a transmission at that moment (a rate gate such as Walmart's 2 price feeds per hour); nothing errored, no per-listing lines are recorded, `eligible_count` shows how many were waiting, and `error` carries the reason and the time the next slot opens. The next cycle sends them."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/runs","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","runs"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 91,\n      \"integration_instance_id\": 1,\n      \"tracked_job_log_id\": 4821,\n      \"trigger\": \"scheduled\",\n      \"trigger_label\": \"Scheduled\",\n      \"status\": \"completed\",\n      \"status_label\": \"Completed\",\n      \"is_dry_run\": false,\n      \"is_forced\": false,\n      \"eligible_count\": 412,\n      \"pushed_count\": 412,\n      \"skipped_count\": 7,\n      \"unchanged_count\": 3102,\n      \"blocked_count\": 2,\n      \"failed_count\": 0,\n      \"guardrail_snapshot\": {\n        \"cadence_minutes\": 30,\n        \"floor_type\": \"margin_pct\",\n        \"floor_value\": 15,\n        \"ceiling_type\": \"none\",\n        \"ceiling_value\": null,\n        \"max_delta_pct\": 50,\n        \"max_delta_action\": \"block_run\",\n        \"require_dry_run_first\": false\n      },\n      \"error\": null,\n      \"started_at\": \"2026-08-22T14:00:04+00:00\",\n      \"completed_at\": \"2026-08-22T14:02:11+00:00\",\n      \"is_no_op\": false\n    },\n    {\n      \"id\": 90,\n      \"integration_instance_id\": 1,\n      \"tracked_job_log_id\": 4816,\n      \"trigger\": \"manual\",\n      \"trigger_label\": \"Manual\",\n      \"status\": \"deferred\",\n      \"status_label\": \"Deferred\",\n      \"is_dry_run\": false,\n      \"is_forced\": false,\n      \"eligible_count\": 405,\n      \"pushed_count\": 0,\n      \"skipped_count\": 1,\n      \"unchanged_count\": 0,\n      \"blocked_count\": 0,\n      \"failed_count\": 0,\n      \"guardrail_snapshot\": {\n        \"cadence_minutes\": 30,\n        \"floor_type\": \"margin_pct\",\n        \"floor_value\": 15,\n        \"ceiling_type\": \"none\",\n        \"ceiling_value\": null,\n        \"max_delta_pct\": 50,\n        \"max_delta_action\": \"block_run\",\n        \"require_dry_run_first\": false\n      },\n      \"error\": \"Walmart accepts 2 price feeds per hour for this seller and both slots are in use — the scheduled push at a 30-minute cadence spends them. The next slot opens at 1:55 PM (in 11 minutes). Nothing was sent; the next scheduled run carries every pending price.\",\n      \"started_at\": \"2026-08-22T13:44:39+00:00\",\n      \"completed_at\": \"2026-08-22T13:44:40+00:00\",\n      \"is_no_op\": true\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 4,\n  \"per_page\": 10,\n  \"total\": 37,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"List Price Push Run Lines","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/runs/{run}/lines","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","runs",":run","lines"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"},{"key":"run","value":"91","description":"Price push run ID"}]},"description":"List the per-listing outcomes for a single price push run.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEvery listing the run considered has a row, including the ones it did not send. A blocked row carries the guardrail that refused it and the numbers behind that decision; a failed row carries the channel's error.\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.view` permission.\n\nEach line also records the policy it was judged under: `pricing_sync_profile` is the pricing sync profile as `{id, name}` (null for a line pushed under the channel's inline settings, or whose profile has since been deleted) and `profile_source` says where that policy came from — `listing`, `product`, `channel` or `inline`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/runs/{run}/lines","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","runs",":run","lines"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"},{"key":"run","value":"91","description":"Price push run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5510,\n      \"run_id\": 91,\n      \"product_listing_id\": 8842,\n      \"channel_sku\": \"LTD-VASE\",\n      \"old_price\": 85,\n      \"new_price\": 12,\n      \"currency\": \"USD\",\n      \"status\": \"blocked\",\n      \"status_label\": \"Blocked\",\n      \"status_color\": \"warning\",\n      \"skip_reason\": \"below_floor\",\n      \"skip_reason_label\": \"Below the price floor\",\n      \"detail\": \"Below the 15% margin floor (landed cost $14.20 requires at least $16.71).\",\n      \"pricing_sync_profile\": {\n        \"id\": 2,\n        \"name\": \"MSRP ceiling\"\n      },\n      \"profile_source\": \"product\",\n      \"created_at\": \"2026-08-22T14:01:52+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 2,\n  \"from\": 1,\n  \"to\": 2\n}"}]},{"name":"Get Price Sync Status","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/settings","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","settings"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]},"description":"Read the stored price push configuration for a sales channel, together with every condition that governs whether a push can run.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`supported` is false when the channel has no price push driver — nothing else in the response applies.\n\n`capability_enabled` reflects the price push capability AND an active sync schedule. `manual_enabled` ignores a paused schedule, because a manual push still runs while the schedule is paused.\n\n`environment_allowed` is false outside production for channels that restrict transmission to production.\n\nPrices are only sent for listings where SKU.io owns the price, so `is_sku_master_of_price` must be true for a push to send anything.\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.view` permission.\n\n`sales_channel_id` identifies the channel behind this integration. `can_refresh_from_channel` indicates whether this channel can re-read its own prices on demand — only channels that report live listing state can, and the rest have no way to refresh the channel-reported price.\n\n`pinned_to_sku_count` — live listings on this instance pinned to SKU.io as their own master of price. The pin is the more granular setting and wins, so these are pushed on schedule even when `is_sku_master_of_price` is false.\n\n**Price-level sequences.** `pricing_tier_ids` is the channel's ordered sequence of pricing level IDs, highest priority first. It is always populated — an instance that never configured a sequence reports `[pricing_tier_id]`, the level it is actually using.\n\n**Fulfillment-channel lanes.** Some channels let one fulfillment channel carry its own sequence, which is checked before the channel sequence for the listings in that lane and falls through to it. `fulfillment_pricing_channels` lists the lanes this channel supports — each with `key`, `label`, `badge` and `listing_count` (the active listings the lane governs) — and is empty on a channel with none. Amazon declares one lane, `MFN` (merchant-fulfilled listings; FBA listings always use the channel sequence). `fulfillment_pricing_tier_ids` holds the configured lane sequences keyed by lane, e.g. `{\"MFN\": [6]}`; a lane absent from it is unset and its listings use `pricing_tier_ids`. When no lane is configured the value is an empty array. `listing_count` is the number of active listings on the instance.\n\n**Pricing sync profile.** `pricing_sync_profile` is the channel's default profile as `{id, name}`, or null when the channel pushes with its inline settings (the `settings` block and `pricing_tier_ids`). When a profile is set it supplies the sequence and guardrails for every listing that does not carry a profile of its own or inherit one from its product.\n\n`push_deferral` is a hint for the push button: when the channel is rate-gating price transmissions (Walmart accepts 2 price feeds per hour and a 30-minute scheduled cadence spends both), it names the time the next slot opens; null when a feed would be accepted now, or when the channel never gates. The answer changes by the minute, so it does not disable the push — starting a push re-checks the gate and refuses with the same message if it is still closed.\n\n`prices_recalculated_at` (ISO 8601) is when any listing on this channel last had its SKU.io price re-resolved — null when nothing on the channel has ever been priced. Use **Recalculate Channel Prices** to refresh it on demand."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/settings","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","settings"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"supported\": true,\n    \"capability_enabled\": true,\n    \"manual_enabled\": true,\n    \"environment_allowed\": true,\n    \"is_sku_master_of_price\": true,\n    \"master_of_price\": \"sku.io\",\n    \"pricing_tier_id\": 1,\n    \"channel_name\": \"Shopify\",\n    \"settings\": {\n      \"cadence_minutes\": 30,\n      \"floor_type\": \"amount\",\n      \"floor_value\": 9.99,\n      \"ceiling_type\": \"none\",\n      \"ceiling_value\": null,\n      \"max_delta_pct\": 25,\n      \"max_delta_action\": \"skip\",\n      \"require_dry_run_first\": false,\n      \"floor_tier_id\": null,\n      \"floor_action\": null,\n      \"ceiling_tier_id\": null,\n      \"ceiling_action\": null\n    },\n    \"sales_channel_id\": 6,\n    \"can_refresh_from_channel\": true,\n    \"pinned_to_sku_count\": 14,\n    \"pricing_tier_ids\": [\n      1\n    ],\n    \"fulfillment_pricing_channels\": [],\n    \"fulfillment_pricing_tier_ids\": [],\n    \"listing_count\": 218,\n    \"pricing_sync_profile\": null,\n    \"push_deferral\": null,\n    \"prices_recalculated_at\": \"2026-09-16T19:30:48+00:00\"\n  }\n}"}]},{"name":"Update Price Push Settings","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/settings","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","settings"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]},"description":"Update the push cadence and price guardrails for an integration instance. Amazon instances may also set `automated_pricing_rule_id` (nullable string, e.g. `328182282407-COMPETITIVE_BUYBOX`): every pushed offer is then attached to that Seller Central Automate Pricing rule, and the resolved floor/ceiling for each listing is published as `minimum_seller_allowed_price` / `maximum_seller_allowed_price` so Amazon's repricer moves the price only between SKU.io's guardrails. Rules are created in Seller Central; SP-API can only attach a SKU to an existing one. Other channels ignore the field.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nGuardrails are applied to every price immediately before it is sent, on every path — scheduled, manual and repricing-driven.\n\nAuthentication: Requires Bearer token.\n\nFields:\n- cadence_minutes (required, integer): how often the scheduled push runs. One of 15, 30, 60, 180, 360, 720, 1440.\n- floor_type (required, string): none, amount or margin_pct.\n- floor_value (required unless floor_type is none, number): the floor amount, or the minimum margin percentage.\n- ceiling_type (required, string): none, amount or markup_pct.\n- ceiling_value (required unless ceiling_type is none, number): the ceiling amount, or the maximum markup percentage.\n- max_delta_pct (optional, number, 0-1000): refuse a price that moves more than this percentage from the last value sent. Omit or null to disable.\n- max_delta_action (required, string): skip to refuse only that listing, or block_run to stop the whole run before anything is sent.\n- require_dry_run_first (optional, boolean): require a preview before the first live push.\n\nA margin floor cannot be verified without a landed cost, and a listing with no cost is blocked rather than sent. A margin floor of 100% or more is rejected as unsatisfiable.\n\nRequires the `integrations.update` permission.\n\n**Pricing-level bounds.** `floor_type` / `ceiling_type` also accept `tier`, which reads the bound from the product's price in the pricing level named by `floor_tier_id` / `ceiling_tier_id` (MAP and MSRP are the usual cases). A product with no price in that level simply has NO bound — it is not blocked, unlike a `margin_pct` rule on a product with no landed cost.\n\n`floor_action` / `ceiling_action` choose what happens when a price violates the bound: `adjust` moves the price to the bound and transmits it, `block` refuses the listing. Omit them (or send null) to get the type's default — `adjust` for `tier`, `block` for `amount` and the percentage types. An adjusted price is reported as a **pushed** line whose `detail` names the bound that moved it, and the maximum-change limit is measured on the ADJUSTED figure.\n\n**Which price to send (the sequence).** `pricing_tier_ids` is an ordered list of pricing level IDs, highest priority first. For each product the first level in the list that holds a price for it supplies the price sent; a product with no price at that level falls through to the next. A product no level prices is reported as `not_priced` and nothing is sent for it.\n\nOrdering is by position only — it is never re-ordered by value, so a level cannot be skipped for being cheaper or dearer than another. Use `floor_tier_id` / `ceiling_tier_id` above for value-based limits such as MAP.\n\n- pricing_tier_ids (optional, array of integers, max 5): the ordered sequence. Each ID must exist and be distinct. Omit the key entirely to leave the saved sequence untouched — this endpoint is also the guardrails save, so an omitted key must not wipe a sequence. Send `[]` to clear it and fall back to the channel's single pricing level.\n- A calculated level (one derived from another) prices every product it can compute, so any level after it is unreachable; the save still succeeds, and the coverage endpoint reports which steps have become unreachable.\n\n**Fulfillment-channel lanes.** `fulfillment_pricing_tier_ids` (optional, object) gives one fulfillment channel its own sequence, keyed by lane: `{\"MFN\": [6]}`. Each lane is an ordered list of up to 5 distinct pricing level IDs, checked before the channel sequence for the listings in that lane and falling through to `pricing_tier_ids` when no lane level prices the product. Only the lanes the channel supports are accepted — Amazon supports `MFN` (merchant-fulfilled listings; FBA listings always use the channel sequence) — and the supported lanes are listed by Get Price Sync Status. Send an empty array for a lane to remove it. Omit the key entirely to leave the saved lanes untouched. Every level across the channel sequence and every lane must share one currency.\n\nReturns 422 for a lane the channel does not support, a level repeated within a lane, or a mix of currencies across the sequence and its lanes.","body":{"mode":"raw","raw":"{\n  \"cadence_minutes\": 30,\n  \"pricing_tier_ids\": [\n    7,\n    2\n  ],\n  \"fulfillment_pricing_tier_ids\": {\n    \"MFN\": [\n      6\n    ]\n  },\n  \"floor_type\": \"tier\",\n  \"floor_value\": null,\n  \"floor_tier_id\": 4,\n  \"floor_action\": \"adjust\",\n  \"ceiling_type\": \"none\",\n  \"ceiling_value\": null,\n  \"ceiling_tier_id\": null,\n  \"ceiling_action\": null,\n  \"max_delta_pct\": 50,\n  \"max_delta_action\": \"block_run\",\n  \"require_dry_run_first\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/settings","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","settings"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"cadence_minutes\": 30,\n    \"floor_type\": \"tier\",\n    \"floor_value\": null,\n    \"floor_tier_id\": 4,\n    \"floor_action\": \"adjust\",\n    \"ceiling_type\": \"none\",\n    \"ceiling_value\": null,\n    \"ceiling_tier_id\": null,\n    \"ceiling_action\": null,\n    \"max_delta_pct\": 50,\n    \"max_delta_action\": \"block_run\",\n    \"require_dry_run_first\": true\n  },\n  \"message\": \"Price push settings saved.\"\n}"}]},{"name":"Get Pricing Level Coverage","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/pricing-tiers/coverage?tier_ids=7,2","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","pricing-tiers","coverage"],"query":[{"key":"tier_ids","value":"7,2","description":"Comma-separated pricing level IDs, in sequence order. Evaluated as sent rather than as saved, so an editor can show what a change would do before committing it. Non-numeric entries are ignored; an empty value returns the catalogue total with no levels.","disabled":true}],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]},"description":"How much of the catalogue each pricing level in a sequence actually covers.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRead-only, and takes the sequence as currently edited rather than as saved.\n\nAuthentication: Requires Bearer token.\n\nResponse fields:\n- total_products (integer): products carrying a price at any level — or, when scoped to a lane, products with a listing in that lane on this instance.\n- fulfillment_channel (string|null): the lane the counts are scoped to, or null for the whole catalogue.\n- tiers (array): one entry per level requested, in sequence order —\n  - id, name, pricing_type, currency_code\n  - is_archived (boolean): archived levels are hidden from the pickers but still supply prices to a sequence they are already in.\n  - priced_count (integer): products this level prices, ignoring the sequence.\n  - wins_count (integer): products this level actually supplies once the levels before it have taken theirs. A step whose earlier steps already cover everything reports 0 here — that is how an unreachable step is identified as a fact rather than a guess.\n- sequence (array of integers): the levels as evaluated.\n- covered_count (integer): products the sequence prices in total. Levels can overlap partially, so this is resolved order-aware and is NOT derivable by arithmetic on priced_count.\n- unpriced_count (integer): products no level in the sequence prices. These are reported as not_priced and nothing is sent for them.\n\nRequires the `integrations.view` permission.\n\n**Scoping to a fulfillment-channel lane.** Pass `fulfillment_channel` (e.g. `MFN` on Amazon) to measure a lane's own sequence over the products that have a listing in that lane on this instance, rather than over the whole catalogue — `total_products`, each level's `priced_count` and `wins_count`, `covered_count` and `unpriced_count` are all taken over that set. The response echoes the lane in `fulfillment_channel` (null when not scoped). A lane the channel does not support returns 422."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/pricing-tiers/coverage?tier_ids=7,2","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","pricing-tiers","coverage"],"query":[{"key":"tier_ids","value":"7,2","description":"Comma-separated pricing level IDs, in sequence order. Evaluated as sent rather than as saved, so an editor can show what a change would do before committing it. Non-numeric entries are ignored; an empty value returns the catalogue total with no levels.","disabled":true}],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_products\": 112,\n    \"fulfillment_channel\": null,\n    \"tiers\": [\n      {\n        \"id\": 7,\n        \"name\": \"Special\",\n        \"pricing_type\": \"absolute\",\n        \"currency_code\": \"USD\",\n        \"is_archived\": false,\n        \"priced_count\": 2,\n        \"wins_count\": 2\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Retail\",\n        \"pricing_type\": \"absolute\",\n        \"currency_code\": \"USD\",\n        \"is_archived\": false,\n        \"priced_count\": 112,\n        \"wins_count\": 110\n      }\n    ],\n    \"sequence\": [\n      7,\n      2\n    ],\n    \"covered_count\": 112,\n    \"unpriced_count\": 0\n  }\n}"}]},{"name":"Get Price Rail Examples","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/pricing-tiers/rail-examples?tier_ids=7,2&floor_tier_id=4&ceiling_tier_id=5","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","pricing-tiers","rail-examples"],"query":[{"key":"tier_ids","value":"7,2","description":"Comma-separated pricing level IDs, in sequence order — the sequence as currently edited. The winning level per product is resolved order-aware, exactly as the coverage endpoint does.","disabled":true},{"key":"floor_tier_id","value":"4","description":"Optional. The pricing level used as the price FLOOR. When set, `floor` answers the first product (by SKU) whose winning sequence price is BELOW its price at this level.","disabled":true},{"key":"ceiling_tier_id","value":"5","description":"Optional. The pricing level used as the price CEILING. When set, `ceiling` answers the first product (by SKU) whose winning sequence price is ABOVE its price at this level.","disabled":true}],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]},"description":"One real product each price rail would move — the sequence editor's preview line (\"Raise to MAP — e.g. RQ-1: its $22.00 level price is sent as $25.00 (its MAP)\").\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRead-only. Takes the sequence and the rails as currently edited rather than as saved, so the editor can show a concrete consequence before the settings are committed.\n\nAuthentication: Requires Bearer token. Permission: `integrations.view`. PAT scope: `integrations:read`.\n\nResponse fields:\n- floor (object|null): null when `floor_tier_id` was not sent, or when no product's winning sequence price is below its floor-level price. Otherwise the first such product by SKU —\n  - product_id, sku, name\n  - level_price (number): the price the sequence chose for the product.\n  - bound_price (number): the product's price at the floor level — what the rail sends instead (or the value it is blocked against).\n- ceiling (object|null): the same shape for the ceiling level — the first product by SKU whose winning price is above its ceiling-level price.\n\nProducts with no usable (> 0) price at the bound level are never examples: the rail does not apply to them."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/pricing-tiers/rail-examples?tier_ids=7,2&floor_tier_id=4&ceiling_tier_id=5","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","pricing-tiers","rail-examples"],"query":[{"key":"tier_ids","value":"7,2","description":"Comma-separated pricing level IDs, in sequence order — the sequence as currently edited. The winning level per product is resolved order-aware, exactly as the coverage endpoint does.","disabled":true},{"key":"floor_tier_id","value":"4","description":"Optional. The pricing level used as the price FLOOR. When set, `floor` answers the first product (by SKU) whose winning sequence price is BELOW its price at this level.","disabled":true},{"key":"ceiling_tier_id","value":"5","description":"Optional. The pricing level used as the price CEILING. When set, `ceiling` answers the first product (by SKU) whose winning sequence price is ABOVE its price at this level.","disabled":true}],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"floor\": {\n      \"product_id\": 4811,\n      \"sku\": \"RQ-1\",\n      \"name\": \"Root Quencher 32oz\",\n      \"level_price\": 22,\n      \"bound_price\": 25\n    },\n    \"ceiling\": {\n      \"product_id\": 5102,\n      \"sku\": \"RQ-7\",\n      \"name\": \"Root Quencher Twin Pack\",\n      \"level_price\": 40,\n      \"bound_price\": 35\n    }\n  }\n}"}]},{"name":"Update Master of Price","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/master","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","master"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]},"description":"Set which side owns the price for a sales channel.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAccepts `sku.io` (SKU.io sends prices to the channel), `sales_channel` (prices are imported from the channel), or `neither` (prices are not synchronised in either direction).\n\nA pricing tier is required when master of price is `sku.io` — it is the tier the pushed price is read from. Without one no price is resolved and nothing is sent.\n\nChanging either value rebuilds the cached listing prices for the channel.\n\nReturns the same payload as Get Price Sync Status.\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.update` permission.","body":{"mode":"raw","raw":"{\n  \"master_of_price\": \"sku.io\",\n  \"pricing_tier_id\": 1\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/master","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","master"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"supported\": true,\n    \"capability_enabled\": true,\n    \"manual_enabled\": true,\n    \"environment_allowed\": true,\n    \"is_sku_master_of_price\": true,\n    \"master_of_price\": \"sku.io\",\n    \"pricing_tier_id\": 1,\n    \"channel_name\": \"Shopify\",\n    \"settings\": {\n      \"cadence_minutes\": 30,\n      \"floor_type\": \"none\",\n      \"floor_value\": null,\n      \"ceiling_type\": \"none\",\n      \"ceiling_value\": null,\n      \"max_delta_pct\": 50,\n      \"max_delta_action\": \"block_run\",\n      \"require_dry_run_first\": true\n    }\n  },\n  \"message\": \"Pricing settings saved.\"\n}"}]},{"name":"List Price Comparison","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/comparison","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","comparison"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]},"description":"Compare the SKU.io price against the price the channel last reported, listing by listing.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`sku_price` is what SKU.io resolves for the listing; `channel_price` is the price the channel last reported. `price_difference` and `price_difference_pct` are SKU.io minus the channel.\n\nEach row carries a status: `in_sync` (the two agree within half a cent), `out_of_sync` (they differ), `not_priced` (SKU.io has no price for the listing), or `unknown_channel_price` (SKU.io has a price but the channel has never reported one).\n\n`master_of_price` is the effective value — the per-listing override where one is set, otherwise the channel-level setting.\n\nSupports advanced filter groups via the `filter_groups` parameter on: listing_sku, external_sku, product_sku, product_name, remote_status, sku_price, channel_price, last_pushed_at.\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.view` permission.\n\n**Which level priced each listing.** Every row carries `priced_from_tier_id` and `priced_from_tier_name` — the level the sequence actually drew the price from, or null when no level prices that product. Filter on `filter[priced_from]` to isolate one level, or `none` for the unpriced.\n\n**Which scope priced each listing.** `priced_from_scope` says where that level came from: `listing` (the listing's own pinned level), `fulfillment` (a step of a fulfillment-channel lane, such as Amazon's MFN lane) or `channel` (a step of the channel sequence); null when no level prices the listing. `is_fba` is true for listings fulfilled by the channel's own network (FBA), which a fulfillment lane never prices. Filter on `filter[priced_from_scope]` and `filter[is_fba]` to isolate what a lane prices. Each row also carries `guardrail_scope` (`channel`, `product` or `listing` — which guardrail rule applies to it), `guardrail_override_id` and `never_pushed`.\n\n`master_of_price_override` and `pinned_pricing_tier_id` are the listing's own stored price ownership — both null while the listing inherits the channel's master of price and price levels. `master_of_price` is the effective value after inheritance.\n\n**Which pricing sync profile governs each listing.** Every row carries `pricing_sync_profile_id` and `pricing_sync_profile_name` — the EFFECTIVE profile after inheritance — and `pricing_sync_profile_source`: `listing` (the listing's own), `product` (inherited from the product), `channel` (the channel default) or `inline` (no profile at any level; the channel's inline settings apply). `pricing_sync_profile_override_id` is the listing's OWN profile, null while it inherits, and `pricing_sync_profile_archived` is true when the effective profile has been archived (it still governs the listing). Filter on `filter[profile]` by profile ID or by source, and sort on `pricing_sync_profile_name`.\n\n**Why a row is Not priced** (call review U1): every row carries `not_priced_reason` — `no_master`, `no_sequence`, `no_channel_price` or `no_price_at_levels` — and `null` on every priced row. It is computed by the same SQL expression that backs `filter[not_priced_reason]` and the summary's `not_priced_reasons` counts, so the tile, the filter and the row can never disagree. A pinned tier, an MFN-lane sequence on an MFN listing, the effective profile's sequence or the inline sequence each count as \"levels exist\".\n\n`filter[master_override]` selects by the listing's OWN master instead (`any`, `sku.io`, `sales_channel`, `neither`) — the pinned listings, independent of the instance setting.\n\n`status` can also be `ended_on_channel`: the channel no longer hosts the listing (retired, expired or deleted — `remote_status` is `missing`). It is its own bucket and never one of the price buckets, whatever the two price columns hold, and such listings are never included in a price push. `status_label` reads \"Ended on channel\"."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/comparison","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","comparison"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 4821,\n      \"product_id\": 915,\n      \"listing_sku\": \"BLK-TEE-M\",\n      \"product_sku\": \"TEE-001\",\n      \"product_name\": \"Black Cotton Tee — Medium\",\n      \"sales_channel_listing_id\": \"7734921\",\n      \"remote_status\": \"active\",\n      \"sku_price\": 24,\n      \"channel_price\": 19.99,\n      \"price_difference\": 4.01,\n      \"price_difference_pct\": 20.06,\n      \"master_of_price\": \"sku.io\",\n      \"master_of_price_override\": null,\n      \"pinned_pricing_tier_id\": null,\n      \"last_pushed_price\": 19.99,\n      \"last_pushed_at\": \"2026-08-19T14:02:11.000000Z\",\n      \"last_push_status\": \"pushed\",\n      \"priced_from_tier_id\": 2,\n      \"priced_from_tier_name\": \"Retail\",\n      \"priced_from_scope\": \"channel\",\n      \"is_fba\": false,\n      \"guardrail_scope\": \"channel\",\n      \"guardrail_override_id\": null,\n      \"pricing_sync_profile_id\": 3,\n      \"pricing_sync_profile_name\": \"MAP protected\",\n      \"pricing_sync_profile_source\": \"channel\",\n      \"pricing_sync_profile_override_id\": null,\n      \"pricing_sync_profile_archived\": false,\n      \"never_pushed\": false,\n      \"price_updated_at\": \"2026-08-22T08:15:40.000000Z\",\n      \"channel_price_checked_at\": \"2026-08-22T06:00:00.000000Z\",\n      \"status\": \"out_of_sync\",\n      \"status_label\": \"Out of sync\",\n      \"status_color\": \"warning\",\n      \"not_priced_reason\": null\n    },\n    {\n      \"id\": 4830,\n      \"product_id\": 236,\n      \"listing_sku\": \"TS-121\",\n      \"product_sku\": \"TS-121\",\n      \"product_name\": \"Tecmate OptiMate Battery Cranking & Charging System Tester\",\n      \"sales_channel_listing_id\": \"5H5UH27KXPQZ\",\n      \"remote_status\": \"missing\",\n      \"sku_price\": 49.95,\n      \"channel_price\": null,\n      \"price_difference\": null,\n      \"price_difference_pct\": null,\n      \"master_of_price\": \"sku.io\",\n      \"master_of_price_override\": null,\n      \"pinned_pricing_tier_id\": null,\n      \"last_pushed_price\": null,\n      \"last_pushed_at\": null,\n      \"last_push_status\": null,\n      \"priced_from_tier_id\": 2,\n      \"priced_from_tier_name\": \"Retail\",\n      \"priced_from_scope\": \"channel\",\n      \"is_fba\": false,\n      \"guardrail_scope\": \"channel\",\n      \"guardrail_override_id\": null,\n      \"pricing_sync_profile_id\": 3,\n      \"pricing_sync_profile_name\": \"MAP protected\",\n      \"pricing_sync_profile_source\": \"channel\",\n      \"pricing_sync_profile_override_id\": null,\n      \"pricing_sync_profile_archived\": false,\n      \"never_pushed\": true,\n      \"price_updated_at\": \"2026-08-22T08:15:40.000000Z\",\n      \"channel_price_checked_at\": \"2026-08-22T06:00:00.000000Z\",\n      \"status\": \"ended_on_channel\",\n      \"status_label\": \"Ended on channel\",\n      \"status_color\": \"grey-darken-1\",\n      \"not_priced_reason\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 9,\n  \"per_page\": 25,\n  \"total\": 218,\n  \"from\": 1,\n  \"to\": 25,\n  \"path\": \"https://app.example.com/api/v2/sales-channels/1/prices/comparison\",\n  \"first_page_url\": \"https://app.example.com/api/v2/sales-channels/1/prices/comparison?page=1\",\n  \"last_page_url\": \"https://app.example.com/api/v2/sales-channels/1/prices/comparison?page=9\",\n  \"next_page_url\": \"https://app.example.com/api/v2/sales-channels/1/prices/comparison?page=2\",\n  \"prev_page_url\": null,\n  \"links\": []\n}"}]},{"name":"Get Price Comparison Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/comparison/summary","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","comparison","summary"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]},"description":"Counts of listings in each price comparison bucket for a sales channel.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`never_pushed` overlaps the other buckets — it counts listings that have a SKU.io price but have never been sent, whatever their current comparison status.\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.view` permission.\n\n`not_priced_reasons` breaks `not_priced` down by cause (call review U1): `{ no_master, no_sequence, no_channel_price, no_price_at_levels }`, all four keys always present. Computed by the same expression each comparison row's `not_priced_reason` uses, so the Not priced tile can name the cause and filter to exactly those rows (`filter[not_priced_reason]` on the comparison endpoint).\n\n`ended_on_channel` counts listings the channel no longer hosts (retired, expired or deleted). They are inside `total` but outside every other bucket, so `in_sync + out_of_sync + not_priced + unknown_channel_price + ended_on_channel = total`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/comparison/summary","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","comparison","summary"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total\": 218,\n    \"in_sync\": 164,\n    \"out_of_sync\": 31,\n    \"not_priced\": 12,\n    \"never_pushed\": 48,\n    \"unknown_channel_price\": 11,\n    \"ended_on_channel\": 18,\n    \"not_priced_reasons\": {\n      \"no_master\": 12,\n      \"no_sequence\": 0,\n      \"no_channel_price\": 0,\n      \"no_price_at_levels\": 0\n    }\n  }\n}"}]},{"name":"List Guardrail Exceptions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/guardrail-overrides","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","guardrail-overrides"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]},"description":"Lists every listing-level exception to this channel's price guardrails.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAn exception supplies the WHOLE guardrail set for its target — floor, ceiling, maximum change and the action taken when the change limit is exceeded. It does not inherit individual fields from the channel. A listing exception replaces the channel's guardrails for that listing.\n\nAuthentication: requires a Bearer token.\n\nFilters:\n- `filter[search]` — matches the exception ID exactly\n- `filter[id]`, `filter[max_delta_pct]`, `filter[floor_value]` — numeric operators\n- `filter[created_at]`, `filter[updated_at]` — datetime operators\n- `filter_groups` — base64-encoded condition tree for compound AND/OR filtering\n\nAllowed sorts: id, created_at, updated_at, floor_value, max_delta_pct. Prefix with `-` for descending. Default sort: `-updated_at`.\n\nPagination: `page` and `per_page` (default 10, maximum 200).\n\n**Pricing-level bounds.** `floor_type` / `ceiling_type` also accept `tier`, which reads the bound from the product's price in the pricing level named by `floor_tier_id` / `ceiling_tier_id` (MAP and MSRP are the usual cases). A product with no price in that level simply has NO bound — it is not blocked, unlike a `margin_pct` rule on a product with no landed cost.\n\n`floor_action` / `ceiling_action` choose what happens when a price violates the bound: `adjust` moves the price to the bound and transmits it, `block` refuses the listing. Omit them (or send null) to get the type's default — `adjust` for `tier`, `block` for `amount` and the percentage types. An adjusted price is reported as a **pushed** line whose `detail` names the bound that moved it, and the maximum-change limit is measured on the ADJUSTED figure.\n\n`target_ownership` is the target listing's stored price ownership — its `master_of_price` and `product_pricing_tier_id`, both null while it inherits from the channel — so a client editing an exception can show ownership as it is rather than assume it. Null for product-scope exceptions, which have no ownership of their own."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/guardrail-overrides","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","guardrail-overrides"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"sales_channel_id\": 6,\n      \"scope\": \"product\",\n      \"target_id\": 146,\n      \"target_label\": \"Glass Storage Jar 500ml\",\n      \"target_sku\": \"DOC-JAR-500\",\n      \"target_route\": \"/products/146\",\n      \"target_ownership\": null,\n      \"floor_type\": \"tier\",\n      \"floor_value\": null,\n      \"floor_tier_id\": 4,\n      \"floor_tier_name\": \"MAP\",\n      \"floor_action\": \"adjust\",\n      \"ceiling_type\": \"none\",\n      \"ceiling_value\": null,\n      \"ceiling_tier_id\": null,\n      \"ceiling_tier_name\": null,\n      \"ceiling_action\": \"block\",\n      \"max_delta_pct\": 25,\n      \"max_delta_action\": \"skip\",\n      \"created_by\": \"Sam Patel\",\n      \"updated_by\": \"Sam Patel\",\n      \"created_at\": \"2026-08-21T09:14:02+00:00\",\n      \"updated_at\": \"2026-08-23T18:00:02+00:00\"\n    },\n    {\n      \"id\": 13,\n      \"sales_channel_id\": 6,\n      \"scope\": \"listing\",\n      \"target_id\": 105,\n      \"target_label\": \"DOC-PAN-28\",\n      \"target_sku\": \"DOC-PAN-28\",\n      \"target_route\": \"/product-listings/105\",\n      \"target_ownership\": {\n        \"master_of_price\": \"sku.io\",\n        \"product_pricing_tier_id\": 7\n      },\n      \"floor_type\": \"amount\",\n      \"floor_value\": 95,\n      \"floor_tier_id\": null,\n      \"floor_tier_name\": null,\n      \"floor_action\": \"block\",\n      \"ceiling_type\": \"none\",\n      \"ceiling_value\": null,\n      \"ceiling_tier_id\": null,\n      \"ceiling_tier_name\": null,\n      \"ceiling_action\": \"block\",\n      \"max_delta_pct\": 10,\n      \"max_delta_action\": \"skip\",\n      \"created_by\": \"Sam Patel\",\n      \"updated_by\": \"Sam Patel\",\n      \"created_at\": \"2026-08-21T09:14:02+00:00\",\n      \"updated_at\": \"2026-08-23T18:00:02+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 2,\n  \"from\": 1,\n  \"to\": 2\n}"}]},{"name":"Create or Replace Guardrail Exception","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/guardrail-overrides","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","guardrail-overrides"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]},"description":"Creates or replaces guardrail exceptions for one or more listings on this channel.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAn exception is the WHOLE guardrail set — every field below is required. There is no per-field inheritance: a `none` floor type means the floor is switched off for that target, not that the channel's floor applies.\n\nAccepts a list of targets so one set of values can be applied to a multi-row selection. Re-sending for a target that already has an exception replaces it.\n\nAuthentication: requires a Bearer token and update permission on integrations.\n\nRequest fields:\n- `scope` (required) — `listing`\n- `target_ids` (required, array of integers, min 1) — listing IDs\n- `floor_type` (required) — `none`, `amount`, or `margin_pct`\n- `floor_value` (nullable, numeric, min 0) — required unless `floor_type` is `none`\n- `ceiling_type` (required) — `none`, `amount`, or `markup_pct`\n- `ceiling_value` (nullable, numeric, min 0) — required unless `ceiling_type` is `none`\n- `max_delta_pct` (nullable, numeric, greater than 0, maximum 1000) — null means no change limit\n- `max_delta_action` (required) — `block_run` or `skip`\n\nExceptions are scoped to a single listing; there is no product-level scope. A fixed-amount floor above a fixed-amount ceiling is rejected: no price could satisfy both, so every push for that target would be blocked.\n\n**Pricing-level bounds.** `floor_type` / `ceiling_type` also accept `tier`, which reads the bound from the product's price in the pricing level named by `floor_tier_id` / `ceiling_tier_id` (MAP and MSRP are the usual cases). A product with no price in that level simply has NO bound — it is not blocked, unlike a `margin_pct` rule on a product with no landed cost.\n\n`floor_action` / `ceiling_action` choose what happens when a price violates the bound: `adjust` moves the price to the bound and transmits it, `block` refuses the listing. Omit them (or send null) to get the type's default — `adjust` for `tier`, `block` for `amount` and the percentage types. An adjusted price is reported as a **pushed** line whose `detail` names the bound that moved it, and the maximum-change limit is measured on the ADJUSTED figure.\n\n`target_ownership` is the target listing's stored price ownership — its `master_of_price` and `product_pricing_tier_id`, both null while it inherits from the channel — so a client editing an exception can show ownership as it is rather than assume it. Null for product-scope exceptions, which have no ownership of their own.","body":{"mode":"raw","raw":"{\n  \"scope\": \"listing\",\n  \"target_ids\": [\n    115,\n    117\n  ],\n  \"floor_type\": \"tier\",\n  \"floor_value\": null,\n  \"floor_tier_id\": 4,\n  \"floor_action\": \"adjust\",\n  \"ceiling_type\": \"none\",\n  \"ceiling_value\": null,\n  \"ceiling_tier_id\": null,\n  \"ceiling_action\": null,\n  \"max_delta_pct\": 10,\n  \"max_delta_action\": \"skip\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/guardrail-overrides","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","guardrail-overrides"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"sales_channel_id\": 6,\n      \"scope\": \"product\",\n      \"target_id\": 146,\n      \"target_label\": \"Glass Storage Jar 500ml\",\n      \"target_sku\": \"DOC-JAR-500\",\n      \"target_route\": \"/products/146\",\n      \"target_ownership\": null,\n      \"floor_type\": \"margin_pct\",\n      \"floor_value\": 30,\n      \"ceiling_type\": \"none\",\n      \"ceiling_value\": null,\n      \"max_delta_pct\": 25,\n      \"max_delta_action\": \"skip\",\n      \"created_by\": \"Sam Patel\",\n      \"updated_by\": \"Sam Patel\",\n      \"created_at\": \"2026-08-21T09:14:02+00:00\",\n      \"updated_at\": \"2026-08-23T18:00:02+00:00\"\n    }\n  ],\n  \"message\": \"Guardrail exception applied to 2 targets.\"\n}"}]},{"name":"Delete Guardrail Exceptions","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/guardrail-overrides","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","guardrail-overrides"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]},"description":"Removes guardrail exceptions by ID. The affected products and listings go back to inheriting this channel's guardrails.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAccepts a list so a multi-row selection can be cleared in one call.\n\nAuthentication: requires a Bearer token and update permission on integrations.\n\nRequest fields:\n- `ids` (required, array of integers, min 1) — exception IDs that must already exist"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/guardrail-overrides","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","guardrail-overrides"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"deleted\": 2\n  },\n  \"message\": \"2 exceptions removed.\"\n}"}]},{"name":"Get Guardrail Exception Counts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/guardrail-overrides/counts","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","guardrail-overrides","counts"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]},"description":"Returns how many guardrail exceptions exist on this channel.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nUsed to show how far the channel's own guardrail settings are actually being applied — a high listing count means the channel defaults govern fewer listings than they appear to.\n\nAuthentication: requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/guardrail-overrides/counts","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","guardrail-overrides","counts"],"variable":[{"key":"integrationInstance","value":"1","description":"Sales channel integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total\": 6,\n    \"products\": 4,\n    \"listings\": 2\n  }\n}"}]}]},{"name":"Pricing Sync Profiles","item":[{"name":"List Pricing Sync Profiles","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/pricing-sync-profiles","host":["{{baseUrl}}"],"path":["api","v2","pricing-sync-profiles"]},"description":"List pricing sync profiles — the reusable outbound price-push policies (price-level sequence, floor, ceiling, maximum change and Amazon rule) that can be set as a channel's default, on a product, or on a single listing. The narrowest assignment wins: listing, then product, then channel; a channel with no profile pushes with its own inline settings.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nProfiles are global, not per channel. Archived profiles are hidden from this list by default (`filter[archived]`) but still govern whatever they were assigned to.\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.view` permission (API tokens: `integrations:read` scope).\n\nEach profile carries:\n- id, name\n- sequence_tier_ids (array of integers) and sequence (array of `{id, name}`, name null when the level no longer exists)\n- floor / ceiling: `{type, value, tier_id, tier_name, action}` — `action` is the action actually in force (`block` or `adjust`; null when the type is `none`)\n- max_delta_pct (number|null), max_delta_action (`skip` or `block_run`)\n- automated_pricing_rule_id (string|null)\n- summary (string): the one-line description of the policy\n- missing_tier_count (integer): sequence steps naming a level that no longer exists\n- usage: `{channel_defaults, products, listings, listings_effective, total}` — where the profile is assigned directly (`total` is the sum of the first three) and how many listings it effectively governs after inheritance\n- is_archived, archived_at, created_by (`{id, name}` or null), created_at, updated_at"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/pricing-sync-profiles","host":["{{baseUrl}}"],"path":["api","v2","pricing-sync-profiles"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3,\n      \"name\": \"MAP protected\",\n      \"sequence_tier_ids\": [\n        7,\n        2\n      ],\n      \"sequence\": [\n        {\n          \"id\": 7,\n          \"name\": \"Amazon US FBA\"\n        },\n        {\n          \"id\": 2,\n          \"name\": \"Retail\"\n        }\n      ],\n      \"floor\": {\n        \"type\": \"tier\",\n        \"value\": null,\n        \"tier_id\": 5,\n        \"tier_name\": \"MAP\",\n        \"action\": \"adjust\"\n      },\n      \"ceiling\": {\n        \"type\": \"none\",\n        \"value\": null,\n        \"tier_id\": null,\n        \"tier_name\": null,\n        \"action\": null\n      },\n      \"max_delta_pct\": 10,\n      \"max_delta_action\": \"skip\",\n      \"automated_pricing_rule_id\": \"328182282407-COMPETITIVE_BUYBOX\",\n      \"summary\": \"Sends Amazon US FBA → Retail · floor MAP · 10% skip\",\n      \"missing_tier_count\": 0,\n      \"usage\": {\n        \"channel_defaults\": 1,\n        \"products\": 12,\n        \"listings\": 3,\n        \"listings_effective\": 1640,\n        \"total\": 16\n      },\n      \"is_archived\": false,\n      \"archived_at\": null,\n      \"created_by\": {\n        \"id\": 4,\n        \"name\": \"Dana Whitfield\"\n      },\n      \"created_at\": \"2026-08-28T15:12:04+00:00\",\n      \"updated_at\": \"2026-09-01T09:41:17+00:00\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"MSRP ceiling\",\n      \"sequence_tier_ids\": [\n        2\n      ],\n      \"sequence\": [\n        {\n          \"id\": 2,\n          \"name\": \"Retail\"\n        }\n      ],\n      \"floor\": {\n        \"type\": \"margin_pct\",\n        \"value\": 15,\n        \"tier_id\": null,\n        \"tier_name\": null,\n        \"action\": \"block\"\n      },\n      \"ceiling\": {\n        \"type\": \"tier\",\n        \"value\": null,\n        \"tier_id\": 6,\n        \"tier_name\": \"MSRP\",\n        \"action\": \"adjust\"\n      },\n      \"max_delta_pct\": 25,\n      \"max_delta_action\": \"skip\",\n      \"automated_pricing_rule_id\": null,\n      \"summary\": \"Sends Retail · floor 15% margin · ceiling MSRP · 25% skip\",\n      \"missing_tier_count\": 0,\n      \"usage\": {\n        \"channel_defaults\": 0,\n        \"products\": 4,\n        \"listings\": 0,\n        \"listings_effective\": 61,\n        \"total\": 4\n      },\n      \"is_archived\": false,\n      \"archived_at\": null,\n      \"created_by\": {\n        \"id\": 4,\n        \"name\": \"Dana Whitfield\"\n      },\n      \"created_at\": \"2026-08-28T15:20:39+00:00\",\n      \"updated_at\": \"2026-08-28T15:20:39+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 100,\n  \"total\": 2,\n  \"from\": 1,\n  \"to\": 2,\n  \"path\": \"https://app.example.com/api/v2/pricing-sync-profiles\",\n  \"first_page_url\": \"https://app.example.com/api/v2/pricing-sync-profiles?page=1\",\n  \"last_page_url\": \"https://app.example.com/api/v2/pricing-sync-profiles?page=1\",\n  \"next_page_url\": null,\n  \"prev_page_url\": null,\n  \"links\": []\n}"}]},{"name":"Create Pricing Sync Profile","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/pricing-sync-profiles","host":["{{baseUrl}}"],"path":["api","v2","pricing-sync-profiles"]},"description":"Create a pricing sync profile.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA profile is a named, reusable price-push policy. It does nothing until it is assigned — as a channel default (Set Channel Default Pricing Sync Profile), on products (Update Product / Bulk Assign Product Pricing Sync Profile) or on listings (Update Product Listing / Bulk Assign Pricing Sync Profile to Listings).\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.update` permission (API tokens: `integrations:write` scope).\n\nRequest body:\n- name (string, required, max 80): unique across all profiles.\n- sequence_tier_ids (array of integers, required, may be empty, max 5): the pricing level IDs to try in order — the first level that prices a product supplies its price. Every level must exist and all must share one currency. An empty sequence is a legal policy that sends nothing. Archived levels are accepted: an archived level still prices, it only leaves the pickers.\n- floor_type (string, required): `none`, `amount`, `margin_pct` or `tier`.\n- floor_value (number, min 0): required when floor_type is `amount` (a price) or `margin_pct` (a margin over landed cost — must be below 100). Cleared for `none` and `tier`.\n- floor_tier_id (integer): required when floor_type is `tier` — the pricing level whose price is the floor (e.g. MAP).\n- floor_action (string, optional): `block` or `adjust`. Null uses the type's default — a `tier` floor adjusts the price up to the level, every other floor blocks the push.\n- ceiling_type (string, required): `none`, `amount`, `markup_pct` or `tier`.\n- ceiling_value (number, min 0): required when ceiling_type is `amount` or `markup_pct` (a markup over landed cost). Cleared for `none` and `tier`.\n- ceiling_tier_id (integer): required when ceiling_type is `tier`.\n- ceiling_action (string, optional): `block` or `adjust`. Null uses the type's default — a `tier` ceiling adjusts the price down to the level, every other ceiling blocks the push.\n- max_delta_pct (number, optional, 0–100): the largest change, as a percentage of the last pushed price, a single push may make. Null disables the limit.\n- max_delta_action (string, required): what happens when the limit is exceeded — `skip` (skip that listing) or `block_run` (stop the whole run).\n- automated_pricing_rule_id (string, optional, max 120, letters, digits, `.`, `_`, `:`, `-`): an Amazon Seller Central Automate Pricing rule ID such as `328182282407-COMPETITIVE_BUYBOX`. Other channels ignore it.\n\nCross-field rules (422): a `tier` floor or ceiling needs its level; the floor and the ceiling cannot both use the same level; an `amount` floor above an `amount` ceiling is refused; a margin floor of 100% or more is refused.\n\nReturns 201 with the new profile and a confirmation `message`.","body":{"mode":"raw","raw":"{\n  \"name\": \"MAP protected\",\n  \"sequence_tier_ids\": [\n    7,\n    2\n  ],\n  \"floor_type\": \"tier\",\n  \"floor_value\": null,\n  \"floor_tier_id\": 5,\n  \"floor_action\": \"adjust\",\n  \"ceiling_type\": \"none\",\n  \"ceiling_value\": null,\n  \"ceiling_tier_id\": null,\n  \"ceiling_action\": null,\n  \"max_delta_pct\": 10,\n  \"max_delta_action\": \"skip\",\n  \"automated_pricing_rule_id\": \"328182282407-COMPETITIVE_BUYBOX\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/pricing-sync-profiles","host":["{{baseUrl}}"],"path":["api","v2","pricing-sync-profiles"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"MAP protected\",\n    \"sequence_tier_ids\": [\n      7,\n      2\n    ],\n    \"sequence\": [\n      {\n        \"id\": 7,\n        \"name\": \"Amazon US FBA\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Retail\"\n      }\n    ],\n    \"floor\": {\n      \"type\": \"tier\",\n      \"value\": null,\n      \"tier_id\": 5,\n      \"tier_name\": \"MAP\",\n      \"action\": \"adjust\"\n    },\n    \"ceiling\": {\n      \"type\": \"none\",\n      \"value\": null,\n      \"tier_id\": null,\n      \"tier_name\": null,\n      \"action\": null\n    },\n    \"max_delta_pct\": 10,\n    \"max_delta_action\": \"skip\",\n    \"automated_pricing_rule_id\": \"328182282407-COMPETITIVE_BUYBOX\",\n    \"summary\": \"Sends Amazon US FBA → Retail · floor MAP · 10% skip\",\n    \"missing_tier_count\": 0,\n    \"usage\": {\n      \"channel_defaults\": 1,\n      \"products\": 12,\n      \"listings\": 3,\n      \"listings_effective\": 1640,\n      \"total\": 16\n    },\n    \"is_archived\": false,\n    \"archived_at\": null,\n    \"created_by\": {\n      \"id\": 4,\n      \"name\": \"Dana Whitfield\"\n    },\n    \"created_at\": \"2026-08-28T15:12:04+00:00\",\n    \"updated_at\": \"2026-09-01T09:41:17+00:00\"\n  },\n  \"message\": \"Profile \\\"MAP protected\\\" created.\"\n}"}]},{"name":"Get Pricing Sync Profile","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/pricing-sync-profiles/{pricingSyncProfile}","host":["{{baseUrl}}"],"path":["api","v2","pricing-sync-profiles",":pricingSyncProfile"],"variable":[{"key":"pricingSyncProfile","value":"3","description":"Pricing sync profile ID"}]},"description":"Get a single pricing sync profile, including its current usage counts.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.view` permission (API tokens: `integrations:read` scope).\n\nEach profile carries:\n- id, name\n- sequence_tier_ids (array of integers) and sequence (array of `{id, name}`, name null when the level no longer exists)\n- floor / ceiling: `{type, value, tier_id, tier_name, action}` — `action` is the action actually in force (`block` or `adjust`; null when the type is `none`)\n- max_delta_pct (number|null), max_delta_action (`skip` or `block_run`)\n- automated_pricing_rule_id (string|null)\n- summary (string): the one-line description of the policy\n- missing_tier_count (integer): sequence steps naming a level that no longer exists\n- usage: `{channel_defaults, products, listings, listings_effective, total}` — where the profile is assigned directly (`total` is the sum of the first three) and how many listings it effectively governs after inheritance\n- is_archived, archived_at, created_by (`{id, name}` or null), created_at, updated_at"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/pricing-sync-profiles/{pricingSyncProfile}","host":["{{baseUrl}}"],"path":["api","v2","pricing-sync-profiles",":pricingSyncProfile"],"variable":[{"key":"pricingSyncProfile","value":"3","description":"Pricing sync profile ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"MAP protected\",\n    \"sequence_tier_ids\": [\n      7,\n      2\n    ],\n    \"sequence\": [\n      {\n        \"id\": 7,\n        \"name\": \"Amazon US FBA\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Retail\"\n      }\n    ],\n    \"floor\": {\n      \"type\": \"tier\",\n      \"value\": null,\n      \"tier_id\": 5,\n      \"tier_name\": \"MAP\",\n      \"action\": \"adjust\"\n    },\n    \"ceiling\": {\n      \"type\": \"none\",\n      \"value\": null,\n      \"tier_id\": null,\n      \"tier_name\": null,\n      \"action\": null\n    },\n    \"max_delta_pct\": 10,\n    \"max_delta_action\": \"skip\",\n    \"automated_pricing_rule_id\": \"328182282407-COMPETITIVE_BUYBOX\",\n    \"summary\": \"Sends Amazon US FBA → Retail · floor MAP · 10% skip\",\n    \"missing_tier_count\": 0,\n    \"usage\": {\n      \"channel_defaults\": 1,\n      \"products\": 12,\n      \"listings\": 3,\n      \"listings_effective\": 1640,\n      \"total\": 16\n    },\n    \"is_archived\": false,\n    \"archived_at\": null,\n    \"created_by\": {\n      \"id\": 4,\n      \"name\": \"Dana Whitfield\"\n    },\n    \"created_at\": \"2026-08-28T15:12:04+00:00\",\n    \"updated_at\": \"2026-09-01T09:41:17+00:00\"\n  }\n}"}]},{"name":"Update Pricing Sync Profile","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/pricing-sync-profiles/{pricingSyncProfile}","host":["{{baseUrl}}"],"path":["api","v2","pricing-sync-profiles",":pricingSyncProfile"],"variable":[{"key":"pricingSyncProfile","value":"3","description":"Pricing sync profile ID"}]},"description":"Update a pricing sync profile. The profile is replaced whole — send every field, not just the ones that changed.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nWhen the price-shaping policy changed (sequence, floor, ceiling, maximum change or Amazon rule) and the profile is in use, every listing it governs is re-priced in the background and `tracked_job_log_id` identifies that job. A rename, or a save that changed nothing, re-prices nothing and returns `tracked_job_log_id: null`.\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.update` permission (API tokens: `integrations:write` scope).\n\nRequest body:\n- name (string, required, max 80): unique across all profiles.\n- sequence_tier_ids (array of integers, required, may be empty, max 5): the pricing level IDs to try in order — the first level that prices a product supplies its price. Every level must exist and all must share one currency. An empty sequence is a legal policy that sends nothing. Archived levels are accepted: an archived level still prices, it only leaves the pickers.\n- floor_type (string, required): `none`, `amount`, `margin_pct` or `tier`.\n- floor_value (number, min 0): required when floor_type is `amount` (a price) or `margin_pct` (a margin over landed cost — must be below 100). Cleared for `none` and `tier`.\n- floor_tier_id (integer): required when floor_type is `tier` — the pricing level whose price is the floor (e.g. MAP).\n- floor_action (string, optional): `block` or `adjust`. Null uses the type's default — a `tier` floor adjusts the price up to the level, every other floor blocks the push.\n- ceiling_type (string, required): `none`, `amount`, `markup_pct` or `tier`.\n- ceiling_value (number, min 0): required when ceiling_type is `amount` or `markup_pct` (a markup over landed cost). Cleared for `none` and `tier`.\n- ceiling_tier_id (integer): required when ceiling_type is `tier`.\n- ceiling_action (string, optional): `block` or `adjust`. Null uses the type's default — a `tier` ceiling adjusts the price down to the level, every other ceiling blocks the push.\n- max_delta_pct (number, optional, 0–100): the largest change, as a percentage of the last pushed price, a single push may make. Null disables the limit.\n- max_delta_action (string, required): what happens when the limit is exceeded — `skip` (skip that listing) or `block_run` (stop the whole run).\n- automated_pricing_rule_id (string, optional, max 120, letters, digits, `.`, `_`, `:`, `-`): an Amazon Seller Central Automate Pricing rule ID such as `328182282407-COMPETITIVE_BUYBOX`. Other channels ignore it.\n\nCross-field rules (422): a `tier` floor or ceiling needs its level; the floor and the ceiling cannot both use the same level; an `amount` floor above an `amount` ceiling is refused; a margin floor of 100% or more is refused.\n- updated_at (ISO-8601 datetime, optional): the `updated_at` you last read. When it no longer matches the stored value — someone else saved the profile in between — the update is refused with 422 on `updated_at` and nothing is written. Omit it to skip the check.","body":{"mode":"raw","raw":"{\n  \"name\": \"MAP protected\",\n  \"sequence_tier_ids\": [\n    7,\n    2\n  ],\n  \"floor_type\": \"tier\",\n  \"floor_value\": null,\n  \"floor_tier_id\": 5,\n  \"floor_action\": \"adjust\",\n  \"ceiling_type\": \"none\",\n  \"ceiling_value\": null,\n  \"ceiling_tier_id\": null,\n  \"ceiling_action\": null,\n  \"max_delta_pct\": 12,\n  \"max_delta_action\": \"skip\",\n  \"automated_pricing_rule_id\": \"328182282407-COMPETITIVE_BUYBOX\",\n  \"updated_at\": \"2026-09-01T09:41:17+00:00\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/pricing-sync-profiles/{pricingSyncProfile}","host":["{{baseUrl}}"],"path":["api","v2","pricing-sync-profiles",":pricingSyncProfile"],"variable":[{"key":"pricingSyncProfile","value":"3","description":"Pricing sync profile ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"MAP protected\",\n    \"sequence_tier_ids\": [\n      7,\n      2\n    ],\n    \"sequence\": [\n      {\n        \"id\": 7,\n        \"name\": \"Amazon US FBA\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Retail\"\n      }\n    ],\n    \"floor\": {\n      \"type\": \"tier\",\n      \"value\": null,\n      \"tier_id\": 5,\n      \"tier_name\": \"MAP\",\n      \"action\": \"adjust\"\n    },\n    \"ceiling\": {\n      \"type\": \"none\",\n      \"value\": null,\n      \"tier_id\": null,\n      \"tier_name\": null,\n      \"action\": null\n    },\n    \"max_delta_pct\": 12,\n    \"max_delta_action\": \"skip\",\n    \"automated_pricing_rule_id\": \"328182282407-COMPETITIVE_BUYBOX\",\n    \"summary\": \"Sends Amazon US FBA → Retail · floor MAP · 12% skip\",\n    \"missing_tier_count\": 0,\n    \"usage\": {\n      \"channel_defaults\": 1,\n      \"products\": 12,\n      \"listings\": 3,\n      \"listings_effective\": 1640,\n      \"total\": 16\n    },\n    \"is_archived\": false,\n    \"archived_at\": null,\n    \"created_by\": {\n      \"id\": 4,\n      \"name\": \"Dana Whitfield\"\n    },\n    \"created_at\": \"2026-08-28T15:12:04+00:00\",\n    \"updated_at\": \"2026-09-02T08:03:45+00:00\"\n  },\n  \"tracked_job_log_id\": 5127,\n  \"message\": \"Profile \\\"MAP protected\\\" saved — re-pricing its listings.\"\n}"}]},{"name":"Delete Pricing Sync Profile","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/pricing-sync-profiles/{pricingSyncProfile}","host":["{{baseUrl}}"],"path":["api","v2","pricing-sync-profiles",":pricingSyncProfile"],"variable":[{"key":"pricingSyncProfile","value":"3","description":"Pricing sync profile ID"}]},"description":"Delete a pricing sync profile. Only an unused profile can be deleted — one that is not a channel default and is not assigned to any product or listing. A profile still in use is refused with 422 naming what to reassign first; archive it instead if you only want it out of the pickers.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.update` permission (API tokens: `integrations:write` scope)."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/pricing-sync-profiles/{pricingSyncProfile}","host":["{{baseUrl}}"],"path":["api","v2","pricing-sync-profiles",":pricingSyncProfile"],"variable":[{"key":"pricingSyncProfile","value":"3","description":"Pricing sync profile ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"deleted\": true\n  },\n  \"message\": \"Profile \\\"Retail only\\\" deleted.\"\n}"}]},{"name":"Archive Pricing Sync Profile","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/pricing-sync-profiles/{pricingSyncProfile}/archive","host":["{{baseUrl}}"],"path":["api","v2","pricing-sync-profiles",":pricingSyncProfile","archive"],"variable":[{"key":"pricingSyncProfile","value":"3","description":"Pricing sync profile ID"}]},"description":"Archive a pricing sync profile. Archiving removes the profile from the pickers and stops it being assigned to anything new; it does NOT stop it governing the products and listings it is already assigned to — those keep pushing under it until reassigned.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA profile that is currently a channel's default cannot be archived: choose another default on that channel first (422).\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.update` permission (API tokens: `integrations:write` scope)."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/pricing-sync-profiles/{pricingSyncProfile}/archive","host":["{{baseUrl}}"],"path":["api","v2","pricing-sync-profiles",":pricingSyncProfile","archive"],"variable":[{"key":"pricingSyncProfile","value":"3","description":"Pricing sync profile ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 2,\n    \"name\": \"MSRP ceiling\",\n    \"sequence_tier_ids\": [\n      2\n    ],\n    \"sequence\": [\n      {\n        \"id\": 2,\n        \"name\": \"Retail\"\n      }\n    ],\n    \"floor\": {\n      \"type\": \"margin_pct\",\n      \"value\": 15,\n      \"tier_id\": null,\n      \"tier_name\": null,\n      \"action\": \"block\"\n    },\n    \"ceiling\": {\n      \"type\": \"tier\",\n      \"value\": null,\n      \"tier_id\": 6,\n      \"tier_name\": \"MSRP\",\n      \"action\": \"adjust\"\n    },\n    \"max_delta_pct\": 25,\n    \"max_delta_action\": \"skip\",\n    \"automated_pricing_rule_id\": null,\n    \"summary\": \"Sends Retail · floor 15% margin · ceiling MSRP · 25% skip\",\n    \"missing_tier_count\": 0,\n    \"usage\": {\n      \"channel_defaults\": 0,\n      \"products\": 4,\n      \"listings\": 0,\n      \"listings_effective\": 61,\n      \"total\": 4\n    },\n    \"is_archived\": true,\n    \"archived_at\": \"2026-09-02T08:10:22+00:00\",\n    \"created_by\": {\n      \"id\": 4,\n      \"name\": \"Dana Whitfield\"\n    },\n    \"created_at\": \"2026-08-28T15:20:39+00:00\",\n    \"updated_at\": \"2026-09-02T08:10:22+00:00\"\n  },\n  \"message\": \"Profile \\\"MSRP ceiling\\\" archived — it leaves the pickers but still governs what it was assigned to.\"\n}"}]},{"name":"Unarchive Pricing Sync Profile","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/pricing-sync-profiles/{pricingSyncProfile}/unarchive","host":["{{baseUrl}}"],"path":["api","v2","pricing-sync-profiles",":pricingSyncProfile","unarchive"],"variable":[{"key":"pricingSyncProfile","value":"3","description":"Pricing sync profile ID"}]},"description":"Restore an archived pricing sync profile so it can be assigned again.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.update` permission (API tokens: `integrations:write` scope)."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/pricing-sync-profiles/{pricingSyncProfile}/unarchive","host":["{{baseUrl}}"],"path":["api","v2","pricing-sync-profiles",":pricingSyncProfile","unarchive"],"variable":[{"key":"pricingSyncProfile","value":"3","description":"Pricing sync profile ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Retail only\",\n    \"sequence_tier_ids\": [\n      2\n    ],\n    \"sequence\": [\n      {\n        \"id\": 2,\n        \"name\": \"Retail\"\n      }\n    ],\n    \"floor\": {\n      \"type\": \"none\",\n      \"value\": null,\n      \"tier_id\": null,\n      \"tier_name\": null,\n      \"action\": null\n    },\n    \"ceiling\": {\n      \"type\": \"none\",\n      \"value\": null,\n      \"tier_id\": null,\n      \"tier_name\": null,\n      \"action\": null\n    },\n    \"max_delta_pct\": null,\n    \"max_delta_action\": \"skip\",\n    \"automated_pricing_rule_id\": null,\n    \"summary\": \"Sends Retail\",\n    \"missing_tier_count\": 0,\n    \"usage\": {\n      \"channel_defaults\": 0,\n      \"products\": 0,\n      \"listings\": 0,\n      \"listings_effective\": 0,\n      \"total\": 0\n    },\n    \"is_archived\": false,\n    \"archived_at\": null,\n    \"created_by\": {\n      \"id\": 4,\n      \"name\": \"Dana Whitfield\"\n    },\n    \"created_at\": \"2026-08-27T10:02:11+00:00\",\n    \"updated_at\": \"2026-09-02T08:12:40+00:00\"\n  },\n  \"message\": \"Profile \\\"Retail only\\\" restored.\"\n}"}]},{"name":"Create Pricing Sync Profile from Channel Settings","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/pricing-sync-profiles/from-instance/{integrationInstance}","host":["{{baseUrl}}"],"path":["api","v2","pricing-sync-profiles","from-instance",":integrationInstance"],"variable":[{"key":"integrationInstance","value":"12","description":"Sales channel integration instance ID"}]},"description":"Create a pricing sync profile from a channel's current inline push settings — its price-level sequence, floor, ceiling, maximum change and Amazon rule — named `<channel name> defaults` (suffixed `(2)`, `(3)`… when that name is taken).\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nWhen the channel has no default profile yet, the new profile becomes its default immediately. Nothing is re-priced and the next push is not forced into a preview, because the profile is an exact copy of the policy the channel already pushes under.\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.update` permission (API tokens: `integrations:write` scope).\n\nNo request body."},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/pricing-sync-profiles/from-instance/{integrationInstance}","host":["{{baseUrl}}"],"path":["api","v2","pricing-sync-profiles","from-instance",":integrationInstance"],"variable":[{"key":"integrationInstance","value":"12","description":"Sales channel integration instance ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 4,\n    \"name\": \"AMZ partusa defaults\",\n    \"sequence_tier_ids\": [\n      7,\n      2\n    ],\n    \"sequence\": [\n      {\n        \"id\": 7,\n        \"name\": \"Amazon US FBA\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Retail\"\n      }\n    ],\n    \"floor\": {\n      \"type\": \"tier\",\n      \"value\": null,\n      \"tier_id\": 5,\n      \"tier_name\": \"MAP\",\n      \"action\": \"adjust\"\n    },\n    \"ceiling\": {\n      \"type\": \"none\",\n      \"value\": null,\n      \"tier_id\": null,\n      \"tier_name\": null,\n      \"action\": null\n    },\n    \"max_delta_pct\": 10,\n    \"max_delta_action\": \"skip\",\n    \"automated_pricing_rule_id\": \"328182282407-COMPETITIVE_BUYBOX\",\n    \"summary\": \"Sends Amazon US FBA → Retail · floor MAP · 10% skip\",\n    \"missing_tier_count\": 0,\n    \"usage\": {\n      \"channel_defaults\": 1,\n      \"products\": 0,\n      \"listings\": 0,\n      \"listings_effective\": 1640,\n      \"total\": 1\n    },\n    \"is_archived\": false,\n    \"archived_at\": null,\n    \"created_by\": {\n      \"id\": 4,\n      \"name\": \"Dana Whitfield\"\n    },\n    \"created_at\": \"2026-09-02T08:15:03+00:00\",\n    \"updated_at\": \"2026-09-02T08:15:03+00:00\"\n  },\n  \"message\": \"Profile \\\"AMZ partusa defaults\\\" created from the channel's current settings.\"\n}"}]},{"name":"Set Channel Default Pricing Sync Profile","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/profile","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","profile"],"variable":[{"key":"integrationInstance","value":"12","description":"Sales channel integration instance ID"}]},"description":"Set or clear the channel's default pricing sync profile — the policy every listing on the channel pushes under unless its product or the listing itself carries a profile of its own.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nChanging the default re-prices the whole channel in the background (`tracked_job_log_id`) and forces the channel's next push to be a preview (`require_dry_run_first: true`), so the effect of the new policy is visible before anything is transmitted. Sending the profile the channel already has does neither.\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.update` permission (API tokens: `integrations:write` scope).\n\nRequest body:\n- pricing_sync_profile_id (integer|null, required — the key must be present): the profile to make the default, or null to return the channel to its inline settings. An archived profile is refused (422).\n\nResponse `data`:\n- pricing_sync_profile (object|null): the profile now in force, in the same shape as Get Pricing Sync Profile, or null when cleared\n- require_dry_run_first (boolean): the next push on this channel will run as a preview\n- tracked_job_log_id (integer|null): the background re-price started by this change","body":{"mode":"raw","raw":"{\n  \"pricing_sync_profile_id\": 3\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/profile","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","profile"],"variable":[{"key":"integrationInstance","value":"12","description":"Sales channel integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"pricing_sync_profile\": {\n      \"id\": 3,\n      \"name\": \"MAP protected\",\n      \"sequence_tier_ids\": [\n        7,\n        2\n      ],\n      \"sequence\": [\n        {\n          \"id\": 7,\n          \"name\": \"Amazon US FBA\"\n        },\n        {\n          \"id\": 2,\n          \"name\": \"Retail\"\n        }\n      ],\n      \"floor\": {\n        \"type\": \"tier\",\n        \"value\": null,\n        \"tier_id\": 5,\n        \"tier_name\": \"MAP\",\n        \"action\": \"adjust\"\n      },\n      \"ceiling\": {\n        \"type\": \"none\",\n        \"value\": null,\n        \"tier_id\": null,\n        \"tier_name\": null,\n        \"action\": null\n      },\n      \"max_delta_pct\": 10,\n      \"max_delta_action\": \"skip\",\n      \"automated_pricing_rule_id\": \"328182282407-COMPETITIVE_BUYBOX\",\n      \"summary\": \"Sends Amazon US FBA → Retail · floor MAP · 10% skip\",\n      \"missing_tier_count\": 0,\n      \"usage\": {\n        \"channel_defaults\": 1,\n        \"products\": 12,\n        \"listings\": 3,\n        \"listings_effective\": 1640,\n        \"total\": 16\n      },\n      \"is_archived\": false,\n      \"archived_at\": null,\n      \"created_by\": {\n        \"id\": 4,\n        \"name\": \"Dana Whitfield\"\n      },\n      \"created_at\": \"2026-08-28T15:12:04+00:00\",\n      \"updated_at\": \"2026-09-01T09:41:17+00:00\"\n    },\n    \"require_dry_run_first\": true,\n    \"tracked_job_log_id\": 5131\n  },\n  \"message\": \"\\\"MAP protected\\\" is now the channel default.\"\n}"}]},{"name":"Get Pricing Sync Profile Coverage","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/profile-coverage","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","profile-coverage"],"variable":[{"key":"integrationInstance","value":"12","description":"Sales channel integration instance ID"}]},"description":"How the channel's listings are distributed across pricing sync profiles — for each profile that governs at least one listing on this channel, how many listings it reaches and by which route.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.view` permission (API tokens: `integrations:read` scope).\n\nResponse `data`:\n- channel_default_profile_id (integer|null): the channel's default profile\n- listings_total (integer): active listings on the channel\n- inline_listings (integer): listings governed by no profile at any level — they push with the channel's inline settings\n- profiles (array): one entry per profile in use on the channel —\n  - profile_id, name, summary\n  - is_default (boolean): this profile is the channel default\n  - is_archived (boolean): archived profiles still appear while they govern listings\n  - listings_direct (integer): listings that carry this profile themselves\n  - listings_via_product (integer): listings inheriting it from their product\n  - listings_inherit (integer): listings inheriting it as the channel default\n  - listings_total (integer): the sum of the three\n\nThe counts are cached for up to a minute; every assignment made through the API refreshes them."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/profile-coverage","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","profile-coverage"],"variable":[{"key":"integrationInstance","value":"12","description":"Sales channel integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"channel_default_profile_id\": 3,\n    \"listings_total\": 1701,\n    \"inline_listings\": 0,\n    \"profiles\": [\n      {\n        \"profile_id\": 3,\n        \"name\": \"MAP protected\",\n        \"is_default\": true,\n        \"is_archived\": false,\n        \"summary\": \"Sends Amazon US FBA → Retail · floor MAP · 10% skip\",\n        \"listings_direct\": 3,\n        \"listings_via_product\": 219,\n        \"listings_inherit\": 1418,\n        \"listings_total\": 1640\n      },\n      {\n        \"profile_id\": 2,\n        \"name\": \"MSRP ceiling\",\n        \"is_default\": false,\n        \"is_archived\": false,\n        \"summary\": \"Sends Retail · floor 15% margin · ceiling MSRP · 25% skip\",\n        \"listings_direct\": 0,\n        \"listings_via_product\": 61,\n        \"listings_inherit\": 0,\n        \"listings_total\": 61\n      }\n    ]\n  }\n}"}]},{"name":"Bulk Assign Pricing Sync Profile to Listings","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/profile-assignments","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","profile-assignments"],"variable":[{"key":"integrationInstance","value":"12","description":"Sales channel integration instance ID"}]},"description":"Assign a pricing sync profile to many of the channel's listings at once — or clear their own profile so they fall back to their product's, then the channel default.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nTarget the listings either by ID list or by the same `filter[...]` query List Price Comparison accepts (send the filter keys without the `filter[]` wrapper, e.g. `{\"status\": \"out_of_sync\"}`). Exactly one of the two must be sent — a body with neither is refused, because it would otherwise mean every listing on the channel. IDs that do not belong to this channel are ignored and reported in `not_found`.\n\nAn explicit selection of up to 200 listings is applied immediately (`mode: \"sync\"`, HTTP 200) and the affected listings are re-priced before the response returns. A larger selection, or any filter scope, runs as a background job (`mode: \"job\"`, HTTP 202) — the filter is resolved to a fixed list of IDs when the job is queued, so listings that start matching later are not swept in.\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.update` permission (API tokens: `integrations:write` scope).\n\nRequest body:\n- pricing_sync_profile_id (integer|null, required — the key must be present): the profile to assign, or null to clear the listings' own profile. An archived profile is refused (422).\n- listing_ids (array of integers, optional): the listings to assign.\n- filters (object, optional): the comparison filters to resolve into listings.\n\nResponse `data` when applied immediately (`mode: \"sync\"`):\n- assigned (integer): listings whose profile changed\n- unchanged (integer): listings that already had this profile\n- not_priced (integer): of the changed listings, how many have no price at any level of the new profile's sequence — nothing will be sent for them\n- not_sku_mastered (integer): of the selection, how many are not mastered by SKU.io and so are never pushed regardless of profile\n- not_found (integer): requested IDs that are not listings on this channel\n\nResponse `data` when queued (`mode: \"job\"`):\n- tracked_job_log_id (integer): the background job to follow\n- total (integer): listings the job will process","body":{"mode":"raw","raw":"{\n  \"pricing_sync_profile_id\": 2,\n  \"listing_ids\": [\n    4821,\n    4822,\n    4830\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/profile-assignments","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","profile-assignments"],"variable":[{"key":"integrationInstance","value":"12","description":"Sales channel integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"mode\": \"sync\",\n    \"assigned\": 2,\n    \"unchanged\": 1,\n    \"not_priced\": 0,\n    \"not_sku_mastered\": 1,\n    \"not_found\": 0\n  },\n  \"message\": \"Profile applied to 2 listings. 1 will not push until SKU.io masters their price.\"\n}"}]},{"name":"Get Listing Price Push Preview","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/listings/{productListing}/preview","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","listings",":productListing","preview"],"variable":[{"key":"integrationInstance","value":"12","description":"Sales channel integration instance ID"},{"key":"productListing","value":"4821","description":"Product listing ID — must belong to this integration instance"}]},"description":"What the next price push would do for one listing, judged by exactly the same gate the push itself uses — so the preview can never disagree with the run about a floor, a ceiling or a maximum-change verdict. Nothing is transmitted.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequires the `integrations.view` permission (API tokens: `integrations:read` scope).\n\nResponse `data`:\n- eligible (boolean): SKU.io is the active master of price for this listing on this channel\n- master_of_price (string|null): the active master as the listing resolves it — `sku.io`, `sales_channel` or `neither`\n- profile (object|null): the effective pricing sync profile `{id, name, source}`, where source is `listing`, `product` or `channel`; null when the channel's inline settings apply\n- priced_from (object|null): `{tier_id, tier_name, price}` — the pricing level the cached price came from\n- sku_price (number|null): the price SKU.io would send before any bound moves it\n- channel_price (number|null): what the channel currently shows, as SKU.io last saw it\n- floor / ceiling (object|null): null when the policy has none; otherwise `{type, tier_id, tier_name, value, action, applies, hit, detail}` — `applies` is false when the bound could not be resolved for this product (e.g. no MAP price), `hit` is true when the price crosses it, and `detail` is the sentence the push would record\n- max_delta (object|null): `{pct, last_pushed_price, change_pct, exceeded, action}` — the maximum-change limit measured against the last pushed price\n- verdict (string): `push` (a price would be sent, possibly adjusted by a bound), `unchanged` (the channel already holds it), `skip` (no price or no channel SKU), `block` (a guardrail refused it) or `not_eligible` (SKU.io is not the master of price — the policy shown is what a pin to SKU.io would put in force)\n- detail (string): one sentence explaining the verdict\n- would_send (number|null): the figure that would actually be transmitted after any bound adjusted it; null unless the verdict is `push`\n\nReturns 403 when the listing does not belong to this integration instance, and 422 when the channel does not support outbound price push."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/sales-channels/{integrationInstance}/prices/listings/{productListing}/preview","host":["{{baseUrl}}"],"path":["api","v2","sales-channels",":integrationInstance","prices","listings",":productListing","preview"],"variable":[{"key":"integrationInstance","value":"12","description":"Sales channel integration instance ID"},{"key":"productListing","value":"4821","description":"Product listing ID — must belong to this integration instance"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"eligible\": true,\n    \"master_of_price\": \"sku.io\",\n    \"profile\": {\n      \"id\": 3,\n      \"name\": \"MAP protected\",\n      \"source\": \"channel\"\n    },\n    \"priced_from\": {\n      \"tier_id\": 7,\n      \"tier_name\": \"Amazon US FBA\",\n      \"price\": 42.5\n    },\n    \"sku_price\": 42.5,\n    \"channel_price\": 39.99,\n    \"channel_price_checked_at\": \"2026-09-18T16:11:52+00:00\",\n    \"floor\": {\n      \"type\": \"tier\",\n      \"tier_id\": 5,\n      \"tier_name\": \"MAP\",\n      \"value\": 39,\n      \"action\": \"adjust\",\n      \"applies\": true,\n      \"hit\": false,\n      \"detail\": \"Floor $39.00 MAP not hit.\"\n    },\n    \"ceiling\": null,\n    \"max_delta\": {\n      \"pct\": 10,\n      \"last_pushed_price\": 39.99,\n      \"change_pct\": 6.28,\n      \"exceeded\": false,\n      \"action\": \"skip\"\n    },\n    \"verdict\": \"push\",\n    \"detail\": \"Next push sends $42.50 (+6.28% from the last pushed $39.99).\",\n    \"would_send\": 42.5\n  }\n}"}]}]},{"name":"Instances","item":[{"name":"List Square Integration Instances","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/square/instances","host":["{{baseUrl}}"],"path":["api","square","instances"]},"description":"List all Square integration instances configured on this tenant, ordered by name.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.instances.index"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/square/instances","host":["{{baseUrl}}"],"path":["api","square","instances"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3,\n      \"name\": \"Main Street Coffee Co.\",\n      \"store_id\": 3,\n      \"environment\": \"sandbox\",\n      \"is_authorized\": true,\n      \"auth_method\": \"oauth\",\n      \"granted_scopes\": [\n        \"MERCHANT_PROFILE_READ\",\n        \"ITEMS_READ\",\n        \"ORDERS_READ\",\n        \"PAYMENTS_READ\",\n        \"CUSTOMERS_READ\",\n        \"INVENTORY_READ\",\n        \"PAYMENTS_WRITE\"\n      ],\n      \"payments_enabled\": true,\n      \"token_expires_at\": \"2026-08-07T18:24:11+00:00\",\n      \"merchant_id\": \"MLKV1XY23ABCD\",\n      \"order_import_start_date\": \"2026-06-01\",\n      \"default_return_warehouse_id\": 3,\n      \"order_sync_watermark_at\": \"2026-07-08T10:15:00+00:00\",\n      \"locations_count\": 2,\n      \"catalog_items_count\": 312,\n      \"orders_count\": 1284,\n      \"created_at\": \"2026-07-01T13:50:16+00:00\",\n      \"updated_at\": \"2026-07-08T10:15:00+00:00\"\n    }\n  ]\n}"}]},{"name":"Get Square Integration Instance","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/square/instances/{squareIntegrationInstance}","host":["{{baseUrl}}"],"path":["api","square","instances",":squareIntegrationInstance"],"variable":[{"key":"squareIntegrationInstance","value":"1","description":"Square integration instance ID"}]},"description":"Get a single Square integration instance by ID.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.instances.show"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/square/instances/{squareIntegrationInstance}","host":["{{baseUrl}}"],"path":["api","square","instances",":squareIntegrationInstance"],"variable":[{"key":"squareIntegrationInstance","value":"1","description":"Square integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Main Street Coffee Co.\",\n    \"store_id\": 3,\n    \"environment\": \"sandbox\",\n    \"is_authorized\": true,\n    \"auth_method\": \"oauth\",\n    \"granted_scopes\": [\n      \"MERCHANT_PROFILE_READ\",\n      \"ITEMS_READ\",\n      \"ORDERS_READ\",\n      \"PAYMENTS_READ\",\n      \"CUSTOMERS_READ\",\n      \"INVENTORY_READ\",\n      \"PAYMENTS_WRITE\"\n    ],\n    \"payments_enabled\": true,\n    \"token_expires_at\": \"2026-08-07T18:24:11+00:00\",\n    \"merchant_id\": \"MLKV1XY23ABCD\",\n    \"order_import_start_date\": \"2026-06-01\",\n    \"default_return_warehouse_id\": 3,\n    \"order_sync_watermark_at\": \"2026-07-08T10:15:00+00:00\",\n    \"locations_count\": 2,\n    \"catalog_items_count\": 312,\n    \"orders_count\": 1284,\n    \"created_at\": \"2026-07-01T13:50:16+00:00\",\n    \"updated_at\": \"2026-07-08T10:15:00+00:00\"\n  }\n}"}]},{"name":"Update Square Integration Instance","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/square/instances/{squareIntegrationInstance}","host":["{{baseUrl}}"],"path":["api","square","instances",":squareIntegrationInstance"],"variable":[{"key":"squareIntegrationInstance","value":"1","description":"Square integration instance ID"}]},"description":"Update a Square integration instance. The manager splits the payload across direct columns (name), connection settings (environment, access_token, merchant_id), and integration settings (order_import_start_date, default_return_warehouse_id).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.instances.update\n\n`payments_enabled` (boolean, optional) — toggles whether the register offers this Square account as a card tender. Automatically set to true when an OAuth grant includes PAYMENTS_WRITE.\n\nThe `name` must stay unique across all of your integrations. Renaming this instance to a name another integration already uses returns 422 with a `NameMustBeUnique` error on the `name` field; keeping its current name always succeeds.","body":{"mode":"raw","raw":"{\n  \"name\": \"Renamed Square Instance\",\n  \"store_id\": 3,\n  \"environment\": \"production\",\n  \"merchant_id\": \"MLXX_ABC123\",\n  \"webhook_signature_key\": \"wbhk_sig_EXAMPLE\",\n  \"payments_enabled\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/square/instances/{squareIntegrationInstance}","host":["{{baseUrl}}"],"path":["api","square","instances",":squareIntegrationInstance"],"variable":[{"key":"squareIntegrationInstance","value":"1","description":"Square integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Main Street Coffee Co.\",\n    \"store_id\": 3,\n    \"environment\": \"sandbox\",\n    \"is_authorized\": true,\n    \"auth_method\": \"oauth\",\n    \"granted_scopes\": [\n      \"MERCHANT_PROFILE_READ\",\n      \"ITEMS_READ\",\n      \"ORDERS_READ\",\n      \"PAYMENTS_READ\",\n      \"CUSTOMERS_READ\",\n      \"INVENTORY_READ\",\n      \"PAYMENTS_WRITE\"\n    ],\n    \"payments_enabled\": true,\n    \"token_expires_at\": \"2026-08-07T18:24:11+00:00\",\n    \"merchant_id\": \"MLKV1XY23ABCD\",\n    \"order_import_start_date\": \"2026-06-01\",\n    \"default_return_warehouse_id\": 3,\n    \"order_sync_watermark_at\": \"2026-07-08T10:15:00+00:00\",\n    \"locations_count\": 2,\n    \"catalog_items_count\": 312,\n    \"orders_count\": 1284,\n    \"created_at\": \"2026-07-01T13:50:16+00:00\",\n    \"updated_at\": \"2026-07-08T10:15:00+00:00\"\n  }\n}"}]},{"name":"Get OAuth Authorize URL","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/square/instances/{squareIntegrationInstance}/oauth/authorize-url","host":["{{baseUrl}}"],"path":["api","square","instances",":squareIntegrationInstance","oauth","authorize-url"],"variable":[{"key":"squareIntegrationInstance","value":"1","description":"Square integration instance id"}]},"description":"Builds the Square OAuth authorization URL for this instance. Redirect the merchant's browser to the returned `authorization_url`; after they approve, Square redirects back and the connection is stored automatically with the granted scopes.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nThe requested scope set always includes the standard sales-channel read scopes plus any scopes the instance was already granted (Square replaces the grant with exactly what is requested, so re-authorizing never narrows an existing grant).\n\nPass `capabilities=payments` to additionally request `PAYMENTS_READ`/`PAYMENTS_WRITE` so the account can process register card payments. Use the same parameter to widen an existing channel-only connection (incremental re-consent).\n\nWhen payment scopes are granted, `payments_enabled` is set on the instance automatically.\n\nRoute name: square.instances.oauth.authorize-url"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/square/instances/{squareIntegrationInstance}/oauth/authorize-url","host":["{{baseUrl}}"],"path":["api","square","instances",":squareIntegrationInstance","oauth","authorize-url"],"variable":[{"key":"squareIntegrationInstance","value":"1","description":"Square integration instance id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"authorization_url\": \"https://connect.squareupsandbox.com/oauth2/authorize?client_id=sq0idb-XXXX&scope=MERCHANT_PROFILE_READ+ITEMS_READ+ORDERS_READ+PAYMENTS_READ+CUSTOMERS_READ+INVENTORY_READ&session=false&state=tenant_abc123\",\n    \"requested_scopes\": [\n      \"MERCHANT_PROFILE_READ\",\n      \"ITEMS_READ\",\n      \"ORDERS_READ\",\n      \"PAYMENTS_READ\",\n      \"CUSTOMERS_READ\",\n      \"INVENTORY_READ\"\n    ]\n  }\n}"}]}]},{"name":"Catalog","item":[{"name":"Get Latest Catalog Sync Info","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/square/catalog/latest-sync-info?integration_instance_id=1","host":["{{baseUrl}}"],"path":["api","square","catalog","latest-sync-info"],"query":[{"key":"integration_instance_id","value":"1","description":"The Square integration instance to inspect (required).","disabled":true}]},"description":"Latest-sync summary for an instance's catalog: the most recently updated catalog item plus total, mapped, and unmapped product counts. Useful before queuing a catalog refresh to decide whether a `since_latest` sync is sufficient.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.catalog.latest-sync-info"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/square/catalog/latest-sync-info?integration_instance_id=1","host":["{{baseUrl}}"],"path":["api","square","catalog","latest-sync-info"],"query":[{"key":"integration_instance_id","value":"1","description":"The Square integration instance to inspect (required).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"latest_product_title\": \"Blue Ceramic Mug - Large\",\n    \"latest_product_date\": \"2026-07-06T18:24:11+00:00\",\n    \"total_products\": 312,\n    \"mapped_products\": 268,\n    \"unmapped_products\": 44\n  }\n}"}]},{"name":"Get Square Catalog Item","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/square/catalog/{squareCatalogItem}","host":["{{baseUrl}}"],"path":["api","square","catalog",":squareCatalogItem"],"variable":[{"key":"squareCatalogItem","value":"1","description":"Square catalog item ID"}]},"description":"Get a single Square catalog item (product or variation), including its mapped SKU product and the raw Square catalog payload.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.catalog.show"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/square/catalog/{squareCatalogItem}","host":["{{baseUrl}}"],"path":["api","square","catalog",":squareCatalogItem"],"variable":[{"key":"squareCatalogItem","value":"1","description":"Square catalog item ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"integration_instance_id\": 1,\n    \"square_catalog_object_id\": \"6XG7EXAMPLEOBJECTID\",\n    \"square_variation_id\": \"VAR7EXAMPLEID\",\n    \"square_parent_id\": null,\n    \"type\": \"ITEM_VARIATION\",\n    \"sku\": \"MUG-BLUE-LG\",\n    \"name\": \"Blue Ceramic Mug - Large\",\n    \"description\": \"16oz glazed ceramic mug\",\n    \"image_url\": \"https://items-images-production.s3.amazonaws.com/files/example/original.jpeg\",\n    \"category\": \"Drinkware\",\n    \"price\": 1899,\n    \"currency_code\": \"USD\",\n    \"is_deleted\": false,\n    \"square_updated_at\": \"2026-07-06T18:24:11+00:00\",\n    \"product_id\": 842,\n    \"product\": {\n      \"id\": 842,\n      \"sku\": \"MUG-BLUE-LG\",\n      \"name\": \"Blue Ceramic Mug - Large\",\n      \"reference\": \"MUG-BLUE-LG\",\n      \"link_route\": \"/products/842\"\n    },\n    \"raw_data\": {\n      \"type\": \"ITEM_VARIATION\",\n      \"id\": \"VAR7EXAMPLEID\",\n      \"item_variation_data\": {\n        \"sku\": \"MUG-BLUE-LG\",\n        \"name\": \"Large\",\n        \"price_money\": {\n          \"amount\": 1899,\n          \"currency\": \"USD\"\n        }\n      }\n    },\n    \"created_at\": \"2026-07-01T09:12:44.000000Z\",\n    \"updated_at\": \"2026-07-06T18:24:11.000000Z\"\n  }\n}"}]},{"name":"List Catalog Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/portal/catalog/products","host":["{{baseUrl}}"],"path":["api","portal","catalog","products"]},"description":"Return the paginated catalog for the buyer's account. Every item carries the buyer's resolved unit price, quantity-break ladder, and availability. Out-of-stock items remain listed (flagged), never hidden. Supports search, brand/category/attribute filters, and sorting by name, sku, price, or availability. Each item's `back_in_stock_subscribed` is true when the authenticated buyer has asked to be emailed when that product is back in stock and has not been emailed yet.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/portal/catalog/products","host":["{{baseUrl}}"],"path":["api","portal","catalog","products"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5001,\n      \"sku\": \"WIDGET-A\",\n      \"name\": \"Widget A\",\n      \"description\": \"Precision widget, blue.\",\n      \"brand\": {\n        \"id\": 3,\n        \"name\": \"Acme\"\n      },\n      \"category\": {\n        \"id\": 9,\n        \"name\": \"Widgets\"\n      },\n      \"image_url\": \"https://cdn.example.com/products/widget-a.jpg\",\n      \"unit_price\": 4.1,\n      \"currency_code\": \"USD\",\n      \"price_source\": \"volume_break\",\n      \"breaks\": [\n        {\n          \"min_quantity\": 12,\n          \"unit_price\": 4.1\n        },\n        {\n          \"min_quantity\": 48,\n          \"unit_price\": 3.8\n        }\n      ],\n      \"availability\": {\n        \"mode\": \"status\",\n        \"status\": \"in_stock\",\n        \"available_qty\": null,\n        \"lead_time_days\": null,\n        \"backorderable\": false\n      },\n      \"min_order_quantity\": 6,\n      \"order_increment\": 12,\n      \"is_variation\": false,\n      \"has_variations\": false,\n      \"back_in_stock_subscribed\": false\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 24,\n  \"total\": 61,\n  \"from\": 1,\n  \"to\": 24\n}"}]},{"name":"Get Catalog Product","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/portal/catalog/products/{product}","host":["{{baseUrl}}"],"path":["api","portal","catalog","products",":product"],"variable":[{"key":"product","value":"5001","description":"Product id."}]},"description":"Return full detail for a catalog product: images, description, specifications, quantity-break price table, availability with lead time, MOQ and case-pack hints, and — for matrix products — the purchasable variations each with their own price and availability. Returns 404 when the product is not resolvable in the active account's catalog. `back_in_stock_subscribed` (on the product and on each variation) is true when the authenticated buyer is waiting for a back-in-stock email for it; the portal shows \"You'll be notified\" with a Stop action.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/portal/catalog/products/{product}","host":["{{baseUrl}}"],"path":["api","portal","catalog","products",":product"],"variable":[{"key":"product","value":"5001","description":"Product id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5001,\n    \"sku\": \"WIDGET-A\",\n    \"name\": \"Widget A\",\n    \"description\": \"Precision widget, blue.\",\n    \"brand\": {\n      \"id\": 3,\n      \"name\": \"Acme\"\n    },\n    \"category\": {\n      \"id\": 9,\n      \"name\": \"Widgets\"\n    },\n    \"image_url\": \"https://cdn.example.com/products/widget-a.jpg\",\n    \"unit_price\": 4.1,\n    \"currency_code\": \"USD\",\n    \"price_source\": \"volume_break\",\n    \"breaks\": [\n      {\n        \"min_quantity\": 12,\n        \"unit_price\": 4.1\n      },\n      {\n        \"min_quantity\": 48,\n        \"unit_price\": 3.8\n      }\n    ],\n    \"availability\": {\n      \"mode\": \"status\",\n      \"status\": \"in_stock\",\n      \"available_qty\": null,\n      \"lead_time_days\": null,\n      \"backorderable\": false\n    },\n    \"min_order_quantity\": 6,\n    \"order_increment\": 12,\n    \"is_variation\": false,\n    \"has_variations\": false,\n    \"images\": [\n      \"https://cdn.example.com/products/widget-a.jpg\"\n    ],\n    \"specifications\": [\n      {\n        \"name\": \"Color\",\n        \"value\": \"Blue\"\n      },\n      {\n        \"name\": \"Material\",\n        \"value\": \"ABS\"\n      }\n    ],\n    \"variations\": [],\n    \"back_in_stock_subscribed\": true\n  }\n}"}]},{"name":"Get Catalog Filters","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/portal/catalog/filters","host":["{{baseUrl}}"],"path":["api","portal","catalog","filters"]},"description":"Return the brands, categories, and attributes available within the active account's catalog. Excluded brands/categories do not appear, so filter controls only ever offer values the buyer can actually see.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/portal/catalog/filters","host":["{{baseUrl}}"],"path":["api","portal","catalog","filters"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"brands\": [\n      {\n        \"id\": 3,\n        \"name\": \"Acme\"\n      }\n    ],\n    \"categories\": [\n      {\n        \"id\": 9,\n        \"name\": \"Widgets\"\n      }\n    ],\n    \"attributes\": [\n      {\n        \"name\": \"Color\",\n        \"values\": [\n          \"Blue\",\n          \"Red\"\n        ]\n      }\n    ]\n  }\n}"}]},{"name":"Subscribe to Back-in-Stock Alert","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/portal/catalog/products/{product}/notify-me","host":["{{baseUrl}}"],"path":["api","portal","catalog","products",":product","notify-me"],"variable":[{"key":"product","value":"5004","description":"Product id."}]},"description":"Subscribe the authenticated buyer to a back-in-stock alert for an out-of-stock product. Idempotent — re-subscribing refreshes the pending alert. The buyer is emailed when the product's available-to-sell quantity next rises above zero. Returns 404 when the product is not resolvable in the active account's catalog.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/portal/catalog/products/{product}/notify-me","host":["{{baseUrl}}"],"path":["api","portal","catalog","products",":product","notify-me"],"variable":[{"key":"product","value":"5004","description":"Product id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"You'll be notified when this product is back in stock.\"\n}"}]},{"name":"Unsubscribe from Back-in-Stock Alert","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/portal/catalog/products/{product}/notify-me","host":["{{baseUrl}}"],"path":["api","portal","catalog","products",":product","notify-me"],"variable":[{"key":"product","value":"5004","description":"Product id."}]},"description":"Remove the authenticated buyer's back-in-stock alert for a product.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/portal/catalog/products/{product}/notify-me","host":["{{baseUrl}}"],"path":["api","portal","catalog","products",":product","notify-me"],"variable":[{"key":"product","value":"5004","description":"Product id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Alert removed.\"\n}"}]}]},{"name":"Orders","item":[{"name":"Get Latest Order Sync Info","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/square/orders/latest-sync-info?integration_instance_id=1","host":["{{baseUrl}}"],"path":["api","square","orders","latest-sync-info"],"query":[{"key":"integration_instance_id","value":"1","description":"The Square integration instance to inspect (required).","disabled":true}]},"description":"Latest-sync summary for an instance's orders: the most recently updated synced order and the total number of orders stored. Useful before queuing a refresh to decide whether a `since_latest` sync is sufficient.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.orders.latest-sync-info"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/square/orders/latest-sync-info?integration_instance_id=1","host":["{{baseUrl}}"],"path":["api","square","orders","latest-sync-info"],"query":[{"key":"integration_instance_id","value":"1","description":"The Square integration instance to inspect (required).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"latest_order_id\": \"CAISENgvlJ6jLWAzERDzjyHVybY\",\n    \"latest_order_date\": \"2026-07-06T18:24:11+00:00\",\n    \"total_orders\": 1284\n  }\n}"}]},{"name":"Get Square Order","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/square/orders/{squareOrder}","host":["{{baseUrl}}"],"path":["api","square","orders",":squareOrder"],"variable":[{"key":"squareOrder","value":"1","description":"Square order row ID"}]},"description":"Get a single Square order by ID, including line items.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.orders.show\n\nWhen the order has been promoted, `sales_order` includes the SKU sales order's order/fulfillment/payment statuses, a `link_route` to its detail page, and its `fulfillments` (tracking number, warehouse, shipping method, and fulfilled line items). `raw_data` (the full Square API payload) is only included on this single-order endpoint, not on the list."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/square/orders/{squareOrder}","host":["{{baseUrl}}"],"path":["api","square","orders",":squareOrder"],"variable":[{"key":"squareOrder","value":"1","description":"Square order row ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 128,\n    \"integration_instance_id\": 1,\n    \"square_order_id\": \"CAISENgvlJ6jLWAzERDzjyHVybY\",\n    \"square_location_id\": \"L2XXXXX7A\",\n    \"state\": \"COMPLETED\",\n    \"source_name\": \"Point of Sale\",\n    \"channel_source\": \"pos\",\n    \"is_pos\": true,\n    \"customer_id\": \"8FMBVGB0Q3GVWMYE86HB4X8H70\",\n    \"customer_email\": \"jane@example.com\",\n    \"customer_name\": \"Jane Doe\",\n    \"subtotal\": 45,\n    \"tax_total\": 3.94,\n    \"discount_total\": 0,\n    \"service_charge_total\": 0,\n    \"tip_total\": 5,\n    \"total\": 53.94,\n    \"currency_code\": \"USD\",\n    \"square_created_at\": \"2026-07-05T14:03:22+00:00\",\n    \"square_updated_at\": \"2026-07-05T14:05:10+00:00\",\n    \"closed_at\": \"2026-07-05T14:05:10+00:00\",\n    \"fulfillment_state\": \"COMPLETED\",\n    \"sync_status\": \"synced\",\n    \"sync_error\": null,\n    \"last_synced_at\": \"2026-07-06T18:24:11+00:00\",\n    \"sales_order_id\": 918,\n    \"sales_order\": {\n      \"id\": 918,\n      \"sales_order_number\": \"SO-000918\",\n      \"order_status\": \"completed\",\n      \"fulfillment_status\": \"fulfilled\",\n      \"payment_status\": \"paid\",\n      \"link_route\": \"/orders/sales-orders/918\",\n      \"fulfillments\": [\n        {\n          \"id\": 501,\n          \"status\": \"fulfilled\",\n          \"fulfillment_type\": \"shipping\",\n          \"tracking_number\": \"1Z999AA10123456784\",\n          \"cost\": 7.25,\n          \"fulfilled_at\": \"2026-07-05T16:10:00.000000Z\",\n          \"submitted_to_sales_channel_at\": null,\n          \"warehouse\": {\n            \"id\": 3,\n            \"name\": \"Main Warehouse\"\n          },\n          \"shipping_method\": {\n            \"id\": 2,\n            \"name\": \"UPS Ground\"\n          },\n          \"line_items\": [\n            {\n              \"id\": 1203,\n              \"quantity\": 2,\n              \"product_name\": \"Blue Ceramic Mug - Large\",\n              \"sku\": \"MUG-BLU-L\"\n            }\n          ]\n        }\n      ]\n    },\n    \"line_items\": [\n      {\n        \"id\": 77,\n        \"uid\": \"8CBRDDGRrM6cSf3HpiUmwB\",\n        \"sku\": \"MUG-BLU-L\",\n        \"name\": \"Blue Ceramic Mug\",\n        \"variation_name\": \"Large\",\n        \"quantity\": 2,\n        \"base_price\": 22.5,\n        \"gross_sales\": 45,\n        \"total_tax\": 3.94,\n        \"total_discount\": 0,\n        \"total\": 48.94,\n        \"currency_code\": \"USD\",\n        \"square_catalog_object_id\": \"W62UWFY35CWMYGVWK6TWJDNI\"\n      }\n    ],\n    \"raw_data\": {\n      \"id\": \"CAISENgvlJ6jLWAzERDzjyHVybY\",\n      \"location_id\": \"L2XXXXX7A\",\n      \"state\": \"COMPLETED\"\n    },\n    \"created_at\": \"2026-07-05T14:06:00+00:00\",\n    \"updated_at\": \"2026-07-06T18:24:11+00:00\"\n  }\n}"}]},{"name":"Sync Square Orders","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/square/orders/sync","host":["{{baseUrl}}"],"path":["api","square","orders","sync"]},"description":"Queue a background refresh of orders from the Square API. The job fetches orders modified since the last sync watermark (default), then promotes newly fetched orders into SKU sales orders. Progress can be tracked via the returned tracked_job_log_id.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nBody fields:\n- integration_instance_id (integer, required) — the Square integration instance to sync.\n- mode (string, optional) — one of `since_latest` (default behavior; resume from the last sync watermark) or `all` (full re-sync from the instance's order-import start date).\n- date_from (date, optional) — explicit start date for the fetch window (overrides mode).\n\nRoute name: square.orders.sync","body":{"mode":"raw","raw":"{\n  \"integration_instance_id\": 1,\n  \"mode\": \"since_latest\",\n  \"date_from\": \"2026-06-01\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/square/orders/sync","host":["{{baseUrl}}"],"path":["api","square","orders","sync"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 9421\n  },\n  \"message\": \"Square orders sync started.\"\n}"}]},{"name":"Submit Square Order Fulfillments","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/square/orders/submit-fulfillments","host":["{{baseUrl}}"],"path":["api","square","orders","submit-fulfillments"]},"description":"Push pending SKU fulfillments back to Square for the given instance. Omit `fulfillment_ids` to submit every pending fulfillment on the instance; provide them to submit a specific subset. Returns per-run counts (submitted, skipped, failed) plus any per-fulfillment errors.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nBody fields:\n- integration_instance_id (integer, required) — the Square instance.\n- fulfillment_ids (array of integers, optional) — specific SKU fulfillment IDs; omit to submit all pending.\n\nRoute name: square.orders.submit-fulfillments","body":{"mode":"raw","raw":"{\n  \"integration_instance_id\": 1,\n  \"fulfillment_ids\": [\n    1201,\n    1202\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/square/orders/submit-fulfillments","host":["{{baseUrl}}"],"path":["api","square","orders","submit-fulfillments"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 fulfillment(s) submitted to Square.\",\n  \"submitted\": 2,\n  \"skipped\": 0,\n  \"failed\": 0,\n  \"errors\": []\n}"}]},{"name":"Get Order By ID","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/orders/{magento2Order}","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","orders",":magento2Order"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"magento2Order","value":"1042","description":"Internal Magento2Order ID (NOT the Magento `entity_id`)."}]},"description":"Fetch a single Magento 2 order by its internal ID, scoped to the supplied integration instance. The response includes the Magento money block, timestamps, the resolved Magento admin URL, all line items, shipments, invoices, credit memos, and — when linked — the SKU.io sales order with its fulfillments. `sku_sales_order_updated_at` carries that linked order's last create/update timestamp (the \"Last SKU Sync\" column on the orders list), or null when the Magento order isn't mapped yet.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Embedded `sku_sales_order.fulfillments[]`** drives the Fulfillments accordion panel on the order detail page. Each fulfillment carries: `id`, `status`, `fulfillment_type`, `tracking_number`, `cost`, `fulfilled_at`, `submitted_to_sales_channel_at`, the `warehouse` (id + name), the `shipping_method` (id + name), and the per-line `line_items[]` array. When the linked SKU sales order has no fulfillments the array is empty.\n\n**Activity log + raw payload** are NOT included here — they are lazy-loaded by the detail page from `/orders/:id/activity-log` and `/orders/:id/raw` respectively. Pass `include_raw=1` if you need `raw_payload` inline.\n\n**Authentication:** Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/orders/{magento2Order}","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","orders",":magento2Order"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"magento2Order","value":"1042","description":"Internal Magento2Order ID (NOT the Magento `entity_id`)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1042,\n    \"integration_instance_id\": 42,\n    \"magento_entity_id\": 5821,\n    \"magento_increment_id\": 5821,\n    \"magento_state\": \"processing\",\n    \"magento_status\": \"processing\",\n    \"store_id\": 1,\n    \"customer_id\": 312,\n    \"customer_email\": \"buyer@example.com\",\n    \"currency_code\": \"USD\",\n    \"base_grand_total\": \"129.99\",\n    \"grand_total\": \"129.99\",\n    \"tax_amount\": \"10.40\",\n    \"shipping_amount\": \"9.99\",\n    \"discount_amount\": \"-5.00\",\n    \"total_paid\": \"129.99\",\n    \"total_refunded\": \"0.00\",\n    \"magento_created_at\": \"2026-06-15T10:22:18+00:00\",\n    \"magento_updated_at\": \"2026-06-15T10:25:00+00:00\",\n    \"created_at\": \"2026-06-15T10:22:30+00:00\",\n    \"updated_at\": \"2026-06-15T10:30:00+00:00\",\n    \"archived_at\": null,\n    \"error_log\": null,\n    \"magento_admin_url\": \"https://shop.example.com/admin/sales/order/view/order_id/5821\",\n    \"sku_sales_order_id\": 99012,\n    \"sku_sales_order\": {\n      \"id\": 99012,\n      \"number\": \"SO-99012\",\n      \"sales_order_number\": \"SO-99012\",\n      \"status\": \"open\",\n      \"order_status\": \"open\",\n      \"fulfillment_status\": \"partial\",\n      \"payment_status\": \"paid\",\n      \"fulfillments\": [\n        {\n          \"id\": 7710,\n          \"status\": \"fulfilled\",\n          \"fulfillment_type\": \"warehouse\",\n          \"tracking_number\": \"1Z999AA10123456784\",\n          \"cost\": 9.99,\n          \"fulfilled_at\": \"2026-06-16T14:05:11+00:00\",\n          \"submitted_to_sales_channel_at\": \"2026-06-16T14:10:00+00:00\",\n          \"warehouse\": {\n            \"id\": 3,\n            \"name\": \"Main Warehouse\"\n          },\n          \"shipping_method\": {\n            \"id\": 12,\n            \"name\": \"UPS Ground\"\n          },\n          \"line_items\": [\n            {\n              \"id\": 88410,\n              \"quantity\": 1,\n              \"product_name\": \"Acme Widget Pro\",\n              \"sku\": \"AWP-001\"\n            }\n          ]\n        }\n      ]\n    },\n    \"sku_sales_order_updated_at\": \"2026-06-16T14:10:00+00:00\",\n    \"integration_instance\": {\n      \"id\": 42,\n      \"name\": \"Acme Magento 2 Store\"\n    },\n    \"customer\": {\n      \"id\": 312,\n      \"magento_customer_id\": 9001,\n      \"email\": \"buyer@example.com\",\n      \"first_name\": \"Jane\",\n      \"last_name\": \"Doe\"\n    },\n    \"line_items_count\": 1,\n    \"line_items\": [\n      {\n        \"id\": 88410,\n        \"magento_item_id\": 18221,\n        \"magento_product_id\": 4501,\n        \"sku\": \"AWP-001\",\n        \"name\": \"Acme Widget Pro\",\n        \"image_url\": \"https://shop.example.com/media/catalog/product/a/w/awp-001.jpg\",\n        \"qty_ordered\": 1,\n        \"qty_invoiced\": 1,\n        \"qty_shipped\": 1,\n        \"qty_refunded\": 0,\n        \"price\": \"114.60\",\n        \"tax_amount\": \"10.40\",\n        \"discount_amount\": \"-5.00\",\n        \"row_total\": \"129.99\"\n      }\n    ],\n    \"shipments\": [\n      {\n        \"id\": 4710,\n        \"magento_entity_id\": 6611,\n        \"magento_increment_id\": \"000006611\",\n        \"tracking_number\": \"1Z999AA10123456784\",\n        \"tracking_carrier\": \"ups\",\n        \"source_code\": \"default\",\n        \"magento_created_at\": \"2026-06-16T14:00:00+00:00\",\n        \"pushed_to_magento_at\": \"2026-06-16T14:10:00+00:00\",\n        \"sku_sales_order_fulfillment_id\": 7710\n      }\n    ],\n    \"invoices\": [\n      {\n        \"id\": 5510,\n        \"magento_entity_id\": 7022,\n        \"magento_increment_id\": \"000007022\",\n        \"state\": \"paid\",\n        \"grand_total\": 129.99,\n        \"subtotal\": 114.6,\n        \"tax_amount\": 10.4,\n        \"shipping_amount\": 9.99,\n        \"discount_amount\": -5,\n        \"magento_created_at\": \"2026-06-15T10:25:00+00:00\",\n        \"pushed_to_accounting_at\": \"2026-06-16T09:00:00+00:00\",\n        \"reconciled_at\": null,\n        \"payment_method\": \"stripe\"\n      }\n    ],\n    \"credit_memos\": [],\n    \"raw_payload\": null\n  }\n}"}]},{"name":"Refresh Orders","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/orders/refresh","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","orders","refresh"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Dispatch the tracked job that refreshes Magento 2 orders. Returns the `tracked_job_log_id` so the frontend can pivot the global JobProgressTray to it. Job name prefix is `Refresh Magento 2 Orders: ` — the page's `lastCompletedJob` watcher matches on this prefix.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Body params:**\n- `mode` *(required)* — one of `since_latest`, `from_start_date`, `date_range`, `all`.\n  - `since_latest` resumes from the cursor stored on the local orders table (falls back to `since_at` then to the integration's configured start_date).\n  - `from_start_date` walks from the integration's configured start date (Settings → Orders → Start date).\n  - `date_range` walks a bounded `[date_from, date_to]` window.\n  - `all` — **Full Re-sync.** Walks every order Magento returns, ignoring start_date entirely. Mirrors the option BigCommerce / eBay / WooCommerce already expose.\n- `since_at` *(optional, ISO datetime)* — fallback cursor used by `since_latest` when our local orders table is empty.\n- `date_from` / `date_to` *(required when `mode=date_range`, ISO date)* — inclusive bounds of the date window.","body":{"mode":"raw","raw":"{\n  \"mode\": \"all\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/orders/refresh","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","orders","refresh"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 92148\n  },\n  \"message\": \"Order sync queued.\"\n}"}]},{"name":"Pre-Start-Date Orders — Search Specific Order","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/orders/pre-start-date-search","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","orders","pre-start-date-search"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Live single-order lookup against the Magento store for the pre-start-date import dialog's **Search specific order** tab. Hits Magento directly via the REST API (GET /V1/orders/{id} for `entity_id`, GET /V1/orders?searchCriteria for `email`) — local DB lookups would miss orders that have never been imported, which is the whole point of a pre-start-date back-fill.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Body params:**\n- `search_by` *(required)* — `entity_id` (Magento order entity id) or `email` (customer email, exact match).\n- `search_term` *(required, max 255 chars)* — the value to look up. For `entity_id` must parse as a positive integer.\n\n**Response shape** mirrors the Browse-by-date result so the shared dialog can render either path uniformly:\n- `data[]` — orders found, each with `eligible`, `eligibility_reason`, `already_imported`, `line_items[]`, etc.\n- `inventory_start_date` — the integration's configured `sync_start_date` (orders on or after this date are not eligible — covered by regular sync).\n- `meta.total_queried` / `meta.eligible_count` / `meta.already_imported_count`.\n\nAn `entity_id` that doesn't exist in Magento returns `data: []` (NOT a 404) so the dialog can show a clean 'no results' state.","body":{"mode":"raw","raw":"{\n  \"search_by\": \"entity_id\",\n  \"search_term\": \"5421\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/orders/pre-start-date-search","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","orders","pre-start-date-search"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"magento_entity_id\": 5421,\n      \"increment_id\": \"100000123\",\n      \"name\": \"100000123\",\n      \"email\": \"customer@example.com\",\n      \"created_at\": \"2023-11-04 14:22:11\",\n      \"magento_status\": \"complete\",\n      \"magento_state\": \"complete\",\n      \"total_price\": \"129.95\",\n      \"currency\": \"USD\",\n      \"item_count\": 2,\n      \"total_quantity\": 3,\n      \"line_items\": [\n        {\n          \"title\": \"Acme Hoodie - Medium - Blue\",\n          \"sku\": \"ACME-HOODIE-M-BLU\",\n          \"quantity\": 2,\n          \"current_quantity\": 2,\n          \"price\": \"49.99\",\n          \"variant_title\": null\n        },\n        {\n          \"title\": \"Stickers Pack\",\n          \"sku\": \"STICKER-PK\",\n          \"quantity\": 1,\n          \"current_quantity\": 1,\n          \"price\": \"29.97\",\n          \"variant_title\": null\n        }\n      ],\n      \"eligible\": true,\n      \"eligibility_reason\": null,\n      \"already_imported\": false\n    }\n  ],\n  \"inventory_start_date\": \"2024-01-01\",\n  \"meta\": {\n    \"total_queried\": 1,\n    \"eligible_count\": 1,\n    \"already_imported_count\": 0\n  }\n}"}]},{"name":"Pre-Start-Date Orders — Import","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/orders/import-pre-start-date","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","orders","import-pre-start-date"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Dispatches the tracked job that back-fills Magento 2 orders pre-dating the integration's normal sync start date. Returns `tracked_job_log_id` so the frontend can pivot the global JobProgressTray to it. Job name prefix is `Import Pre-Start-Date Magento 2 Orders: `.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Two modes — supply exactly one:**\n- **Date-range mode** *(Browse-by-date tab)* — walks every Magento order in [`created_at` >= `date_from`, `created_at` <= `date_to`].\n  - `date_from` *(required, date)* — inclusive lower bound.\n  - `date_to` *(required, date, >= date_from)* — inclusive upper bound.\n- **Entity-ids mode** *(Search-specific-order tab)* — imports only the explicit entity ids the user picked from the live Magento search.\n  - `magento_entity_ids[]` *(required, array of positive integers)* — Magento order entity ids to import. Each is fetched live via `syncManager->syncOrder()` and upserted into `magento2_orders`.\n\nWhen `magento_entity_ids` is present, `date_from` / `date_to` are not required and are ignored if supplied.\n\nAlso accessible via the alias route `POST /api/magento2/integration-instances/{instance}/pre-start-date-orders/import`.","body":{"mode":"raw","raw":"{\n  \"magento_entity_ids\": [\n    5421,\n    5422\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/orders/import-pre-start-date","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","orders","import-pre-start-date"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Pre-start-date import job queued\",\n  \"data\": {\n    \"tracked_job_log_id\": 92201\n  }\n}"}]},{"name":"Refresh Single Square Order","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/square/orders/{squareOrder}/refresh","host":["{{baseUrl}}"],"path":["api","square","orders",":squareOrder","refresh"],"variable":[{"key":"squareOrder","value":"1","description":"Square order row ID"}]},"description":"Refetch a single Square order from the Square API and re-promote it if a linked SKU sales order exists.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.orders.refresh"}},{"name":"Create SKU Orders (bulk)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/square/orders/create-sku-orders","host":["{{baseUrl}}"],"path":["api","square","orders","create-sku-orders"]},"description":"Bulk-promote selected Square orders into SKU sales orders. Only rows in the `pending` state with no existing sales_order_id are considered.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.orders.create-sku-orders","body":{"mode":"raw","raw":"{\n  \"integration_instance_id\": 1,\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}}},{"name":"Update SKU Orders (bulk)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/square/orders/update-sku-orders","host":["{{baseUrl}}"],"path":["api","square","orders","update-sku-orders"]},"description":"Bulk-refresh linked SKU sales orders — unlinks the existing SO and re-promotes each Square order so the SO is regenerated with latest data.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.orders.update-sku-orders","body":{"mode":"raw","raw":"{\n  \"integration_instance_id\": 1,\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}}},{"name":"Delete Single SKU Order (unlink)","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/square/orders/{squareOrder}/sku-order","host":["{{baseUrl}}"],"path":["api","square","orders",":squareOrder","sku-order"],"variable":[{"key":"squareOrder","value":"1","description":"Square order row ID"}]},"description":"Detach the linked SKU sales order from a single Square order (deletes the SKU order; the Square row is kept and reset back to `pending`).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.orders.delete-sku-order"}},{"name":"Bulk Delete SKU Orders","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/square/orders/bulk-delete-sku-orders","host":["{{baseUrl}}"],"path":["api","square","orders","bulk-delete-sku-orders"]},"description":"Bulk-detach linked SKU sales orders. Square orders are kept and reset back to `pending`.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.orders.bulk-delete-sku-orders","body":{"mode":"raw","raw":"{\n  \"integration_instance_id\": 1,\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}}},{"name":"Bulk Delete Square Orders","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/square/orders/bulk-delete","host":["{{baseUrl}}"],"path":["api","square","orders","bulk-delete"]},"description":"Hard-delete Square orders (removes the channel-side rows). Linked SKU sales orders are NOT removed automatically — call `bulk-delete-sku-orders` first if you want both gone.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.orders.bulk-delete","body":{"mode":"raw","raw":"{\n  \"integration_instance_id\": 1,\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}}}]},{"name":"Payouts","item":[{"name":"List Square Payouts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/square/payouts","host":["{{baseUrl}}"],"path":["api","square","payouts"]},"description":"Paginated list of Square payouts (deposit / settlement batches) across integration instances. Each row carries the deposit amount, status, arrival date and mapped location so channel revenue can be reconciled against actual bank deposits.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nFilters: integration_instance_id, status, square_location_id, search (Payout ID / End-to-End ID), date_from, date_to (compared against the Square created date).\nAllowed sorts: id, square_payout_id, status, amount, arrival_date, square_created_at. Default: -square_created_at.\nPagination: page, per_page (default 25).\n\nRoute name: square.payouts.index"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/square/payouts","host":["{{baseUrl}}"],"path":["api","square","payouts"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 17,\n      \"integration_instance_id\": 1,\n      \"square_payout_id\": \"po_EXAMPLEPAYOUTID\",\n      \"square_location_id\": \"L7EXAMPLELOCID\",\n      \"location_name\": \"Downtown Store\",\n      \"status\": \"PAID\",\n      \"type\": \"BATCH\",\n      \"amount\": 48213,\n      \"currency_code\": \"USD\",\n      \"arrival_date\": \"2026-07-09\",\n      \"end_to_end_id\": \"E2E-EXAMPLE-8842\",\n      \"square_created_at\": \"2026-07-08T23:59:02+00:00\",\n      \"square_updated_at\": \"2026-07-09T02:14:55+00:00\",\n      \"entries_synced_at\": \"2026-07-09T03:00:12.000000Z\",\n      \"entries_count\": 26,\n      \"created_at\": \"2026-07-08T23:59:10.000000Z\",\n      \"updated_at\": \"2026-07-09T03:00:12.000000Z\",\n      \"integration_instance\": {\n        \"id\": 1,\n        \"name\": \"Square — Downtown\"\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 25,\n  \"total\": 62,\n  \"from\": 1,\n  \"to\": 25,\n  \"path\": \"{{base_url}}/api/square/payouts\",\n  \"first_page_url\": \"{{base_url}}/api/square/payouts?page=1\",\n  \"last_page_url\": \"{{base_url}}/api/square/payouts?page=3\",\n  \"next_page_url\": \"{{base_url}}/api/square/payouts?page=2\",\n  \"prev_page_url\": null\n}"}]},{"name":"Get Square Payout","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/square/payouts/{squarePayout}","host":["{{baseUrl}}"],"path":["api","square","payouts",":squarePayout"],"variable":[{"key":"squarePayout","value":"17","description":"Square payout ID"}]},"description":"Get a single Square payout with its integration instance, mapped location and entries count.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRoute name: square.payouts.show"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/square/payouts/{squarePayout}","host":["{{baseUrl}}"],"path":["api","square","payouts",":squarePayout"],"variable":[{"key":"squarePayout","value":"17","description":"Square payout ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 17,\n    \"integration_instance_id\": 1,\n    \"square_payout_id\": \"po_EXAMPLEPAYOUTID\",\n    \"square_location_id\": \"L7EXAMPLELOCID\",\n    \"location_name\": \"Downtown Store\",\n    \"status\": \"PAID\",\n    \"type\": \"BATCH\",\n    \"amount\": 48213,\n    \"currency_code\": \"USD\",\n    \"arrival_date\": \"2026-07-09\",\n    \"end_to_end_id\": \"E2E-EXAMPLE-8842\",\n    \"square_created_at\": \"2026-07-08T23:59:02+00:00\",\n    \"square_updated_at\": \"2026-07-09T02:14:55+00:00\",\n    \"entries_synced_at\": \"2026-07-09T03:00:12.000000Z\",\n    \"entries_count\": 26,\n    \"created_at\": \"2026-07-08T23:59:10.000000Z\",\n    \"updated_at\": \"2026-07-09T03:00:12.000000Z\",\n    \"integration_instance\": {\n      \"id\": 1,\n      \"name\": \"Square — Downtown\"\n    }\n  }\n}"}]},{"name":"List Square Payout Entries","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/square/payouts/{squarePayout}/entries","host":["{{baseUrl}}"],"path":["api","square","payouts",":squarePayout","entries"],"variable":[{"key":"squarePayout","value":"17","description":"Square payout ID"}]},"description":"Paginated list of the line-item entries that make up a Square payout (individual charges, refunds and fees). Each entry links back to the underlying Square payment or refund.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nFilters: type. Allowed sorts: id, type, effective_at, gross_amount, fee_amount, net_amount. Default: -effective_at. Pagination: page, per_page (default 25).\n\nRoute name: square.payouts.entries"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/square/payouts/{squarePayout}/entries","host":["{{baseUrl}}"],"path":["api","square","payouts",":squarePayout","entries"],"variable":[{"key":"squarePayout","value":"17","description":"Square payout ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 501,\n      \"square_payout_id\": 17,\n      \"square_payout_entry_id\": \"poe_EXAMPLEENTRYID\",\n      \"type\": \"CHARGE\",\n      \"effective_at\": \"2026-07-08T18:31:00+00:00\",\n      \"gross_amount\": 1899,\n      \"fee_amount\": 85,\n      \"net_amount\": 1814,\n      \"currency_code\": \"USD\",\n      \"square_payment_id\": \"pay_EXAMPLEPAYMENTID\",\n      \"square_refund_id\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 2,\n  \"per_page\": 25,\n  \"total\": 26,\n  \"from\": 1,\n  \"to\": 25,\n  \"path\": \"{{base_url}}/api/square/payouts/17/entries\",\n  \"first_page_url\": \"{{base_url}}/api/square/payouts/17/entries?page=1\",\n  \"last_page_url\": \"{{base_url}}/api/square/payouts/17/entries?page=2\",\n  \"next_page_url\": \"{{base_url}}/api/square/payouts/17/entries?page=2\",\n  \"prev_page_url\": null\n}"}]}]},{"name":"Connection","item":[{"name":"Test Connection","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/test-connection","host":["{{baseUrl}}"],"path":["api","magento2","test-connection"]},"description":"Validates the supplied Magento 2 credentials by calling the REST API. Returns the store views and MSI (Multi-Source Inventory) sources the merchant has access to — used by the UI to preview before persisting.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nNothing is persisted by this endpoint.\n\n**Body parameters:**\n- `base_url` (required, URL): The storefront base URL — e.g. `https://shop.example.com`. Trailing slash is trimmed server-side.\n- `access_token` (required, string, min 8 chars, max 255): The integration access token from Magento Admin → System → Integrations.\n\n**Error responses:**\n- `422` — validation failure OR Magento rejected the credentials (`success: false` with a human-readable `message`).\n- `401` — caller is not authenticated to SKU.io.","body":{"mode":"raw","raw":"{\n  \"base_url\": \"https://shop.example.com\",\n  \"access_token\": \"magento_integration_access_token_xxxxxxxx\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/test-connection","host":["{{baseUrl}}"],"path":["api","magento2","test-connection"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"data\": {\n    \"store_views\": [\n      {\n        \"id\": 0,\n        \"code\": \"admin\",\n        \"name\": \"Admin\",\n        \"website_id\": 0,\n        \"store_group_id\": 0\n      },\n      {\n        \"id\": 1,\n        \"code\": \"default\",\n        \"name\": \"Default Store View\",\n        \"website_id\": 1,\n        \"store_group_id\": 1\n      },\n      {\n        \"id\": 2,\n        \"code\": \"en_us\",\n        \"name\": \"United States\",\n        \"website_id\": 1,\n        \"store_group_id\": 1\n      }\n    ],\n    \"inventory_sources\": [\n      {\n        \"source_code\": \"default\",\n        \"name\": \"Default Source\",\n        \"enabled\": true,\n        \"country_id\": \"US\"\n      },\n      {\n        \"source_code\": \"us_warehouse\",\n        \"name\": \"US Warehouse\",\n        \"enabled\": true,\n        \"country_id\": \"US\"\n      }\n    ]\n  }\n}"}]},{"name":"Install","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/install","host":["{{baseUrl}}"],"path":["api","magento2","install"]},"description":"Provisions a new Magento 2 IntegrationInstance after re-validating the supplied credentials server-side. The probe runs again here — clients cannot skip validation by calling `/install` directly.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nThe returned instance includes the access token in masked form only (last 4 chars). The full token is encrypted at rest and never echoed back.\n\n**Body parameters:**\n- `name` (required, string, max 255): Human-readable instance name. Must be unique within your tenant.\n- `base_url` (required, URL): Magento storefront base URL.\n- `access_token` (required, string, min 8 chars): The integration access token from Magento Admin.\n- `country` (optional, nullable, ISO 3166-1 alpha-2): Two-letter country code, e.g. `US`.\n\n**Behavior:**\n- Server probes Magento for store views + MSI sources and persists the discovery into `integration_settings.discovery` so downstream phases can read them without re-fetching.\n- Default `integration_settings` are seeded: orders auto-download disabled, email_customers enabled.\n\n**Error responses:**\n- `422` — name already taken OR Magento rejected the credentials.\n- `401` — caller is not authenticated to SKU.io.","body":{"mode":"raw","raw":"{\n  \"name\": \"Acme — US Storefront\",\n  \"base_url\": \"https://shop.example.com\",\n  \"access_token\": \"magento_integration_access_token_xxxxxxxx\",\n  \"country\": \"US\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/install","host":["{{baseUrl}}"],"path":["api","magento2","install"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"data\": {\n    \"id\": 42,\n    \"integration_id\": 17,\n    \"name\": \"Acme — US Storefront\",\n    \"country\": \"US\",\n    \"sync_status\": null,\n    \"connection_status\": null,\n    \"is_automatic_sync_enabled\": false,\n    \"connection_settings\": {\n      \"base_url\": \"https://shop.example.com\",\n      \"access_token_masked\": \"••••••••••••••••••••xxxx\"\n    },\n    \"integration_settings\": {\n      \"orders\": {\n        \"download\": {\n          \"last_updated_after\": null,\n          \"status\": [],\n          \"auto_download\": {\n            \"enabled\": false,\n            \"frequency_mins\": 15\n          }\n        }\n      },\n      \"inventory\": {\n        \"master_of_stock\": null\n      },\n      \"communications\": {\n        \"email_customers\": true\n      },\n      \"discovery\": {\n        \"store_views\": [\n          {\n            \"id\": 1,\n            \"code\": \"default\",\n            \"name\": \"Default Store View\"\n          }\n        ],\n        \"inventory_sources\": [\n          {\n            \"source_code\": \"default\",\n            \"name\": \"Default Source\",\n            \"enabled\": true\n          }\n        ]\n      }\n    },\n    \"created_at\": \"2026-06-10T12:34:56+00:00\",\n    \"updated_at\": \"2026-06-10T12:34:56+00:00\"\n  }\n}"}]},{"name":"Test Instance Connection","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/test-connection","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","test-connection"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Probe an EXISTING instance's STORED credentials against the Magento 2 REST API. Backs the live Connection Status chip on the settings Connection tab — the frontend never sees the raw access token, so the server re-uses what's persisted in connection_settings. No request body.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nReturns 200 with `data.connected = true` when the token authenticates, or 422 with `data.connected = false` and an actionable `message` when Magento rejects it (invalid/expired token, missing resource scopes, or unreachable host).\n\n**Authentication:** Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/test-connection","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","test-connection"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"data\": {\n    \"connected\": true\n  }\n}"}]}]},{"name":"Magento 2","item":[{"name":"List Sync Runs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/webhooks","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","webhooks"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]},"description":"Paginated list of polling sync runs for the instance. Magento 2 has no native webhook system — each row is one execution of a tracked sync job (orders, SKU order creation, products, customers, transactions, inventory, fulfillments), sourced from tracked_job_logs.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token.\n\nFilters: filter[search] (run ID exact / job name partial), filter[status] (pending, processing, completed, failed, cancelled), filter[topic] (orders, sku_orders, products, customers, transactions, inventory, fulfillments).\n\nAllowed sorts: id, job_name, status, progress_percentage, attempted_at, completed_at. Default: -id."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/webhooks","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","webhooks"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 42,\n      \"job_name\": \"Refresh Magento 2 Orders: Acme Magento Store\",\n      \"topic\": \"orders\",\n      \"status\": \"completed\",\n      \"progress_percentage\": \"100.00\",\n      \"total_items\": 150,\n      \"processed_items\": 150,\n      \"duration_seconds\": 12.34,\n      \"error\": null,\n      \"attempted_at\": \"2026-07-06T14:05:01+00:00\",\n      \"completed_at\": \"2026-07-06T14:05:13+00:00\",\n      \"created_at\": \"2026-07-06T14:05:00+00:00\",\n      \"updated_at\": \"2026-07-06T14:05:13+00:00\",\n      \"user_id\": 1\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1,\n  \"first_page_url\": \"…\",\n  \"last_page_url\": \"…\",\n  \"next_page_url\": null,\n  \"prev_page_url\": null,\n  \"path\": \"…\",\n  \"links\": []\n}"}]},{"name":"Run Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/webhooks/summary","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","webhooks","summary"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]},"description":"Per-status run counts plus the most recent run — feeds the KPI cards on the Webhooks page (Pending / Processing / Completed / Failed are clickable filters).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/webhooks/summary","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","webhooks","summary"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_runs\": 128,\n    \"completed\": 120,\n    \"failed\": 3,\n    \"processing\": 1,\n    \"pending\": 2,\n    \"cancelled\": 2,\n    \"latest_run\": {\n      \"id\": 42,\n      \"job_name\": \"Refresh Magento 2 Orders: Acme Magento Store\",\n      \"topic\": \"orders\",\n      \"status\": \"completed\",\n      \"attempted_at\": \"2026-07-06T14:05:01+00:00\",\n      \"completed_at\": \"2026-07-06T14:05:13+00:00\"\n    }\n  }\n}"}]},{"name":"Polling Schedule","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/webhooks/schedule","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","webhooks","schedule"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]},"description":"The polling schedule that replaces webhook subscriptions for this channel — one row per sync type with its cron cadence (mirrors routes/schedule/magento2.php) and the last run of that type. auto_sync_enabled reflects the instance's automatic-sync flag; scheduled runs are skipped when it is off.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/webhooks/schedule","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","webhooks","schedule"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"auto_sync_enabled\": true,\n    \"sync_types\": [\n      {\n        \"key\": \"orders\",\n        \"name\": \"Orders Refresh\",\n        \"command\": \"magento2:orders:refresh\",\n        \"schedule\": \"Every 5 minutes\",\n        \"description\": \"Pulls new and updated orders from Magento 2 (cursor: latest synced order).\",\n        \"last_run\": {\n          \"id\": 42,\n          \"job_name\": \"Refresh Magento 2 Orders: Acme Magento Store\",\n          \"topic\": \"orders\",\n          \"status\": \"completed\",\n          \"attempted_at\": \"2026-07-06T14:05:01+00:00\",\n          \"completed_at\": \"2026-07-06T14:05:13+00:00\"\n        }\n      },\n      {\n        \"key\": \"sku_orders\",\n        \"name\": \"SKU Order Creation\",\n        \"command\": \"magento2:orders:create-sku-orders\",\n        \"schedule\": \"Every 5 minutes\",\n        \"description\": \"Converts eligible Magento 2 orders into SKU.io sales orders.\",\n        \"last_run\": null\n      },\n      {\n        \"key\": \"products\",\n        \"name\": \"Products Refresh\",\n        \"command\": \"magento2:products:refresh\",\n        \"schedule\": \"Every hour\",\n        \"description\": \"Pulls new and updated products from the Magento 2 catalog.\",\n        \"last_run\": null\n      },\n      {\n        \"key\": \"customers\",\n        \"name\": \"Customers Refresh\",\n        \"command\": \"magento2:customers:refresh\",\n        \"schedule\": \"Daily\",\n        \"description\": \"Pulls customers and their addresses from Magento 2.\",\n        \"last_run\": null\n      },\n      {\n        \"key\": \"transactions\",\n        \"name\": \"Transactions Refresh\",\n        \"command\": \"magento2:transactions:refresh\",\n        \"schedule\": \"Every hour\",\n        \"description\": \"Pulls invoices and credit memos from Magento 2.\",\n        \"last_run\": null\n      },\n      {\n        \"key\": \"inventory\",\n        \"name\": \"Inventory Push\",\n        \"command\": \"magento2:inventory:push\",\n        \"schedule\": \"Every 15 minutes (production only)\",\n        \"description\": \"Pushes SKU.io on-hand quantities to enabled Magento 2 MSI sources when SKU.io is master of stock.\",\n        \"last_run\": null\n      }\n    ]\n  }\n}"}]},{"name":"Show Sync Run","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/webhooks/{runId}","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","webhooks",":runId"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"},{"key":"runId","value":"42","description":"Tracked run ID"}]},"description":"Full detail for one sync run — list fields plus the progress message log, structured results payload, job class and acting user. Returns 404 when the run does not belong to this instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/webhooks/{runId}","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","webhooks",":runId"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"},{"key":"runId","value":"42","description":"Tracked run ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"job_name\": \"Refresh Magento 2 Orders: Acme Magento Store\",\n    \"topic\": \"orders\",\n    \"status\": \"completed\",\n    \"progress_percentage\": \"100.00\",\n    \"total_items\": 150,\n    \"processed_items\": 150,\n    \"duration_seconds\": 12.34,\n    \"error\": null,\n    \"attempted_at\": \"2026-07-06T14:05:01+00:00\",\n    \"completed_at\": \"2026-07-06T14:05:13+00:00\",\n    \"created_at\": \"2026-07-06T14:05:00+00:00\",\n    \"updated_at\": \"2026-07-06T14:05:13+00:00\",\n    \"user_id\": 1,\n    \"job_type\": \"Modules\\\\Magento2\\\\Jobs\\\\RefreshMagento2OrdersTrackedJob\",\n    \"messages\": \"Determining cursor...\\nSyncing Magento 2 orders updated since 2026-07-06 13:00:00...\\nSynced 150 order(s) in 3 page(s).\",\n    \"results\": {\n      \"mode\": \"since_latest\",\n      \"cursor\": \"2026-07-06 13:00:00\",\n      \"synced_orders\": 150,\n      \"pages_walked\": 3\n    },\n    \"user_name\": \"Jane Admin\"\n  }\n}"}]},{"name":"Inventory Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/inventory/summary","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","inventory","summary"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]},"description":"KPI counts for the inventory page header. `in_sync` / `drift` compare the cached Magento quantity against the CACHED calculated SKU quantity (the value Sync pushes). `needs_recalculation` counts mapped products whose cached quantity is stale or missing — it gates the Sync button (\"Recache SKU\" first).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/inventory/summary","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","inventory","summary"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_items\": 40,\n    \"in_sync\": 23,\n    \"drift\": 3,\n    \"unmapped\": 14,\n    \"needs_recalculation\": 0,\n    \"last_compared_at\": \"2026-07-08T08:52:59+00:00\",\n    \"last_pushed_at\": \"2026-07-08T05:54:18+00:00\"\n  }\n}"}]},{"name":"List Inventory Items","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/inventory/items","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","inventory","items"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]},"description":"Paginated inventory reconciliation grid — one entry per Magento product, with one `sources[]` row per MSI source. `sku_quantity` is the CACHED calculated SKU.io quantity (on-hand across the selected warehouses with modification rules applied) — exactly what \"Sync to Magento\" pushes. `is_sku_recalculation_needed` marks a stale cache; run Recache to refresh.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nFilters: `filter[search]` (SKU/name partial), `filter[sku]` (exact), `filter[source_code]`, `filter[is_mapped]` (true/false), `filter[status]` (in_sync | drift | unmapped | pending), `filter[sku_quantity]` (numeric, advanced operators supported). Allowed sorts: sku, name, updated_at, id (prefix with - for DESC). Default sort: sku.\n\nAuthentication: Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/inventory/items","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","inventory","items"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 15,\n      \"magento2_product_id\": 15,\n      \"sku\": \"APRD-BELT-003\",\n      \"name\": \"Leather Belt Brown\",\n      \"sku_product_id\": 27,\n      \"is_mapped\": true,\n      \"sources\": [\n        {\n          \"id\": \"p15-sdefault\",\n          \"source_code\": \"default\",\n          \"source_name\": \"Default Source\",\n          \"magento_quantity\": 120,\n          \"magento_status\": 1,\n          \"sku_warehouse_id\": 1,\n          \"sku_warehouse_name\": \"Default Warehouse\",\n          \"sku_quantity\": 115,\n          \"is_sku_recalculation_needed\": false,\n          \"sku_quantity_updated_at\": \"2026-07-08T10:15:00+00:00\",\n          \"diff\": -5,\n          \"status\": \"drift\",\n          \"last_fetched_at\": \"2026-07-08T08:52:59+00:00\",\n          \"last_pushed_at\": \"2026-07-08T05:54:18+00:00\"\n        }\n      ]\n    }\n  ],\n  \"first_page_url\": \"https://example.test/api/magento2/integration-instances/1/inventory/items?page=1\",\n  \"from\": 1,\n  \"last_page\": 2,\n  \"last_page_url\": \"https://example.test/api/magento2/integration-instances/1/inventory/items?page=2\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://example.test/api/magento2/integration-instances/1/inventory/items\",\n  \"per_page\": 25,\n  \"prev_page_url\": null,\n  \"to\": 25,\n  \"total\": 40\n}"}]},{"name":"Fetch From Magento (Compare)","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/inventory/compare","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","inventory","compare"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]},"description":"Queues the tracked \"Compare Magento 2 Inventory\" job: downloads the current MSI source-item quantities from Magento (all pages, all sources) into the local cache for comparison with the cached SKU.io quantities. This is the \"Fetch from Magento\" button.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/inventory/compare","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","inventory","compare"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Compare Magento 2 inventory job has been queued\",\n  \"data\": {\n    \"tracked_job_log_id\": 42\n  }\n}"}]},{"name":"Recache SKU Quantities","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/inventory/recache","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","inventory","recache"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]},"description":"Queues the tracked \"Recache Magento 2 Inventory\" job: recalculates the cached SKU.io quantity for every linked product (on-hand summed across the globally-selected warehouses, inventory modification rules applied, floored at 0) and clears `is_sku_recalculation_needed`. This is the \"Recache SKU\" button — WooCommerce parity. The cache is also refreshed automatically for stale rows before every push.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/inventory/recache","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","inventory","recache"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Inventory recache job has been queued\",\n  \"data\": {\n    \"tracked_job_log_id\": 42\n  }\n}"}]},{"name":"Sync To Magento (Push)","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/inventory/push-to-magento","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","inventory","push-to-magento"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]},"description":"Queues the tracked \"Push Inventory to Magento 2\" job: pushes the CACHED calculated SKU.io quantities to every enabled MSI source (same value across sources — the WooCommerce single-value model). Stale cache rows are recached first. No-op unless Master of Stock is SKU.io and warehouses are selected in the inventory settings. This is the \"Sync to Magento\" button.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/inventory/push-to-magento","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","inventory","push-to-magento"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Push to Magento 2 job has been queued\",\n  \"data\": {\n    \"tracked_job_log_id\": 42\n  }\n}"}]},{"name":"Pull From Magento","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/inventory/pull-from-magento","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","inventory","pull-from-magento"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]},"description":"Queues the tracked \"Pull Inventory from Magento 2\" job: overwrites SKU.io on-hand from Magento 2's cached MSI levels for every mapped source. This is the \"Pull from Magento\" button (no WooCommerce equivalent).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{instanceId}/inventory/pull-from-magento","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":instanceId","inventory","pull-from-magento"],"variable":[{"key":"instanceId","value":"1","description":"Magento 2 integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Pull from Magento 2 job has been queued\",\n  \"data\": {\n    \"tracked_job_log_id\": 42\n  }\n}"}]}]},{"name":"Odoo 3PL","item":[{"name":"List Shipping Method Mappings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods"],"variable":[{"key":"integration_instance","value":"3","description":"Integration instance ID"}]},"description":"Returns the mapping editor payload for this integration.\n\n`shipping_methods` is the row set: one entry per non-archived shipping method, each with the Odoo 3PL carrier it dispatches through (`mapped_carrier`, null when unmapped) and `will_send` — the literal value the fulfillment submit will carry.\n\n`carriers` is the synced Odoo 3PL catalog to map onto, each with `mapped_shipping_method_count` (how many shipping methods route through it).\n\nSeveral shipping methods may share one Odoo 3PL carrier — a Odoo 3PL catalog is normally coarser than a shipping-method list, so this is expected, not a conflict.\n\nAuthentication: requires a Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods"],"variable":[{"key":"integration_instance","value":"3","description":"Integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"shipping_methods\": [\n      {\n        \"shipping_method_id\": 12,\n        \"name\": \"Ground\",\n        \"full_name\": \"FedEx Ground\",\n        \"type\": \"domestic\",\n        \"mapped_carrier\": {\n          \"id\": 7,\n          \"odoo_3pl_carrier_id\": 20,\n          \"name\": \"Fedex US\",\n          \"delivery_type\": \"fedex_rest\"\n        },\n        \"will_send\": \"Fedex US\",\n        \"is_mapped\": true\n      },\n      {\n        \"shipping_method_id\": 13,\n        \"name\": \"2Day\",\n        \"full_name\": \"FedEx 2Day\",\n        \"type\": \"domestic\",\n        \"mapped_carrier\": {\n          \"id\": 7,\n          \"odoo_3pl_carrier_id\": 20,\n          \"name\": \"Fedex US\",\n          \"delivery_type\": \"fedex_rest\"\n        },\n        \"will_send\": \"Fedex US\",\n        \"is_mapped\": true\n      },\n      {\n        \"shipping_method_id\": 18,\n        \"name\": \"Express\",\n        \"full_name\": \"DHL Express\",\n        \"type\": \"international\",\n        \"mapped_carrier\": null,\n        \"will_send\": \"DHL Express\",\n        \"is_mapped\": false\n      }\n    ],\n    \"carriers\": [\n      {\n        \"id\": 7,\n        \"odoo_3pl_carrier_id\": 20,\n        \"name\": \"Fedex US\",\n        \"delivery_type\": \"fedex_rest\",\n        \"mapped_shipping_method_count\": 2\n      },\n      {\n        \"id\": 8,\n        \"odoo_3pl_carrier_id\": 21,\n        \"name\": \"ups ground\",\n        \"delivery_type\": \"ups_rest\",\n        \"mapped_shipping_method_count\": 0\n      }\n    ],\n    \"counts\": {\n      \"total\": 3,\n      \"mapped\": 2,\n      \"unmapped\": 1\n    },\n    \"last_synced_at\": \"2026-08-19T10:11:16.000000Z\"\n  }\n}"}]},{"name":"Sync Shipping Methods","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/sync","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","sync"],"variable":[{"key":"integration_instance","value":"3","description":"Integration instance ID"}]},"description":"Refresh the carrier catalog from Odoo 3PL, then auto-link any unmapped carriers to SKU.io shipping methods whose name matches. Returns how many carriers were fetched and how many were auto-matched. Requires a Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/sync","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","sync"],"variable":[{"key":"integration_instance","value":"3","description":"Integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Synced 3 shipping method(s); 1 auto-matched.\",\n  \"data\": {\n    \"carriers_fetched\": 3,\n    \"auto_matched\": 1\n  }\n}"}]},{"name":"Export Shipping Method Mappings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/export","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","export"],"variable":[{"key":"integration_instance","value":"3","description":"Integration instance ID"}]},"description":"Downloads the current mappings as a re-importable CSV, one row per non-archived shipping method.\n\nColumns: `shipping_method_id,shipping_method_name,shipping_method_type,odoo_3pl_carrier_id,odoo_3pl_carrier_name`\n\nUnmapped shipping methods are included with blank provider columns, so the file doubles as the worklist. Re-importing a row with both provider columns blank clears that mapping.\n\nReturns a `text/csv` file download.\n\nAuthentication: requires a Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/export","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","export"],"variable":[{"key":"integration_instance","value":"3","description":"Integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"odoo_3pl_carrier_id,name,delivery_type,shipping_method_id,shipping_method_name\r\n5,Fedex Canada Ground,fedex_rest,,\r\n10,ups ground,ups_rest,5,UPS Ground\r\n"}]},{"name":"Import Shipping Method Mappings","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/import","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","import"],"variable":[{"key":"integration_instance","value":"3","description":"Integration instance ID"}]},"description":"Applies mappings from a CSV keyed on the shipping method.\n\nEach row identifies a shipping method by `shipping_method_id` (authoritative) or `shipping_method_name`, and a Odoo 3PL carrier by `odoo_3pl_carrier_id` or `odoo_3pl_carrier_name`. Leaving both provider columns blank clears that mapping.\n\nA `shipping_method_id` appearing on two rows is reported as an error for the second row rather than silently taking the last value — one shipping method cannot have two carriers.\n\nUpload as `multipart/form-data` with a `file` field (CSV, max 10MB). Returns counts of created, updated and removed mappings plus any per-row errors.\n\nAuthentication: requires a Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/import","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","import"],"variable":[{"key":"integration_instance","value":"3","description":"Integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"created\": 2,\n    \"updated\": 1,\n    \"deleted\": 1,\n    \"errors\": [\n      \"Line 5: duplicate shipping_method_id '12' (already set on line 3)\"\n    ]\n  },\n  \"message\": \"Import processed\"\n}"}]},{"name":"Auto-Match Unmapped Shipping Methods","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/auto-match","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","auto-match"],"variable":[{"key":"integration_instance","value":"3","description":"Integration instance ID"}]},"description":"Links still-unmapped shipping methods to the Odoo 3PL carrier whose name matches, ignoring case, spacing and punctuation. Never overwrites an existing mapping, so it is safe to re-run after a sync.\n\nTwo shipping methods matching the same Odoo 3PL carrier is a valid outcome — several shipping methods may share one Odoo 3PL carrier — a Odoo 3PL catalog is normally coarser than a shipping-method list, so this is expected, not a conflict.\n\nReturns the number of shipping methods newly linked.\n\nAuthentication: requires a Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/auto-match","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","auto-match"],"variable":[{"key":"integration_instance","value":"3","description":"Integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"matched_count\": 4\n  },\n  \"message\": \"4 shipping method(s) auto-matched\"\n}"}]},{"name":"Bulk Map Shipping Methods","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/mappings/bulk","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","mappings","bulk"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"}]},"description":"Points many shipping methods at one Odoo 3PL carrier in a single call, or clears them all.\n\nThis is the primary way to handle a coarse provider catalog: map every FedEx service you sell to the one Odoo 3PL carrier that carries them.\n\n`shipping_method_ids` — required, 1 to 500 unique shipping method IDs.\n`odoo_3pl_shipping_method_id` — required (nullable); the catalog entry to map them all to, or `null` to clear.\n\nReturns how many mappings were written or cleared, plus refreshed mapped/unmapped counts.\n\nAuthentication: requires a Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.","body":{"mode":"raw","raw":"{\n  \"shipping_method_ids\": [\n    12,\n    13,\n    14\n  ],\n  \"odoo_3pl_shipping_method_id\": 7\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/mappings/bulk","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","mappings","bulk"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"saved_count\": 3,\n    \"cleared_count\": 0,\n    \"counts\": {\n      \"total\": 18,\n      \"mapped\": 14,\n      \"unmapped\": 4\n    }\n  },\n  \"message\": \"3 mapping(s) saved\"\n}"}]},{"name":"Map Shipping Method","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/mappings/{shippingMethod}","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","mappings",":shippingMethod"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"},{"key":"shippingMethod","value":"12","description":"Shipping method ID (the mapping is keyed on this)"}]},"description":"Sets or clears the Odoo 3PL carrier that one shipping method dispatches through.\n\nThe path parameter is the **shipping method** ID — the mapping is keyed on it, so a shipping method resolves to at most one Odoo 3PL carrier. Send `odoo_3pl_shipping_method_id: null` to clear the mapping.\n\nSeveral shipping methods may share one Odoo 3PL carrier — a Odoo 3PL catalog is normally coarser than a shipping-method list, so this is expected, not a conflict.\n\n`odoo_3pl_shipping_method_id` is the ID of a synced catalog entry as returned by List Shipping Method Mappings.\n\nAuthentication: requires a Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.","body":{"mode":"raw","raw":"{\n  \"odoo_3pl_shipping_method_id\": 7\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/mappings/{shippingMethod}","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","mappings",":shippingMethod"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"},{"key":"shippingMethod","value":"12","description":"Shipping method ID (the mapping is keyed on this)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 41,\n    \"integration_instance_id\": 5,\n    \"shipping_method_id\": 12,\n    \"odoo_3pl_shipping_method_id\": 7,\n    \"carrier\": {\n      \"id\": 7,\n      \"odoo_3pl_carrier_id\": 20,\n      \"name\": \"Fedex US\",\n      \"delivery_type\": \"fedex_rest\"\n    },\n    \"created_at\": \"2026-08-19T10:12:00.000000Z\",\n    \"updated_at\": \"2026-08-19T10:12:00.000000Z\"\n  },\n  \"message\": \"Shipping method mapped.\"\n}"}]},{"name":"Get Shipping Routing","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/routing","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","routing"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"}]},"description":"Shipping routing decides which Odoo 3PL carrier a fulfillment order dispatches with. Four layers resolve in order, first match wins:\n\n1. **Method override** — an explicit choice pinned to one shipping method.\n2. **Carrier family** — the default for a whole shipping-carrier family, so a catalog of 40+ methods is routable in a handful of decisions.\n3. **Text rules** — ordered rules matched against the free-text shipping method an order carries when it has no structured shipping method. This is the only layer configurable for text that has never been seen.\n4. **Fallback** — the instance-wide catch-all.\n\nEvery response returns the WHOLE refreshed configuration, because the layers interact: a family default changes what its un-overridden methods send, and a new rule can claim text the fallback used to catch. Each row reports `resolved_by` — the layer that decided it.\n\nAuthentication: requires a Bearer token.\n\nReturns the carrier families with their members, the ordered text rules, the fallback, the synced catalog, and the free text actually observed on recent shipments (each resolved through the live chain so you can see which layer handled it).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/routing","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","routing"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"families\": [\n      {\n        \"shipping_carrier_id\": 3,\n        \"name\": \"FedEx\",\n        \"default_target_id\": 7,\n        \"default_target_name\": \"Fedex US\",\n        \"default_suppress\": false,\n        \"method_count\": 18,\n        \"volume\": 412,\n        \"methods\": [\n          {\n            \"shipping_method_id\": 12,\n            \"name\": \"Ground\",\n            \"full_name\": \"FedEx Ground\",\n            \"type\": \"domestic\",\n            \"volume\": 300,\n            \"override_target_id\": null,\n            \"override_suppress\": false,\n            \"has_override\": false,\n            \"will_send\": \"Fedex US\",\n            \"resolved_by\": \"carrier_family\",\n            \"resolved_detail\": \"FedEx family → Fedex US\",\n            \"suppressed\": false,\n            \"is_configured\": true\n          }\n        ]\n      }\n    ],\n    \"text_rules\": [\n      {\n        \"id\": 1,\n        \"position\": 1,\n        \"match_type\": \"contains\",\n        \"match_value\": \"Canada\",\n        \"target_id\": 8,\n        \"target_name\": \"Fedex Canada Ground\",\n        \"suppress\": false,\n        \"describe\": \"contains \\\"Canada\\\"\"\n      }\n    ],\n    \"observed_text\": [\n      {\n        \"text\": \"Free Shipping\",\n        \"volume\": 494,\n        \"will_send\": \"Fedex US\",\n        \"resolved_by\": \"fallback\",\n        \"resolved_detail\": \"fallback → Fedex US\",\n        \"suppressed\": false,\n        \"is_configured\": true\n      }\n    ],\n    \"fallback\": {\n      \"mode\": \"carrier\",\n      \"target_id\": 7,\n      \"target_name\": \"Fedex US\"\n    },\n    \"carriers\": [\n      {\n        \"id\": 7,\n        \"name\": \"Fedex US\"\n      }\n    ],\n    \"last_synced_at\": \"2026-08-20T10:11:16.000000Z\"\n  }\n}"}]},{"name":"Set Carrier Family Default","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/routing/families/{shippingCarrier}","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","routing","families",":shippingCarrier"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"},{"key":"shippingCarrier","value":"3","description":"SKU shipping carrier ID"}]},"description":"Layer 2 — set the default Odoo 3PL carrier for a whole shipping-carrier family. This is what makes a large catalog routable before any order exists.\n\n`odoo_3pl_shipping_method_id` — the catalog entry to route the family to; `null` clears the default.\n`suppress` — the family deliberately sends no carrier (it is not a 3PL shipment); cannot be combined with a target.\n`apply_to_methods` — also clear per-method overrides inside the family, so they stop shadowing the new default.\n\nAuthentication: requires a Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRequires the `integrations.update` permission.","body":{"mode":"raw","raw":"{\n  \"odoo_3pl_shipping_method_id\": 7,\n  \"suppress\": false,\n  \"apply_to_methods\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/routing/families/{shippingCarrier}","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","routing","families",":shippingCarrier"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"},{"key":"shippingCarrier","value":"3","description":"SKU shipping carrier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"families\": [\n      {\n        \"shipping_carrier_id\": 3,\n        \"name\": \"FedEx\",\n        \"default_target_id\": 7,\n        \"default_target_name\": \"Fedex US\",\n        \"default_suppress\": false,\n        \"method_count\": 18,\n        \"volume\": 412,\n        \"methods\": [\n          {\n            \"shipping_method_id\": 12,\n            \"name\": \"Ground\",\n            \"full_name\": \"FedEx Ground\",\n            \"type\": \"domestic\",\n            \"volume\": 300,\n            \"override_target_id\": null,\n            \"override_suppress\": false,\n            \"has_override\": false,\n            \"will_send\": \"Fedex US\",\n            \"resolved_by\": \"carrier_family\",\n            \"resolved_detail\": \"FedEx family → Fedex US\",\n            \"suppressed\": false,\n            \"is_configured\": true\n          }\n        ]\n      }\n    ],\n    \"text_rules\": [\n      {\n        \"id\": 1,\n        \"position\": 1,\n        \"match_type\": \"contains\",\n        \"match_value\": \"Canada\",\n        \"target_id\": 8,\n        \"target_name\": \"Fedex Canada Ground\",\n        \"suppress\": false,\n        \"describe\": \"contains \\\"Canada\\\"\"\n      }\n    ],\n    \"observed_text\": [\n      {\n        \"text\": \"Free Shipping\",\n        \"volume\": 494,\n        \"will_send\": \"Fedex US\",\n        \"resolved_by\": \"fallback\",\n        \"resolved_detail\": \"fallback → Fedex US\",\n        \"suppressed\": false,\n        \"is_configured\": true\n      }\n    ],\n    \"fallback\": {\n      \"mode\": \"carrier\",\n      \"target_id\": 7,\n      \"target_name\": \"Fedex US\"\n    },\n    \"carriers\": [\n      {\n        \"id\": 7,\n        \"name\": \"Fedex US\"\n      }\n    ],\n    \"last_synced_at\": \"2026-08-20T10:11:16.000000Z\"\n  }\n}"}]},{"name":"Set Shipping Method Override","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/routing/methods/{shippingMethod}","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","routing","methods",":shippingMethod"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"},{"key":"shippingMethod","value":"12","description":"SKU shipping method ID"}]},"description":"Layer 1 — pin one shipping method to a specific carrier, overriding its family. Send `odoo_3pl_shipping_method_id: null` with `suppress: false` to remove the override and inherit the family again.\n\nAuthentication: requires a Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRequires the `integrations.update` permission.","body":{"mode":"raw","raw":"{\n  \"odoo_3pl_shipping_method_id\": 7,\n  \"suppress\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/routing/methods/{shippingMethod}","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","routing","methods",":shippingMethod"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"},{"key":"shippingMethod","value":"12","description":"SKU shipping method ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"families\": [\n      {\n        \"shipping_carrier_id\": 3,\n        \"name\": \"FedEx\",\n        \"default_target_id\": 7,\n        \"default_target_name\": \"Fedex US\",\n        \"default_suppress\": false,\n        \"method_count\": 18,\n        \"volume\": 412,\n        \"methods\": [\n          {\n            \"shipping_method_id\": 12,\n            \"name\": \"Ground\",\n            \"full_name\": \"FedEx Ground\",\n            \"type\": \"domestic\",\n            \"volume\": 300,\n            \"override_target_id\": null,\n            \"override_suppress\": false,\n            \"has_override\": false,\n            \"will_send\": \"Fedex US\",\n            \"resolved_by\": \"carrier_family\",\n            \"resolved_detail\": \"FedEx family → Fedex US\",\n            \"suppressed\": false,\n            \"is_configured\": true\n          }\n        ]\n      }\n    ],\n    \"text_rules\": [\n      {\n        \"id\": 1,\n        \"position\": 1,\n        \"match_type\": \"contains\",\n        \"match_value\": \"Canada\",\n        \"target_id\": 8,\n        \"target_name\": \"Fedex Canada Ground\",\n        \"suppress\": false,\n        \"describe\": \"contains \\\"Canada\\\"\"\n      }\n    ],\n    \"observed_text\": [\n      {\n        \"text\": \"Free Shipping\",\n        \"volume\": 494,\n        \"will_send\": \"Fedex US\",\n        \"resolved_by\": \"fallback\",\n        \"resolved_detail\": \"fallback → Fedex US\",\n        \"suppressed\": false,\n        \"is_configured\": true\n      }\n    ],\n    \"fallback\": {\n      \"mode\": \"carrier\",\n      \"target_id\": 7,\n      \"target_name\": \"Fedex US\"\n    },\n    \"carriers\": [\n      {\n        \"id\": 7,\n        \"name\": \"Fedex US\"\n      }\n    ],\n    \"last_synced_at\": \"2026-08-20T10:11:16.000000Z\"\n  }\n}"}]},{"name":"Add Text Rule","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/routing/rules","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","routing","rules"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"}]},"description":"Layer 3 — add a rule matched against the free-text shipping method on an order.\n\n`match_type` — `contains`, `starts_with` or `exact`. Matching ignores case, spacing and punctuation.\n`match_value` — the text to match.\n`odoo_3pl_shipping_method_id` — required unless `suppress` is true.\n`suppress` — the matched text is not a carrier, so send nothing. Defaults to `false` when omitted, so a rule that names neither is rejected.\n\nNew rules are appended last. Rules are first-match-wins, so order matters.\n\nAuthentication: requires a Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRequires the `integrations.update` permission.","body":{"mode":"raw","raw":"{\n  \"match_type\": \"contains\",\n  \"match_value\": \"Canada\",\n  \"odoo_3pl_shipping_method_id\": 8,\n  \"suppress\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/routing/rules","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","routing","rules"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"families\": [\n      {\n        \"shipping_carrier_id\": 3,\n        \"name\": \"FedEx\",\n        \"default_target_id\": 7,\n        \"default_target_name\": \"Fedex US\",\n        \"default_suppress\": false,\n        \"method_count\": 18,\n        \"volume\": 412,\n        \"methods\": [\n          {\n            \"shipping_method_id\": 12,\n            \"name\": \"Ground\",\n            \"full_name\": \"FedEx Ground\",\n            \"type\": \"domestic\",\n            \"volume\": 300,\n            \"override_target_id\": null,\n            \"override_suppress\": false,\n            \"has_override\": false,\n            \"will_send\": \"Fedex US\",\n            \"resolved_by\": \"carrier_family\",\n            \"resolved_detail\": \"FedEx family → Fedex US\",\n            \"suppressed\": false,\n            \"is_configured\": true\n          }\n        ]\n      }\n    ],\n    \"text_rules\": [\n      {\n        \"id\": 1,\n        \"position\": 1,\n        \"match_type\": \"contains\",\n        \"match_value\": \"Canada\",\n        \"target_id\": 8,\n        \"target_name\": \"Fedex Canada Ground\",\n        \"suppress\": false,\n        \"describe\": \"contains \\\"Canada\\\"\"\n      }\n    ],\n    \"observed_text\": [\n      {\n        \"text\": \"Free Shipping\",\n        \"volume\": 494,\n        \"will_send\": \"Fedex US\",\n        \"resolved_by\": \"fallback\",\n        \"resolved_detail\": \"fallback → Fedex US\",\n        \"suppressed\": false,\n        \"is_configured\": true\n      }\n    ],\n    \"fallback\": {\n      \"mode\": \"carrier\",\n      \"target_id\": 7,\n      \"target_name\": \"Fedex US\"\n    },\n    \"carriers\": [\n      {\n        \"id\": 7,\n        \"name\": \"Fedex US\"\n      }\n    ],\n    \"last_synced_at\": \"2026-08-20T10:11:16.000000Z\"\n  }\n}"}]},{"name":"Update Text Rule","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/routing/rules/{rule}","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","routing","rules",":rule"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"},{"key":"rule","value":"1","description":"Text rule ID"}]},"description":"Layer 3 — update a rule in place. Its position is unchanged. The rule must still either name a `odoo_3pl_shipping_method_id` or set `suppress: true`.\n\nAuthentication: requires a Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRequires the `integrations.update` permission.","body":{"mode":"raw","raw":"{\n  \"match_type\": \"starts_with\",\n  \"match_value\": \"Free\",\n  \"odoo_3pl_shipping_method_id\": 7,\n  \"suppress\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/routing/rules/{rule}","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","routing","rules",":rule"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"},{"key":"rule","value":"1","description":"Text rule ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"families\": [\n      {\n        \"shipping_carrier_id\": 3,\n        \"name\": \"FedEx\",\n        \"default_target_id\": 7,\n        \"default_target_name\": \"Fedex US\",\n        \"default_suppress\": false,\n        \"method_count\": 18,\n        \"volume\": 412,\n        \"methods\": [\n          {\n            \"shipping_method_id\": 12,\n            \"name\": \"Ground\",\n            \"full_name\": \"FedEx Ground\",\n            \"type\": \"domestic\",\n            \"volume\": 300,\n            \"override_target_id\": null,\n            \"override_suppress\": false,\n            \"has_override\": false,\n            \"will_send\": \"Fedex US\",\n            \"resolved_by\": \"carrier_family\",\n            \"resolved_detail\": \"FedEx family → Fedex US\",\n            \"suppressed\": false,\n            \"is_configured\": true\n          }\n        ]\n      }\n    ],\n    \"text_rules\": [\n      {\n        \"id\": 1,\n        \"position\": 1,\n        \"match_type\": \"contains\",\n        \"match_value\": \"Canada\",\n        \"target_id\": 8,\n        \"target_name\": \"Fedex Canada Ground\",\n        \"suppress\": false,\n        \"describe\": \"contains \\\"Canada\\\"\"\n      }\n    ],\n    \"observed_text\": [\n      {\n        \"text\": \"Free Shipping\",\n        \"volume\": 494,\n        \"will_send\": \"Fedex US\",\n        \"resolved_by\": \"fallback\",\n        \"resolved_detail\": \"fallback → Fedex US\",\n        \"suppressed\": false,\n        \"is_configured\": true\n      }\n    ],\n    \"fallback\": {\n      \"mode\": \"carrier\",\n      \"target_id\": 7,\n      \"target_name\": \"Fedex US\"\n    },\n    \"carriers\": [\n      {\n        \"id\": 7,\n        \"name\": \"Fedex US\"\n      }\n    ],\n    \"last_synced_at\": \"2026-08-20T10:11:16.000000Z\"\n  }\n}"}]},{"name":"Delete Text Rule","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/routing/rules/{rule}","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","routing","rules",":rule"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"},{"key":"rule","value":"1","description":"Text rule ID"}]},"description":"Remove a text rule. Anything it used to claim falls through to the next matching rule, or to the fallback.\n\nAuthentication: requires a Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRequires the `integrations.update` permission."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/routing/rules/{rule}","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","routing","rules",":rule"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"},{"key":"rule","value":"1","description":"Text rule ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"families\": [\n      {\n        \"shipping_carrier_id\": 3,\n        \"name\": \"FedEx\",\n        \"default_target_id\": 7,\n        \"default_target_name\": \"Fedex US\",\n        \"default_suppress\": false,\n        \"method_count\": 18,\n        \"volume\": 412,\n        \"methods\": [\n          {\n            \"shipping_method_id\": 12,\n            \"name\": \"Ground\",\n            \"full_name\": \"FedEx Ground\",\n            \"type\": \"domestic\",\n            \"volume\": 300,\n            \"override_target_id\": null,\n            \"override_suppress\": false,\n            \"has_override\": false,\n            \"will_send\": \"Fedex US\",\n            \"resolved_by\": \"carrier_family\",\n            \"resolved_detail\": \"FedEx family → Fedex US\",\n            \"suppressed\": false,\n            \"is_configured\": true\n          }\n        ]\n      }\n    ],\n    \"text_rules\": [\n      {\n        \"id\": 1,\n        \"position\": 1,\n        \"match_type\": \"contains\",\n        \"match_value\": \"Canada\",\n        \"target_id\": 8,\n        \"target_name\": \"Fedex Canada Ground\",\n        \"suppress\": false,\n        \"describe\": \"contains \\\"Canada\\\"\"\n      }\n    ],\n    \"observed_text\": [\n      {\n        \"text\": \"Free Shipping\",\n        \"volume\": 494,\n        \"will_send\": \"Fedex US\",\n        \"resolved_by\": \"fallback\",\n        \"resolved_detail\": \"fallback → Fedex US\",\n        \"suppressed\": false,\n        \"is_configured\": true\n      }\n    ],\n    \"fallback\": {\n      \"mode\": \"carrier\",\n      \"target_id\": 7,\n      \"target_name\": \"Fedex US\"\n    },\n    \"carriers\": [\n      {\n        \"id\": 7,\n        \"name\": \"Fedex US\"\n      }\n    ],\n    \"last_synced_at\": \"2026-08-20T10:11:16.000000Z\"\n  }\n}"}]},{"name":"Reorder Text Rules","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/routing/rules/reorder","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","routing","rules","reorder"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"}]},"description":"Rewrite the evaluation order. Order is behaviour, not presentation — rules are first-match-wins, so moving one changes which shipments it claims. Send the complete ordered list.\n\nAuthentication: requires a Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRequires the `integrations.update` permission.","body":{"mode":"raw","raw":"{\n  \"rule_ids\": [\n    3,\n    1,\n    2\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/routing/rules/reorder","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","routing","rules","reorder"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"families\": [\n      {\n        \"shipping_carrier_id\": 3,\n        \"name\": \"FedEx\",\n        \"default_target_id\": 7,\n        \"default_target_name\": \"Fedex US\",\n        \"default_suppress\": false,\n        \"method_count\": 18,\n        \"volume\": 412,\n        \"methods\": [\n          {\n            \"shipping_method_id\": 12,\n            \"name\": \"Ground\",\n            \"full_name\": \"FedEx Ground\",\n            \"type\": \"domestic\",\n            \"volume\": 300,\n            \"override_target_id\": null,\n            \"override_suppress\": false,\n            \"has_override\": false,\n            \"will_send\": \"Fedex US\",\n            \"resolved_by\": \"carrier_family\",\n            \"resolved_detail\": \"FedEx family → Fedex US\",\n            \"suppressed\": false,\n            \"is_configured\": true\n          }\n        ]\n      }\n    ],\n    \"text_rules\": [\n      {\n        \"id\": 1,\n        \"position\": 1,\n        \"match_type\": \"contains\",\n        \"match_value\": \"Canada\",\n        \"target_id\": 8,\n        \"target_name\": \"Fedex Canada Ground\",\n        \"suppress\": false,\n        \"describe\": \"contains \\\"Canada\\\"\"\n      }\n    ],\n    \"observed_text\": [\n      {\n        \"text\": \"Free Shipping\",\n        \"volume\": 494,\n        \"will_send\": \"Fedex US\",\n        \"resolved_by\": \"fallback\",\n        \"resolved_detail\": \"fallback → Fedex US\",\n        \"suppressed\": false,\n        \"is_configured\": true\n      }\n    ],\n    \"fallback\": {\n      \"mode\": \"carrier\",\n      \"target_id\": 7,\n      \"target_name\": \"Fedex US\"\n    },\n    \"carriers\": [\n      {\n        \"id\": 7,\n        \"name\": \"Fedex US\"\n      }\n    ],\n    \"last_synced_at\": \"2026-08-20T10:11:16.000000Z\"\n  }\n}"}]},{"name":"Set Fallback","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/routing/fallback","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","routing","fallback"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"}]},"description":"Layer 4 — what happens when no other layer matched.\n\n`mode`:\n- `passthrough` — send the method's own name / the raw order text. The legacy behaviour and the default; the provider may reject it.\n- `carrier` — send a carrier you choose (requires `odoo_3pl_shipping_method_id`).\n- `suppress` — send nothing at all.\n\nThe single highest-value setting to configure in advance, since it needs no knowledge of the data.\n\nAuthentication: requires a Bearer token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRequires the `integrations.update` permission.","body":{"mode":"raw","raw":"{\n  \"mode\": \"carrier\",\n  \"odoo_3pl_shipping_method_id\": 7\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/odoo-3pl/{integration_instance}/shipping-methods/routing/fallback","host":["{{baseUrl}}"],"path":["api","odoo-3pl",":integration_instance","shipping-methods","routing","fallback"],"variable":[{"key":"integration_instance","value":"1","description":"Integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"families\": [\n      {\n        \"shipping_carrier_id\": 3,\n        \"name\": \"FedEx\",\n        \"default_target_id\": 7,\n        \"default_target_name\": \"Fedex US\",\n        \"default_suppress\": false,\n        \"method_count\": 18,\n        \"volume\": 412,\n        \"methods\": [\n          {\n            \"shipping_method_id\": 12,\n            \"name\": \"Ground\",\n            \"full_name\": \"FedEx Ground\",\n            \"type\": \"domestic\",\n            \"volume\": 300,\n            \"override_target_id\": null,\n            \"override_suppress\": false,\n            \"has_override\": false,\n            \"will_send\": \"Fedex US\",\n            \"resolved_by\": \"carrier_family\",\n            \"resolved_detail\": \"FedEx family → Fedex US\",\n            \"suppressed\": false,\n            \"is_configured\": true\n          }\n        ]\n      }\n    ],\n    \"text_rules\": [\n      {\n        \"id\": 1,\n        \"position\": 1,\n        \"match_type\": \"contains\",\n        \"match_value\": \"Canada\",\n        \"target_id\": 8,\n        \"target_name\": \"Fedex Canada Ground\",\n        \"suppress\": false,\n        \"describe\": \"contains \\\"Canada\\\"\"\n      }\n    ],\n    \"observed_text\": [\n      {\n        \"text\": \"Free Shipping\",\n        \"volume\": 494,\n        \"will_send\": \"Fedex US\",\n        \"resolved_by\": \"fallback\",\n        \"resolved_detail\": \"fallback → Fedex US\",\n        \"suppressed\": false,\n        \"is_configured\": true\n      }\n    ],\n    \"fallback\": {\n      \"mode\": \"carrier\",\n      \"target_id\": 7,\n      \"target_name\": \"Fedex US\"\n    },\n    \"carriers\": [\n      {\n        \"id\": 7,\n        \"name\": \"Fedex US\"\n      }\n    ],\n    \"last_synced_at\": \"2026-08-20T10:11:16.000000Z\"\n  }\n}"}]}]},{"name":"ShipBob","item":[{"name":"Create Instance","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob","host":["{{baseUrl}}"],"path":["api","shipbob"]},"description":"Connect a new ShipBob integration instance. The provided ShipBob Personal Access Token is stored encrypted, a per-instance webhook signing secret is generated, webhook subscriptions are registered, and an initial pull of every entity is queued: fulfillment centers, shipping methods, products and inventory always; orders (back to settings.sync_start_date), returns and receiving orders when their settings.auto_sync toggle is on.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nShipBob is single-instance per account: when a ShipBob connection already exists the request is refused with 422 on `integration`.\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- name (string, required) — Unique display name for the instance.\n- connection_settings (object, required):\n  - environment (string, required) — 'production' or 'sandbox'.\n  - api_token (string, required) — ShipBob Personal Access Token.\n  - channel_id (integer, optional) — ShipBob write channel id (from Test Connection). When omitted, the server calls ShipBob GET /channel with the token and picks the token's only write-scoped channel (or the single Single-Merchant Application channel among several). If none is writable, or several are and none is the SMA channel, the request is refused with 422 on connection_settings.channel_id asking you to run Test Connection and choose one; a token ShipBob rejects is refused with 422 on connection_settings.api_token.\n  - channel_name (string, optional) — Display name of the channel (taken from ShipBob when the channel is resolved server-side).\n- settings (object, optional):\n  - sync_start_date (date, optional) — Earliest order date for the first import (Y-m-d). Blank or omitted stores 30 days ago.\n  - auto_create_products (boolean, optional) — Auto-create SKU products from ShipBob products.\n  - record_shipping_costs (boolean, optional, default: true) — Record shipping costs from shipments.\n  - default_shipping_method (string, optional) — Fallback shipping method name.\n- is_automatic_sync_enabled (boolean, optional, default: true) — Enable automatic background syncing.","body":{"mode":"raw","raw":"{\n  \"name\": \"My ShipBob Warehouse\",\n  \"connection_settings\": {\n    \"environment\": \"production\",\n    \"api_token\": \"shipbob_pat_xxxxxxxxxxxxxxxxxxxx\",\n    \"channel_id\": 918273,\n    \"channel_name\": \"My Store\"\n  },\n  \"settings\": {\n    \"sync_start_date\": \"2024-01-01\",\n    \"auto_create_products\": false,\n    \"record_shipping_costs\": true,\n    \"default_shipping_method\": \"Standard\"\n  },\n  \"is_automatic_sync_enabled\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob","host":["{{baseUrl}}"],"path":["api","shipbob"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"name\": \"My ShipBob Warehouse\",\n    \"environment\": \"production\",\n    \"channel_id\": 918273,\n    \"channel_name\": \"My Store\",\n    \"is_automatic_sync_enabled\": true,\n    \"settings\": {\n      \"auto_create_products\": false,\n      \"record_shipping_costs\": true,\n      \"default_shipping_method\": \"Standard\",\n      \"sync_start_date\": \"2024-01-01\",\n      \"is_automatic_sync_enabled\": true,\n      \"auto_sync\": {\n        \"orders\": true,\n        \"shipments\": true,\n        \"inventory\": true,\n        \"products\": true,\n        \"returns\": true,\n        \"receiving\": true\n      }\n    },\n    \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"updated_at\": \"2024-06-01T14:22:05.000000Z\"\n  }\n}"}]},{"name":"Test Connection","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/test-connection","host":["{{baseUrl}}"],"path":["api","shipbob","test-connection"]},"description":"Validate an unsaved set of ShipBob credentials before creating an instance. Never returns an error status for bad credentials — a failure is reported as connected=false with a message. On success it returns the channels available to the token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- environment (string, required) — 'production' or 'sandbox'.\n- api_token (string, required) — ShipBob Personal Access Token.","body":{"mode":"raw","raw":"{\n  \"environment\": \"production\",\n  \"api_token\": \"shipbob_pat_xxxxxxxxxxxxxxxxxxxx\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/test-connection","host":["{{baseUrl}}"],"path":["api","shipbob","test-connection"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"connected\": true,\n    \"channels\": [\n      {\n        \"id\": 918273,\n        \"name\": \"My Store\",\n        \"application_name\": \"SKU.io\",\n        \"writable\": true\n      },\n      {\n        \"id\": 918274,\n        \"name\": \"Wholesale\",\n        \"application_name\": \"SKU.io\",\n        \"writable\": false\n      }\n    ],\n    \"message\": null\n  }\n}"}]},{"name":"Get Instance","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Retrieve a single ShipBob integration instance with its connection metadata and behavior settings. Sensitive credentials (the API token and webhook secret) are never returned.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"name\": \"My ShipBob Warehouse\",\n    \"environment\": \"production\",\n    \"channel_id\": 918273,\n    \"channel_name\": \"My Store\",\n    \"is_automatic_sync_enabled\": true,\n    \"settings\": {\n      \"auto_create_products\": false,\n      \"record_shipping_costs\": true,\n      \"default_shipping_method\": \"Standard\",\n      \"sync_start_date\": \"2024-01-01\",\n      \"is_automatic_sync_enabled\": true,\n      \"auto_sync\": {\n        \"orders\": true,\n        \"shipments\": true,\n        \"inventory\": true,\n        \"products\": true,\n        \"returns\": true,\n        \"receiving\": true\n      },\n      \"fulfillment_cost_financial_line_type_id\": 12,\n      \"billing\": {\n        \"enabled\": true,\n        \"start_date\": \"2026-09-01\",\n        \"attribute_to_orders\": true,\n        \"attribute_storage\": true,\n        \"capitalise_receiving\": false,\n        \"post_invoices_to_accounting\": false,\n        \"default_nominal_code_id\": null,\n        \"last_synced_at\": \"2026-10-08T10:15:00.000000Z\",\n        \"last_error\": null\n      }\n    },\n    \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"updated_at\": \"2024-06-01T14:22:05.000000Z\"\n  }\n}"}]},{"name":"Update Instance","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Partially update a ShipBob integration instance. Only the sections present in the request are applied; omitted sections keep their current values. The environment cannot be changed after connect. Leave api_token out to keep the stored token.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nRequest Body (all fields optional):\n- name (string) — Unique display name.\n- connection_settings (object):\n  - api_token (string, nullable) — New Personal Access Token (omit to keep the existing one).\n  - channel_id (integer, nullable) — ShipBob write channel id. A changed id is checked live against the token's write-scoped channels (ShipBob GET /channel); a channel the token cannot write is refused with 422 on connection_settings.channel_id, and the stored channel_name is taken from ShipBob.\n  - channel_name (string, nullable) — Channel display name.\n- settings (object):\n  - sync_start_date (date, nullable) — Earliest order date pulled by a \"from start date\" order sync (Y-m-d); null means 30 days ago. A Full Re-sync ignores it.\n  - auto_create_products (boolean, nullable).\n  - record_shipping_costs (boolean, nullable).\n  - default_shipping_method (string, nullable).\n  - fulfillment_cost_financial_line_type_id (integer, optional, nullable) — COST financial line type for freight; omitted keeps the current value.\n  - billing (object, optional) — merged over the current billing settings:\n    - enabled (boolean) — billing sync + attribution switch (default false); turning it on fills the freight line type with the default \"Shipping Cost\" type when empty and queues the first billing sync.\n    - start_date (date, nullable) — earliest charge date synced/attributed.\n    - attribute_to_orders (boolean, default true)\n    - attribute_storage (boolean, default true)\n    - capitalise_receiving (boolean, default false) — write receiving fees as purchase order cost lines (landed cost) instead of a cost allocation.\n    - post_invoices_to_accounting (boolean, default false) — post ShipBob invoices as vendor bills.\n    - default_nominal_code_id (integer, nullable) — fallback expense account for posted fee lines.\n- is_automatic_sync_enabled (boolean) — false pauses all scheduled syncing for the instance.\n  - settings.auto_sync (object, nullable) — per-entity scheduled-sync toggles: orders, shipments (also tracking), inventory, products, returns, receiving. An entity switched off is skipped by the scheduler.\n\n`settings.billing.*` keys are saved one by one: sending a single billing switch leaves the other billing settings untouched. `settings.billing.default_nominal_code_id` is required whenever invoice posting is on after the save — `settings.billing.post_invoices_to_accounting` sent as `true`, or already on and not sent — so clearing the account on a posting instance is also rejected with 422. Settings the request does not send (including `fulfillment_cost_financial_line_type_id`) are left as stored; sending `fulfillment_cost_financial_line_type_id: null` selects the default Shipping Cost type.","body":{"mode":"raw","raw":"{\n  \"name\": \"My ShipBob Warehouse (East)\",\n  \"connection_settings\": {\n    \"channel_id\": 918273,\n    \"channel_name\": \"My Store\"\n  },\n  \"settings\": {\n    \"record_shipping_costs\": true,\n    \"default_shipping_method\": \"Standard\",\n    \"fulfillment_cost_financial_line_type_id\": 12,\n    \"billing\": {\n      \"enabled\": true,\n      \"start_date\": \"2026-09-01\",\n      \"attribute_to_orders\": true,\n      \"attribute_storage\": true,\n      \"capitalise_receiving\": false,\n      \"post_invoices_to_accounting\": false,\n      \"default_nominal_code_id\": null\n    }\n  },\n  \"is_automatic_sync_enabled\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"name\": \"My ShipBob Warehouse\",\n    \"environment\": \"production\",\n    \"channel_id\": 918273,\n    \"channel_name\": \"My Store\",\n    \"is_automatic_sync_enabled\": true,\n    \"settings\": {\n      \"auto_create_products\": false,\n      \"record_shipping_costs\": true,\n      \"default_shipping_method\": \"Standard\",\n      \"sync_start_date\": \"2024-01-01\",\n      \"is_automatic_sync_enabled\": true,\n      \"auto_sync\": {\n        \"orders\": true,\n        \"shipments\": true,\n        \"inventory\": true,\n        \"products\": true,\n        \"returns\": true,\n        \"receiving\": true\n      },\n      \"fulfillment_cost_financial_line_type_id\": 12,\n      \"billing\": {\n        \"enabled\": true,\n        \"start_date\": \"2026-09-01\",\n        \"attribute_to_orders\": true,\n        \"attribute_storage\": true,\n        \"capitalise_receiving\": false,\n        \"post_invoices_to_accounting\": false,\n        \"default_nominal_code_id\": null,\n        \"last_synced_at\": \"2026-10-08T10:15:00.000000Z\",\n        \"last_error\": null\n      }\n    },\n    \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"updated_at\": \"2024-06-01T14:22:05.000000Z\"\n  }\n}"}]},{"name":"Delete Instance","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Disconnect a ShipBob integration instance. Webhook subscriptions are removed at ShipBob and all mirrored data is deleted in the background; the request returns immediately.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nReturns 422 (nothing is removed) while ShipBob billing has capitalised receiving fees into purchase orders received in a locked accounting period; `data.locked_lines` lists them. Unlock the period or keep the integration."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"ShipBob integration is being disconnected.\"\n}"}]},{"name":"Test Saved Connection","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/test-connection","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","test-connection"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Re-validate the stored credentials for an existing instance and list the channels available to the token. Never returns an error status for bad credentials.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nWhen ShipBob accepts the credentials and the connection had been flagged inactive (after a rejected token), the connection is marked active again, automatic sync is re-enabled and the admins' \"authorization expired\" alert is resolved. Saving a new `connection_settings.api_token` through Update Integration Instance does the same once ShipBob accepts the new token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/test-connection","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","test-connection"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"connected\": true,\n    \"channels\": [\n      {\n        \"id\": 918273,\n        \"name\": \"My Store\",\n        \"application_name\": \"SKU.io\",\n        \"writable\": true\n      }\n    ],\n    \"message\": null\n  }\n}"}]},{"name":"Get Dashboard Metrics","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/dashboard","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","dashboard"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Aggregate KPI counters and sync/webhook health for the instance dashboard.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nReturns order/shipment/product/return/receiving counters plus per-entity last-sync timestamps, connection state, and webhook health.\n\n`sync_status.connection_state` is `active`, or `inactive` once ShipBob has rejected the API token (HTTP 401) — until a new token is verified."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/dashboard","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","dashboard"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"metrics\": {\n      \"processing_orders\": 8,\n      \"shipped_last_7d\": 143,\n      \"exceptions\": 2,\n      \"on_hold\": 1,\n      \"unmapped_products\": 5,\n      \"wros_in_progress\": 3,\n      \"open_returns\": 4\n    },\n    \"sync_status\": {\n      \"connection_state\": \"active\",\n      \"last_synced_at\": {\n        \"orders\": \"2024-06-01T14:22:05.000000Z\",\n        \"shipments\": \"2024-06-01T14:22:05.000000Z\",\n        \"inventory\": \"2024-06-01T14:22:05.000000Z\",\n        \"products\": \"2024-06-01T14:22:05.000000Z\",\n        \"returns\": \"2024-06-01T14:22:05.000000Z\",\n        \"receiving\": \"2024-06-01T14:22:05.000000Z\"\n      },\n      \"webhook_health\": {\n        \"last_event_at\": \"2024-06-01T14:22:05.000000Z\"\n      }\n    }\n  }\n}"}]},{"name":"Get Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/activity-log","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","activity-log"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Paginated activity for the connection — instance-level configuration and connection changes plus activity for every order belonging to the instance. Ordered newest-first.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nFilters: filter[event], filter[source], filter[batch_uuid], and filter[search] (matches the event name, the acting user's name, the description, and the properties payload).\n\nPagination: page, per_page (default 15).\n\nEach entry also carries `subject_label` (e.g. `ShipBob connection`, `ShipBob order SO-SB-1003`), `field_labels` (changed-field key → plain name, e.g. `integration_settings.fulfillment_cost_financial_line_type_id` → `Freight line type`) and `value_labels` (field key → id → name for the freight line type and default nominal code). Sync bookkeeping (`last_sync`, `last_webhook_received_at`, `billing.last_synced_at`, `billing.sync_resume`, `billing.resolver_version`, `billing.last_error*`) is never logged and is stripped from older entries.\n\nAuthentication: Requires Bearer token.\n\nSync bookkeeping (`details_last_updated` on orders, sync timestamps on the connection) is never shown, and rows whose only change was bookkeeping are left out. Changes a ShipBob sync or webhook made carry `properties.source = shipbob_sync` and no causer (`filter[source]=shipbob_sync`)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/activity-log","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","activity-log"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 512,\n      \"log_name\": \"default\",\n      \"description\": \"updated\",\n      \"event\": \"updated\",\n      \"subject_type\": \"ShipbobIntegrationInstance\",\n      \"subject_id\": 12,\n      \"properties\": {\n        \"attributes\": {\n          \"name\": \"ShipBob US\"\n        },\n        \"old\": {\n          \"name\": \"ShipBob\"\n        },\n        \"source\": \"manual\"\n      },\n      \"causer_name\": \"Jane Smith\",\n      \"created_at\": \"2026-09-01T12:00:00.000000Z\"\n    },\n    {\n      \"id\": 498,\n      \"log_name\": \"default\",\n      \"description\": \"created\",\n      \"event\": \"created\",\n      \"subject_type\": \"ShipbobOrder\",\n      \"subject_id\": 3345,\n      \"properties\": {\n        \"attributes\": {\n          \"shipbob_id\": 448899123,\n          \"reference_id\": \"SO-100245\",\n          \"order_number\": \"SO-100245\",\n          \"status\": \"Processing\"\n        },\n        \"source\": \"sync\"\n      },\n      \"causer_name\": \"Jane Smith\",\n      \"created_at\": \"2026-08-30T09:15:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 2,\n  \"from\": 1,\n  \"to\": 2,\n  \"path\": \"{{protocol}}{{domain}}/api/shipbob/{instance}/activity-log\",\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/shipbob/{instance}/activity-log?page=1\",\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/shipbob/{instance}/activity-log?page=1\",\n  \"next_page_url\": null,\n  \"prev_page_url\": null\n}"}]},{"name":"List Orders","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/orders","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","orders"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Paginated, filterable, sortable list of ShipBob orders for the instance (the provider mirror of a fulfillment order).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFree-text search covers order_number and reference_id. Sort with the `sort` param (prefix - for descending). Default page size is 10.\n\n`filter[search]` matches the order #, reference, any shipment's tracking number (`search_columns=tracking_number`) and, for an all-digit term, the exact ShipBob order id (`search_columns=shipbob_id`)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/orders","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","orders"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3345,\n      \"integration_instance_id\": 12,\n      \"shipbob_id\": 448899123,\n      \"reference_id\": \"SO-100245\",\n      \"order_number\": \"SO-100245\",\n      \"status\": \"Processing\",\n      \"status_label\": \"Processing\",\n      \"status_color\": \"blue\",\n      \"order_type\": \"DTC\",\n      \"external_channel\": false,\n      \"shipment_count\": 1,\n      \"tracking_summary\": [\n        {\n          \"tracking_number\": \"1Z999AA10123456784\",\n          \"carrier\": \"UPS\",\n          \"tracking_url\": \"https://www.ups.com/track?tracknum=1Z999AA10123456784\",\n          \"status\": \"Completed\"\n        }\n      ],\n      \"purchase_date\": \"2024-06-01T14:22:05.000000Z\",\n      \"last_synced\": \"2024-06-01T14:22:05.000000Z\",\n      \"details_last_updated\": \"2024-06-01T14:22:05.000000Z\",\n      \"external_url\": \"https://web.shipbob.com/app/orders/448899123\",\n      \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n      \"updated_at\": \"2024-06-01T14:22:05.000000Z\",\n      \"sku_fulfillment_order_id\": 55021,\n      \"link_type_label\": \"Fulfillment Order\",\n      \"link_id\": 55021,\n      \"link_reference\": \"SO-100245\",\n      \"link_route\": \"/orders/fulfillment-orders/55021\",\n      \"sales_order\": {\n        \"link_type_label\": \"Sales Order\",\n        \"link_id\": 40012,\n        \"link_reference\": \"SO-100245\",\n        \"link_route\": \"/orders/sales-orders/40012\"\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 5,\n  \"per_page\": 10,\n  \"total\": 48,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"Get Order","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/orders/{order}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","orders",":order"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"order","value":"","description":"The order ID."}]},"description":"Retrieve a single ShipBob order by its SKU.io row id (as returned by the list endpoint), scoped to the instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nIncludes `shipments[]` — every synced shipment of the order with status, tracking and a `link_route` to its shipment page — and resolves the fulfillment-order number (`link_reference`, e.g. `SO-100245.1`) and the `sales_order` cross-link."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/orders/{order}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","orders",":order"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"order","value":"","description":"The order ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3345,\n    \"integration_instance_id\": 12,\n    \"shipbob_id\": 448899123,\n    \"reference_id\": \"SO-100245\",\n    \"order_number\": \"SO-100245\",\n    \"status\": \"Processing\",\n    \"status_label\": \"Processing\",\n    \"status_color\": \"blue\",\n    \"order_type\": \"DTC\",\n    \"external_channel\": false,\n    \"shipment_count\": 1,\n    \"tracking_summary\": [\n      {\n        \"tracking_number\": \"1Z999AA10123456784\",\n        \"carrier\": \"UPS\",\n        \"tracking_url\": \"https://www.ups.com/track?tracknum=1Z999AA10123456784\",\n        \"status\": \"Completed\"\n      }\n    ],\n    \"shipments\": [\n      {\n        \"id\": 771122,\n        \"shipbob_shipment_id\": 559900,\n        \"status\": \"Completed\",\n        \"status_label\": \"Completed\",\n        \"status_color\": \"success\",\n        \"status_details\": [],\n        \"tracking_number\": \"1Z999AA10123456784\",\n        \"tracking_url\": \"https://www.ups.com/track?tracknum=1Z999AA10123456784\",\n        \"carrier\": \"UPS\",\n        \"fulfillment_center\": \"Moreno Valley (CA)\",\n        \"invoice_amount\": 6.42,\n        \"estimated_fulfillment_date\": \"2024-06-01T14:22:05.000000Z\",\n        \"estimated_fulfillment_date_status\": \"OnTime\",\n        \"link_type_label\": \"ShipBob Shipment\",\n        \"link_id\": 771122,\n        \"link_reference\": 559900,\n        \"link_route\": \"/integrations/shipbob/12/shipments/771122\"\n      }\n    ],\n    \"purchase_date\": \"2024-06-01T14:22:05.000000Z\",\n    \"last_synced\": \"2024-06-01T14:22:05.000000Z\",\n    \"details_last_updated\": \"2024-06-01T14:22:05.000000Z\",\n    \"external_url\": \"https://web.shipbob.com/app/orders/448899123\",\n    \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"updated_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"sku_fulfillment_order_id\": 55021,\n    \"link_type_label\": \"Fulfillment Order\",\n    \"link_id\": 55021,\n    \"link_reference\": \"SO-100245.1\",\n    \"link_route\": \"/orders/fulfillment-orders/55021\",\n    \"sales_order\": {\n      \"link_type_label\": \"Sales Order\",\n      \"link_id\": 40012,\n      \"link_reference\": \"SO-100245\",\n      \"link_route\": \"/orders/sales-orders/40012\"\n    }\n  }\n}"}]},{"name":"Get Raw Order Data","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/orders/{order}/raw","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","orders",":order","raw"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"order","value":"","description":"The order ID."}]},"description":"Fetch the raw ShipBob order payload live from ShipBob (fetch-on-demand, never persisted).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/orders/{order}/raw","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","orders",":order","raw"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"order","value":"","description":"The order ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product\": {\n      \"id\": 448899123,\n      \"order_number\": \"SO-100245\",\n      \"status\": \"Processing\",\n      \"reference_id\": \"SO-100245\",\n      \"purchase_date\": \"2024-06-01T14:22:05.000000Z\",\n      \"shipments\": [\n        {\n          \"id\": 771122,\n          \"status\": \"Processing\"\n        }\n      ]\n    }\n  },\n  \"fetched_at\": \"2024-06-01T14:22:05.000000Z\"\n}"}]},{"name":"Get Order Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/orders/{order}/activity-log","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","orders",":order","activity-log"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"order","value":"","description":"The order ID."}]},"description":"Paginated activity-log entries for a single order — the record of changes to the order (created, updated, deleted), the user who made each change, and a properties payload with the changed attributes. Ordered newest-first.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nFilters: filter[event], filter[source], filter[batch_uuid], and filter[search] (matches the event name, the acting user's name, and the properties payload).\n\nPagination: page, per_page (default 15).\n\nAuthentication: Requires Bearer token.\n\nRows come in the same shape as the connection's Activity Log: `subject_label`, `field_labels` and `value_labels` carry plain names. Sync bookkeeping (`details_last_updated`) is never shown, and rows whose only change was bookkeeping are left out. Changes a ShipBob sync or webhook made carry `properties.source = shipbob_sync` and no causer (filter with `filter[source]=shipbob_sync`)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/orders/{order}/activity-log","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","orders",":order","activity-log"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"order","value":"","description":"The order ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 355,\n      \"log_name\": \"default\",\n      \"description\": \"created\",\n      \"event\": \"created\",\n      \"subject_type\": \"ShipbobOrder\",\n      \"subject_id\": 1,\n      \"subject_label\": \"ShipBob order SO-SB-1003.1\",\n      \"properties\": {\n        \"attributes\": {\n          \"id\": 1,\n          \"integration_instance_id\": 46,\n          \"shipbob_id\": 23663586,\n          \"reference_id\": \"SO-0001.1\",\n          \"order_number\": \"SO-0001.1\",\n          \"status\": \"Exception\"\n        },\n        \"source\": \"manual\"\n      },\n      \"causer_name\": \"Administrator\",\n      \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n      \"field_labels\": {\n        \"shipbob_id\": \"ShipBob order ID\",\n        \"reference_id\": \"Reference\",\n        \"order_number\": \"Order #\"\n      },\n      \"value_labels\": {}\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1,\n  \"path\": \"{{protocol}}{{domain}}/api/shipbob/{instance}/orders/{order}/activity-log\",\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/shipbob/{instance}/orders/{order}/activity-log?page=1\",\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/shipbob/{instance}/orders/{order}/activity-log?page=1\",\n  \"next_page_url\": null,\n  \"prev_page_url\": null\n}"}]},{"name":"Sync Orders","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/orders/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","orders","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Queue a background order sync and return the tracked job id so progress can be followed.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nRequest Body (all optional):\n- mode (string) — Sync mode (e.g. 'recent', 'date_range', 'single').\n- date_from (date) — Lower bound for the sync window (Y-m-d).\n- date_to (date) — Upper bound for the sync window (Y-m-d).\n- order_number (string) — Restrict the sync to a single order number.\n- reference_ids (string) — Comma-separated reference ids to sync.","body":{"mode":"raw","raw":"{\n  \"mode\": \"date_range\",\n  \"date_from\": \"2024-06-01\",\n  \"date_to\": \"2024-06-30\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/orders/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","orders","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 90211\n  },\n  \"message\": \"Order sync queued\"\n}"}]},{"name":"Get Order Sync Info","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/orders/sync-info","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","orders","sync-info"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Metadata for the order sync/refresh modal, read from SKU.io's synced orders (no ShipBob call): `latest_order_number` / `latest_order_date` are the synced order with the newest purchase date, `total_orders` is how many orders are synced for this instance, plus the configured sync start date.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/orders/sync-info","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","orders","sync-info"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"latest_order_number\": \"SO-100245\",\n    \"latest_order_date\": \"2024-06-01T14:22:05.000000Z\",\n    \"total_orders\": 1204,\n    \"order_start_date\": \"2024-06-01T14:22:05.000000Z\",\n    \"order_start_date_source\": \"settings\"\n  }\n}"}]},{"name":"Search Orders","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/orders/search?query=SO-100245","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","orders","search"],"query":[{"key":"query","value":"SO-100245","description":"Reference id or order number to search for.","disabled":true}],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Live provider-side order search for the refresh/search modal: matches `query` as a ShipBob reference id first, then as an order number. Returns the raw ShipBob order rows (empty array when nothing matches), each with `exists_in_database` — true when SKU.io already holds that ShipBob order, so the search dialog offers Update instead of Download.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nQuery parameters:\n- query (string, required) — the reference id or order number."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/orders/search?query=SO-100245","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","orders","search"],"query":[{"key":"query","value":"SO-100245","description":"Reference id or order number to search for.","disabled":true}],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 448899123,\n      \"order_number\": \"SO-100245\",\n      \"reference_id\": \"SO-100245\",\n      \"status\": \"Processing\",\n      \"purchase_date\": \"2024-06-01T14:22:05.000000Z\",\n      \"exists_in_database\": true\n    }\n  ]\n}"}]},{"name":"Refresh Order","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/orders/refresh-order","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","orders","refresh-order"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Live-fetch a single order from ShipBob by its ShipBob id and upsert it locally.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- unique_id (string, required) — The ShipBob order id to refresh.","body":{"mode":"raw","raw":"{\n  \"unique_id\": \"448899123\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/orders/refresh-order","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","orders","refresh-order"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3345,\n    \"integration_instance_id\": 12,\n    \"shipbob_id\": 448899123,\n    \"reference_id\": \"SO-100245\",\n    \"order_number\": \"SO-100245\",\n    \"status\": \"Processing\",\n    \"status_label\": \"Processing\",\n    \"status_color\": \"blue\",\n    \"order_type\": \"DTC\",\n    \"external_channel\": false,\n    \"shipment_count\": 1,\n    \"tracking_summary\": [\n      {\n        \"tracking_number\": \"1Z999AA10123456784\",\n        \"carrier\": \"UPS\",\n        \"tracking_url\": \"https://www.ups.com/track?tracknum=1Z999AA10123456784\",\n        \"status\": \"Completed\"\n      }\n    ],\n    \"purchase_date\": \"2024-06-01T14:22:05.000000Z\",\n    \"last_synced\": \"2024-06-01T14:22:05.000000Z\",\n    \"details_last_updated\": \"2024-06-01T14:22:05.000000Z\",\n    \"external_url\": \"https://web.shipbob.com/app/orders/448899123\",\n    \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"updated_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"sku_fulfillment_order_id\": 55021,\n    \"link_type_label\": \"Fulfillment Order\",\n    \"link_id\": 55021,\n    \"link_reference\": \"SO-100245\",\n    \"link_route\": \"/orders/fulfillment-orders/55021\",\n    \"sales_order\": {\n      \"link_type_label\": \"Sales Order\",\n      \"link_id\": 40012,\n      \"link_reference\": \"SO-100245\",\n      \"link_route\": \"/orders/sales-orders/40012\"\n    }\n  },\n  \"message\": \"Order refreshed from ShipBob\"\n}"}]},{"name":"List Shipments","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipments","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipments"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Paginated, filterable, sortable list of ShipBob shipments for the instance (one ship event each).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFree-text search covers tracking_number and carrier. Default page size is 10.\n\nEach row's `sof_link.link_reference` is the fulfillment number (e.g. `SO-SB-1003.1`). `sales_order` resolves through the linked fulfillment or, for a parcel not matched to a fulfillment yet, through the ShipBob order's fulfillment order."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipments","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipments"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 771122,\n      \"shipbob_order_id\": 3345,\n      \"shipbob_shipment_id\": 559900,\n      \"status\": \"Completed\",\n      \"status_label\": \"Completed\",\n      \"status_color\": \"green\",\n      \"status_details\": [],\n      \"status_detail_chips\": [],\n      \"tracking_number\": \"1Z999AA10123456784\",\n      \"carrier\": \"UPS\",\n      \"tracking_url\": \"https://www.ups.com/track?tracknum=1Z999AA10123456784\",\n      \"invoice_amount\": 6.42,\n      \"insurance_value\": 0,\n      \"estimated_fulfillment_date\": \"2024-06-01T14:22:05.000000Z\",\n      \"estimated_fulfillment_date_status\": \"AwaitingReceipt\",\n      \"fulfillment_center\": \"Cicero (IL)\",\n      \"shipped_at\": \"2024-06-01T14:22:05.000000Z\",\n      \"delivered_at\": \"2024-06-01T14:22:05.000000Z\",\n      \"carton_count\": 1,\n      \"cartons\": [\n        {\n          \"type\": \"Box\",\n          \"weight\": 12.5\n        }\n      ],\n      \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n      \"updated_at\": \"2024-06-01T14:22:05.000000Z\",\n      \"order\": {\n        \"id\": 3345,\n        \"order_number\": \"SO-100245\",\n        \"shipbob_id\": 448899123,\n        \"link_type_label\": \"ShipBob Order\",\n        \"link_id\": 3345,\n        \"link_reference\": \"SO-100245\",\n        \"link_route\": \"/integrations/shipbob/12/orders/3345\"\n      },\n      \"sku_sales_order_fulfillment_id\": 88123,\n      \"sof_link\": {\n        \"link_type_label\": \"Fulfillment\",\n        \"link_id\": 88123,\n        \"link_reference\": \"F-88123\",\n        \"link_route\": \"/orders/sales-orders/fulfillments/88123\"\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 10,\n  \"total\": 27,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"Get Shipment","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipments/{shipment}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipments",":shipment"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"shipment","value":"","description":"The shipment ID."}]},"description":"Retrieve a single ShipBob shipment by its SKU.io row id (falling back to the ShipBob shipment id), scoped to the instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nThe path id accepts EITHER the local record id (as returned by the list endpoint) OR the external ShipBob id, so both in-app links and external/deep links resolve.\n\n`sof_link.link_reference` is the fulfillment's number (e.g. `SO-100245.1`); `sales_order` links the SKU.io sales order that fulfillment belongs to (null when the shipment is not linked).\n\n`sales_order` resolves through the linked fulfillment or, when the shipment has none yet (e.g. an exception parcel), through the ShipBob order's fulfillment order."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipments/{shipment}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipments",":shipment"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"shipment","value":"","description":"The shipment ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 771122,\n    \"shipbob_order_id\": 3345,\n    \"shipbob_shipment_id\": 559900,\n    \"status\": \"Completed\",\n    \"status_label\": \"Completed\",\n    \"status_color\": \"green\",\n    \"status_details\": [],\n    \"status_detail_chips\": [],\n    \"tracking_number\": \"1Z999AA10123456784\",\n    \"carrier\": \"UPS\",\n    \"tracking_url\": \"https://www.ups.com/track?tracknum=1Z999AA10123456784\",\n    \"invoice_amount\": 6.42,\n    \"insurance_value\": 0,\n    \"estimated_fulfillment_date\": \"2024-06-01T14:22:05.000000Z\",\n    \"estimated_fulfillment_date_status\": \"AwaitingReceipt\",\n    \"fulfillment_center\": \"Cicero (IL)\",\n    \"shipped_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"delivered_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"carton_count\": 1,\n    \"cartons\": [\n      {\n        \"type\": \"Box\",\n        \"weight\": 12.5\n      }\n    ],\n    \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"updated_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"order\": {\n      \"id\": 3345,\n      \"order_number\": \"SO-100245\",\n      \"shipbob_id\": 448899123,\n      \"link_type_label\": \"ShipBob Order\",\n      \"link_id\": 3345,\n      \"link_reference\": \"SO-100245\",\n      \"link_route\": \"/integrations/shipbob/12/orders/3345\"\n    },\n    \"sku_sales_order_fulfillment_id\": 88123,\n    \"sof_link\": {\n      \"link_type_label\": \"Fulfillment\",\n      \"link_id\": 88123,\n      \"link_reference\": \"SO-100245.1\",\n      \"link_route\": \"/orders/sales-orders/fulfillments/88123\"\n    },\n    \"sales_order\": {\n      \"id\": 40012,\n      \"link_type_label\": \"Sales Order\",\n      \"link_id\": 40012,\n      \"link_reference\": \"SO-100245\",\n      \"link_route\": \"/orders/sales-orders/40012\"\n    }\n  }\n}"}]},{"name":"Get Raw Shipment Data","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipments/{shipment}/raw","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipments",":shipment","raw"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"shipment","value":"","description":"The shipment ID."}]},"description":"Fetch the raw ShipBob shipment payload live from ShipBob (fetch-on-demand, never persisted).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nThe path id accepts EITHER the local record id (as returned by the list endpoint) OR the external ShipBob id, so both in-app links and external/deep links resolve."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipments/{shipment}/raw","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipments",":shipment","raw"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"shipment","value":"","description":"The shipment ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product\": {\n      \"id\": 559900,\n      \"status\": \"Completed\",\n      \"tracking\": {\n        \"tracking_number\": \"1Z999AA10123456784\",\n        \"carrier\": \"UPS\"\n      }\n    }\n  },\n  \"fetched_at\": \"2024-06-01T14:22:05.000000Z\"\n}"}]},{"name":"Get Shipment Timeline","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipments/{shipment}/timeline","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipments",":shipment","timeline"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"shipment","value":"","description":"The shipment ID."}]},"description":"Fetch the shipment's event timeline live from ShipBob.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nThe path id accepts EITHER the local record id (as returned by the list endpoint) OR the external ShipBob id, so both in-app links and external/deep links resolve."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipments/{shipment}/timeline","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipments",":shipment","timeline"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"shipment","value":"","description":"The shipment ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"timestamp\": \"2024-06-01T14:22:05.000000Z\",\n      \"name\": \"LabelCreated\",\n      \"description\": \"Shipping label created.\"\n    },\n    {\n      \"timestamp\": \"2024-06-01T14:22:05.000000Z\",\n      \"name\": \"Shipped\",\n      \"description\": \"Package handed to carrier.\"\n    }\n  ]\n}"}]},{"name":"Refresh Shipment","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipments/{shipment}/refresh","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipments",":shipment","refresh"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"shipment","value":"","description":"The shipment ID."}]},"description":"Live-fetch one shipment from ShipBob and upsert the stored copy, then return the refreshed record. Use it to bring a single shipment up to date without running a full shipment sync.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nThe path id accepts EITHER the local record id (as returned by the list endpoint) OR the external ShipBob id.\n\nShipBob nests shipments under their order, so the refresh reads the parent order and re-ingests its shipments. Tracking passback and fulfillment linking run exactly as they do during a sync.\n\nThe response omits the raw provider payload; call Get Shipment with include_json=true for that."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipments/{shipment}/refresh","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipments",":shipment","refresh"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"shipment","value":"","description":"The shipment ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 771122,\n    \"shipbob_order_id\": 3345,\n    \"shipbob_shipment_id\": 559900,\n    \"status\": \"Completed\",\n    \"status_label\": \"Completed\",\n    \"status_color\": \"green\",\n    \"status_details\": [],\n    \"status_detail_chips\": [],\n    \"tracking_number\": \"1Z999AA10123456784\",\n    \"carrier\": \"UPS\",\n    \"tracking_url\": \"https://www.ups.com/track?tracknum=1Z999AA10123456784\",\n    \"invoice_amount\": 6.42,\n    \"insurance_value\": 0,\n    \"estimated_fulfillment_date\": \"2024-06-01T14:22:05.000000Z\",\n    \"estimated_fulfillment_date_status\": \"AwaitingReceipt\",\n    \"fulfillment_center\": \"Cicero (IL)\",\n    \"shipped_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"delivered_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"carton_count\": 1,\n    \"cartons\": [\n      {\n        \"type\": \"Box\",\n        \"weight\": 12.5\n      }\n    ],\n    \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"updated_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"order\": {\n      \"id\": 3345,\n      \"order_number\": \"SO-100245\",\n      \"shipbob_id\": 448899123,\n      \"link_type_label\": \"ShipBob Order\",\n      \"link_id\": 3345,\n      \"link_reference\": \"SO-100245\",\n      \"link_route\": \"/integrations/shipbob/12/orders/3345\"\n    },\n    \"sku_sales_order_fulfillment_id\": 88123,\n    \"sof_link\": {\n      \"link_type_label\": \"Fulfillment\",\n      \"link_id\": 88123,\n      \"link_reference\": \"F-88123\",\n      \"link_route\": \"/orders/sales-orders/fulfillments/88123\"\n    }\n  },\n  \"message\": \"Shipment refreshed from ShipBob\"\n}"}]},{"name":"Sync Shipments","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipments/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipments","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Queue a background shipment sync and return the tracked job id.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipments/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipments","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 90212\n  },\n  \"message\": \"Shipment sync queued\"\n}"}]},{"name":"List Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Paginated, filterable, sortable list of ShipBob products (one row per variant) with their SKU.io product mapping state.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFree-text search covers name, SKU, barcode, UPC, and GTIN; a wholly numeric search term also matches the record id exactly. Filter by mapping state with filter[mapping] (comma-separable: mapped, unmapped, needs_review, bundle). Advanced numeric, text, and datetime filters with operator variants (e.g. filter[name.contains], filter[total_on_hand.greater_than], filter[last_synced_at.between]) are also available for id, shipbob_product_id, name, sku, barcode, product_type, total_on_hand, total_fulfillable, last_synced_at, created_at, and updated_at.\n\nSort with the `sort` param (prefix - for descending). Default page size is 10.\n- filter[sku_product_id] — exact SKU.io product id the ShipBob product is mapped to."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 671,\n      \"integration_instance_id\": 12,\n      \"shipbob_product_id\": 776655,\n      \"shipbob_variant_id\": 887766,\n      \"name\": \"Widget - Large\",\n      \"sku\": \"WIDGET-L\",\n      \"barcode\": 123456789012,\n      \"upc\": \"123456789012\",\n      \"gtin\": 123456789012,\n      \"product_type\": \"Regular\",\n      \"total_on_hand\": 240,\n      \"total_committed\": 12,\n      \"total_fulfillable\": 228,\n      \"total_awaiting\": 0,\n      \"total_backordered\": 0,\n      \"total_exception\": 0,\n      \"total_sellable\": 228,\n      \"sku_product_id\": 9012,\n      \"sku_product\": {\n        \"id\": 9012,\n        \"sku\": \"WIDGET-L\",\n        \"name\": \"Widget - Large\",\n        \"link_route\": \"/products/9012\"\n      },\n      \"needs_review\": false,\n      \"mapping_state\": \"mapped\",\n      \"last_synced_at\": \"2026-09-01T12:00:00+00:00\",\n      \"created_at\": \"2026-08-15T09:30:00+00:00\",\n      \"updated_at\": \"2026-09-01T12:00:00+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 12,\n  \"per_page\": 10,\n  \"total\": 120,\n  \"from\": 1,\n  \"to\": 10,\n  \"path\": \"{{protocol}}{{domain}}/api/shipbob/{instance}/products\",\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/shipbob/{instance}/products?page=1\",\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/shipbob/{instance}/products?page=12\",\n  \"next_page_url\": \"{{protocol}}{{domain}}/api/shipbob/{instance}/products?page=2\",\n  \"prev_page_url\": null,\n  \"links\": [\n    {\n      \"url\": null,\n      \"label\": \"&laquo; Previous\",\n      \"active\": false\n    },\n    {\n      \"url\": \"{{protocol}}{{domain}}/api/shipbob/{instance}/products?page=1\",\n      \"label\": \"1\",\n      \"active\": true\n    },\n    {\n      \"url\": \"{{protocol}}{{domain}}/api/shipbob/{instance}/products?page=2\",\n      \"label\": \"2\",\n      \"active\": false\n    },\n    {\n      \"url\": \"{{protocol}}{{domain}}/api/shipbob/{instance}/products?page=2\",\n      \"label\": \"Next &raquo;\",\n      \"active\": false\n    }\n  ]\n}"}]},{"name":"Get Product","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products/{product}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products",":product"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"product","value":"","description":"The product ID."}]},"description":"Get a single ShipBob product by id, scoped to the instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products/{product}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products",":product"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"product","value":"","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 671,\n    \"integration_instance_id\": 12,\n    \"shipbob_product_id\": 776655,\n    \"shipbob_variant_id\": 887766,\n    \"name\": \"Widget - Large\",\n    \"sku\": \"WIDGET-L\",\n    \"barcode\": 123456789012,\n    \"upc\": \"123456789012\",\n    \"gtin\": 123456789012,\n    \"product_type\": \"Regular\",\n    \"total_on_hand\": 240,\n    \"total_committed\": 12,\n    \"total_fulfillable\": 228,\n    \"total_awaiting\": 0,\n    \"total_backordered\": 0,\n    \"total_exception\": 0,\n    \"total_sellable\": 228,\n    \"sku_product_id\": 9012,\n    \"sku_product\": {\n      \"id\": 9012,\n      \"sku\": \"WIDGET-L\",\n      \"name\": \"Widget - Large\",\n      \"link_route\": \"/products/9012\"\n    },\n    \"needs_review\": false,\n    \"mapping_state\": \"mapped\",\n    \"last_synced_at\": \"2026-09-01T12:00:00+00:00\",\n    \"created_at\": \"2026-08-15T09:30:00+00:00\",\n    \"updated_at\": \"2026-09-01T12:00:00+00:00\"\n  }\n}"}]},{"name":"Get Raw Product Data","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products/{product}/raw","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products",":product","raw"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"product","value":"","description":"The product ID."}]},"description":"The stored raw provider payload for one product, with the timestamp of when it was last refreshed. Run a product sync to refresh it.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products/{product}/raw","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products",":product","raw"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"product","value":"","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"json_object\": {\n      \"id\": 887766,\n      \"name\": \"Widget - Large\",\n      \"sku\": \"WIDGET-L\",\n      \"barcode\": 123456789012,\n      \"upc\": \"123456789012\",\n      \"gtin\": 123456789012,\n      \"total_inventory\": {\n        \"total_on_hand_quantity\": 240,\n        \"total_committed_quantity\": 12,\n        \"total_fulfillable_quantity\": 228\n      }\n    },\n    \"details_last_updated\": \"2026-09-01T12:00:00+00:00\"\n  }\n}"}]},{"name":"Get Product Mapping Candidates","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products/{product}/mapping-candidates","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products",":product","mapping-candidates"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"product","value":"","description":"The product ID."}]},"description":"The SKU.io products the auto-matcher considers for one ShipBob product: every product whose SKU or barcode equals the ShipBob product's barcode, UPC, GTIN or SKU, with the identifiers it shares (`matched_on`). A product with more than one candidate is flagged Needs Review instead of being mapped; the mapping dialog lists these for a one-click pick. 404 for a product of another connection.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products/{product}/mapping-candidates","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products",":product","mapping-candidates"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"product","value":"","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1201,\n      \"sku\": \"DOC-CARAFE-1L\",\n      \"name\": \"Glass Carafe 1L\",\n      \"barcode\": \"8100000000055\",\n      \"matched_on\": [\n        \"8100000000055\"\n      ]\n    },\n    {\n      \"id\": 1207,\n      \"sku\": \"DOC-SPICE-TRIO\",\n      \"name\": \"Spice Trio\",\n      \"barcode\": \"8100000000055\",\n      \"matched_on\": [\n        \"8100000000055\"\n      ]\n    }\n  ]\n}"}]},{"name":"Get Products Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products/summary","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products","summary"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Aggregate counts of ShipBob products by mapping state, plus when the product catalog was last synced.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products/summary","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products","summary"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"all\": 120,\n    \"mapped\": 95,\n    \"unmapped\": 25,\n    \"needs_review\": 3,\n    \"bundles\": 7,\n    \"total\": 120,\n    \"last_synced_at\": \"2026-09-01T12:00:00.000000Z\"\n  }\n}"}]},{"name":"Sync Products","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Queue a background product sync and return the tracked job id. The sync runs asynchronously.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 123\n  },\n  \"message\": \"Product sync queued\"\n}"}]},{"name":"Auto-Match Products","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products/auto-match","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products","auto-match"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Auto-match still-unmapped ShipBob products to SKU.io products by barcode, UPC, GTIN, or SKU. When an identifier matches more than one SKU.io product, the ShipBob product is flagged needs_review instead of being mapped.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products/auto-match","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products","auto-match"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"matched\": 4,\n    \"matched_count\": 4\n  },\n  \"message\": \"4 product(s) auto-matched\"\n}"}]},{"name":"Map Product","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products/{product}/mapping","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products",":product","mapping"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"product","value":"","description":"The product ID."}]},"description":"Map a ShipBob product to the SKU.io product it represents.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- sku_product_id (integer, required) — id of an existing SKU.io product. To remove a mapping, use the Unmap Product endpoint instead.\n\nOne SKU.io product backs at most one ShipBob product on a connection: mapping a SKU.io product that another ShipBob product already uses is refused with a 422 naming that ShipBob product. Remove that mapping first.","body":{"mode":"raw","raw":"{\n  \"sku_product_id\": 456\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products/{product}/mapping","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products",":product","mapping"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"product","value":"","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 671,\n    \"integration_instance_id\": 12,\n    \"shipbob_product_id\": 776655,\n    \"shipbob_variant_id\": 887766,\n    \"name\": \"Widget - Large\",\n    \"sku\": \"WIDGET-L\",\n    \"barcode\": 123456789012,\n    \"upc\": \"123456789012\",\n    \"gtin\": 123456789012,\n    \"product_type\": \"Regular\",\n    \"total_on_hand\": 240,\n    \"total_committed\": 12,\n    \"total_fulfillable\": 228,\n    \"total_awaiting\": 0,\n    \"total_backordered\": 0,\n    \"total_exception\": 0,\n    \"total_sellable\": 228,\n    \"sku_product_id\": 456,\n    \"sku_product\": {\n      \"id\": 456,\n      \"sku\": \"WIDGET-L\",\n      \"name\": \"Widget - Large\",\n      \"link_route\": \"/products/456\"\n    },\n    \"needs_review\": false,\n    \"mapping_state\": \"mapped\",\n    \"last_synced_at\": \"2026-09-01T12:00:00+00:00\",\n    \"created_at\": \"2026-08-15T09:30:00+00:00\",\n    \"updated_at\": \"2026-09-01T12:00:00+00:00\"\n  },\n  \"message\": \"Product mapping saved\"\n}"}]},{"name":"Unmap Product","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products/{product}/mapping","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products",":product","mapping"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"product","value":"","description":"The product ID."}]},"description":"Remove the SKU.io product mapping from a ShipBob product so it returns to the unmapped queue.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/products/{product}/mapping","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","products",":product","mapping"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"product","value":"","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 671,\n    \"integration_instance_id\": 12,\n    \"shipbob_product_id\": 776655,\n    \"shipbob_variant_id\": 887766,\n    \"name\": \"Widget - Large\",\n    \"sku\": \"WIDGET-L\",\n    \"barcode\": 123456789012,\n    \"upc\": \"123456789012\",\n    \"gtin\": 123456789012,\n    \"product_type\": \"Regular\",\n    \"total_on_hand\": 240,\n    \"total_committed\": 12,\n    \"total_fulfillable\": 228,\n    \"total_awaiting\": 0,\n    \"total_backordered\": 0,\n    \"total_exception\": 0,\n    \"total_sellable\": 228,\n    \"sku_product_id\": null,\n    \"sku_product\": null,\n    \"needs_review\": false,\n    \"mapping_state\": \"unmapped\",\n    \"last_synced_at\": \"2026-09-01T12:00:00+00:00\",\n    \"created_at\": \"2026-08-15T09:30:00+00:00\",\n    \"updated_at\": \"2026-09-01T12:00:00+00:00\"\n  },\n  \"message\": \"Product mapping removed\"\n}"}]},{"name":"List Inventory Levels","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/inventory","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","inventory"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Paginated, filterable, sortable list of ShipBob inventory levels (per inventory item x fulfillment center).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nDefault page size is 10."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/inventory","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","inventory"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5501,\n      \"integration_instance_id\": 12,\n      \"shipbob_inventory_id\": 334455,\n      \"shipbob_fulfillment_center_id\": 8,\n      \"fulfillment_center_name\": \"Cicero (IL)\",\n      \"on_hand\": 240,\n      \"committed\": 12,\n      \"fulfillable\": 228,\n      \"awaiting\": 0,\n      \"internal_transfer\": 0,\n      \"product_link\": {\n        \"link_type_label\": \"Product\",\n        \"link_id\": 9012,\n        \"link_reference\": \"WIDGET-L\",\n        \"link_route\": \"/products/9012\"\n      },\n      \"last_synced_at\": \"2024-06-01T14:22:05.000000Z\",\n      \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n      \"updated_at\": \"2024-06-01T14:22:05.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 2,\n  \"per_page\": 10,\n  \"total\": 14,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"Sync Inventory","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/inventory/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","inventory","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Queue a background inventory sync and return the tracked job id.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/inventory/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","inventory","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 90215\n  },\n  \"message\": \"Inventory sync queued\"\n}"}]},{"name":"List Discrepancies","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/inventory/discrepancies","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","inventory","discrepancies"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"List on-hand discrepancies between ShipBob fulfillable quantities and SKU on-hand for mapped products (mapped warehouses only).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/inventory/discrepancies","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","inventory","discrepancies"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"shipbob_product\": {\n        \"id\": 671,\n        \"sku\": \"WIDGET-L\",\n        \"sku_product_id\": 9012\n      },\n      \"warehouse_ids\": [\n        3\n      ],\n      \"shipbob_on_hand\": 240,\n      \"sku_on_hand\": 232,\n      \"delta\": 8\n    }\n  ]\n}"}]},{"name":"Create Stock Take from Discrepancies","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/inventory/discrepancies/stock-take","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","inventory","discrepancies","stock-take"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Create adjustment-mode stock takes from ShipBob-vs-SKU.io inventory discrepancies — ONE stock take per mapped SKU.io warehouse, each holding only that warehouse's deltas (ShipBob on-hand at the fulfillment centers mapped to the warehouse minus SKU.io on-hand in it).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token. Permission: integrations.sync.\n\nRequest Body (all optional):\n- warehouse_id (integer) — Only reconcile this mapped SKU.io warehouse (the Discrepancies tab's warehouse filter). 0 or omitted = every mapped warehouse.\n- items (array) — Exact discrepancy rows to include: [{product_id, warehouse_id}]. Takes precedence over product_ids.\n- items.*.product_id (integer, required with items) — SKU.io product id.\n- items.*.warehouse_id (integer, required with items) — SKU.io warehouse id.\n- product_ids (array of integers) — SKU.io product ids; includes every in-scope warehouse row for them.\n- date (date) — Stock take count date (Y-m-d); defaults to today.\n- notes (string, max 5000) — Notes for every stock take created.\n- status (string) — 'draft' (default) or 'closed' to finalize each adjustment immediately (a take blocked by reserved inventory is kept as a draft and `warning` is set).\n\nOmit items and product_ids to include every discrepancy in scope.\n\nResponse: stock_take_id (the first take), stock_takes (one summary per warehouse), selected_count (rows included), total_value_adjustment (money: Σ delta × product unit cost), warning.\n\nErrors: 422 when no warehouse is mapped or nothing matches the selection; 422 validation errors for a malformed body.","body":{"mode":"raw","raw":"{\n  \"warehouse_id\": 12,\n  \"items\": [\n    {\n      \"product_id\": 9012,\n      \"warehouse_id\": 12\n    }\n  ],\n  \"date\": \"2024-06-15\",\n  \"notes\": \"Reconcile against ShipBob\",\n  \"status\": \"draft\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/inventory/discrepancies/stock-take","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","inventory","discrepancies","stock-take"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"stock_take_id\": 44120,\n    \"stock_takes\": [\n      {\n        \"id\": 44120,\n        \"warehouse_id\": 12,\n        \"warehouse_name\": \"ShipBob Bethlehem\",\n        \"item_count\": 1,\n        \"total_value_adjustment\": \"-30.00\",\n        \"status\": \"draft\"\n      }\n    ],\n    \"selected_count\": 1,\n    \"total_value_adjustment\": \"-30.00\",\n    \"warning\": null\n  },\n  \"message\": \"Stock take created from discrepancies\"\n}"}]},{"name":"List Fulfillment Centers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/fulfillment-centers","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","fulfillment-centers"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"List all ShipBob fulfillment centers for the instance with their SKU warehouse mapping.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/fulfillment-centers","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","fulfillment-centers"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 41,\n      \"integration_instance_id\": 12,\n      \"shipbob_fulfillment_center_id\": 8,\n      \"name\": \"Cicero (IL)\",\n      \"address\": {\n        \"address1\": \"5900 W Ogden Ave\",\n        \"city\": \"Cicero\",\n        \"state\": \"IL\",\n        \"zip_code\": \"60804\",\n        \"country\": \"US\"\n      },\n      \"timezone\": \"America/Chicago\",\n      \"mapped\": true,\n      \"sku_warehouse_id\": 3,\n      \"warehouse_link\": {\n        \"link_type_label\": \"Warehouse\",\n        \"link_id\": 3,\n        \"link_reference\": \"Main Warehouse\",\n        \"link_route\": \"/crm/warehouses/3\"\n      },\n      \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n      \"updated_at\": \"2024-06-01T14:22:05.000000Z\"\n    }\n  ]\n}"}]},{"name":"Sync Fulfillment Centers","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/fulfillment-centers/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","fulfillment-centers","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Queue a background fulfillment center sync and return the tracked job id.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/fulfillment-centers/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","fulfillment-centers","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 90216\n  },\n  \"message\": \"Fulfillment center sync queued\"\n}"}]},{"name":"List Mappable Warehouses","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/fulfillment-centers/mappable-warehouses","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","fulfillment-centers","mappable-warehouses"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"SKU.io warehouses a ShipBob fulfillment center may be mapped to — exactly those the mapping save accepts: Direct warehouses on Manual (or no) fulfillment (adopted into this instance's 3PL warehouse on save) and warehouses already bound to this instance. Other 3PL / shipping-provider, FBA/AWD and supplier warehouses, and archived warehouses, are excluded; warehouses an FC of this instance is already mapped to are always included. Unpaginated, ordered by name.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nResponse fields: id, name, type ('direct' | '3pl'), bound_to_instance (already this instance's ShipBob 3PL warehouse)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/fulfillment-centers/mappable-warehouses","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","fulfillment-centers","mappable-warehouses"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"name\": \"ShipBob Bethlehem\",\n      \"type\": \"3pl\",\n      \"bound_to_instance\": true\n    },\n    {\n      \"id\": 31,\n      \"name\": \"Main Warehouse\",\n      \"type\": \"direct\",\n      \"bound_to_instance\": false\n    }\n  ]\n}"}]},{"name":"Save Fulfillment Center Mappings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/fulfillment-centers/mappings","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","fulfillment-centers","mappings"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Bulk save fulfillment center to SKU warehouse mappings.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- mappings (array, required):\n  - id (integer, required) — Fulfillment center row id.\n  - warehouse_id (integer, nullable) — SKU warehouse id to map to; null clears the mapping.","body":{"mode":"raw","raw":"{\n  \"mappings\": [\n    {\n      \"id\": 41,\n      \"warehouse_id\": 3\n    },\n    {\n      \"id\": 42,\n      \"warehouse_id\": null\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/fulfillment-centers/mappings","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","fulfillment-centers","mappings"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Fulfillment center mappings saved\"\n}"}]},{"name":"List Shipping Methods","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipping-methods","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipping-methods"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"List ShipBob shipping methods with their mapping state, plus the active SKU shipping methods available to map to.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipping-methods","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipping-methods"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"services\": [\n      {\n        \"id\": 21,\n        \"shipbob_shipping_method_id\": 100,\n        \"name\": \"Standard\",\n        \"service_level_name\": \"Ground\",\n        \"active\": true,\n        \"is_default\": true,\n        \"shipping_method_id\": 7,\n        \"shipping_method_name\": \"UPS Ground\"\n      }\n    ],\n    \"sku_shipping_methods\": [\n      {\n        \"id\": 7,\n        \"name\": \"UPS Ground\",\n        \"full_name\": \"UPS - UPS Ground\",\n        \"type\": \"carrier\"\n      }\n    ]\n  }\n}"}]},{"name":"Sync Shipping Methods","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipping-methods/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipping-methods","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Queue a background shipping method sync and return the tracked job id.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipping-methods/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipping-methods","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 90217\n  },\n  \"message\": \"Shipping method sync queued\"\n}"}]},{"name":"Map Shipping Method","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipping-methods/{id}/mapping","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipping-methods",":id","mapping"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"id","value":"","description":"The record ID."}]},"description":"Set or clear the SKU shipping method for a ShipBob shipping method. Omit or null shipping_method_id to unmap.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- shipping_method_id (integer, nullable) — SKU shipping method id to map to; null unmaps.","body":{"mode":"raw","raw":"{\n  \"shipping_method_id\": 7\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipping-methods/{id}/mapping","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipping-methods",":id","mapping"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Shipping method mapping saved\"\n}"}]},{"name":"Auto-Match Shipping Methods","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipping-methods/auto-match","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipping-methods","auto-match"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Auto-match still-unmapped ShipBob shipping methods to SKU shipping methods by normalized name.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipping-methods/auto-match","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipping-methods","auto-match"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"matched_count\": 3\n  },\n  \"message\": \"3 shipping method(s) auto-matched\"\n}"}]},{"name":"Bulk Map Shipping Methods","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipping-methods/bulk-map","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipping-methods","bulk-map"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Map many ShipBob shipping methods to one SKU shipping method (or clear when omitted) in one call.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- ids (array of integers, required, min 1) — ShipBob shipping method row ids.\n- shipping_method_id (integer, nullable) — SKU shipping method id to map to; null clears.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    21,\n    22\n  ],\n  \"shipping_method_id\": 7\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipping-methods/bulk-map","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipping-methods","bulk-map"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"updated_count\": 2\n  },\n  \"message\": \"2 shipping method mapping(s) saved\"\n}"}]},{"name":"Import Shipping Method Mappings","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipping-methods/import","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipping-methods","import"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Import shipping method mappings from a CSV file. Good rows apply; each rejected row is reported in `data.errors` as \"Line N: …\" (the header is line 1). Blank lines are skipped and a UTF-8 BOM (Excel \"CSV UTF-8\") is tolerated.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data.\n\nForm Data:\n- file (file, required) — CSV file (.csv or .txt, max 2 MB).\n\nColumns (use Export first for a template):\n- id — the ShipBob shipping method row id (from the export). Matched first.\n- shipbob_shipping_method — the ShipBob method name; used when id is blank or unknown.\n- service_level — informational, ignored on import.\n- shipping_method_id — SKU shipping method id (authoritative when present).\n- shipping_method_name — SKU shipping method name or full name (case-insensitive), used when shipping_method_id is blank.\nLeave both SKU columns blank to remove that mapping.\n\nResponse counts: created (newly mapped), updated (re-mapped), deleted (mapping cleared), errors (per-row messages).\n\nErrors:\n- 422 — `file` missing / wrong type / over 2 MB (validation), or the CSV has no header row or lacks the key columns (an id or shipbob_shipping_method column, and a shipping_method_id or shipping_method_name column).","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipping-methods/import","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipping-methods","import"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"created\": 2,\n    \"updated\": 1,\n    \"deleted\": 0,\n    \"errors\": []\n  },\n  \"message\": \"Shipping methods import processed\"\n}"}]},{"name":"Export Shipping Method Mappings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipping-methods/export","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipping-methods","export"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Export the shipping method mappings as a re-importable CSV file download.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nResponse: text/csv file attachment."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/shipping-methods/export","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","shipping-methods","export"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"id,shipbob_shipping_method,service_level,shipping_method_id,shipping_method_name\n21,Standard,Ground,7,UPS - UPS Ground\n"}]},{"name":"List Returns","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/returns","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","returns"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Paginated, filterable, sortable list of ShipBob returns for the instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFree-text search covers reference_id and tracking_number. Default page size is 10.\n\nFilter `filter[return_type]` (and `return_type.is` / `.is_not` / `.is_one_of` / `.is_not_one_of`) matches every spelling of a type: `ReturnToSender` and `RTS` both select return-to-sender returns.\n\nEach return carries cross-links: `original_shipment` (the synced ShipBob shipment, matched by ShipBob shipment id within this instance), `original_order` (its ShipBob order) and `sales_order` (the SKU.io sales order that shipment fulfilled) — each null when not resolvable. `return_type_label` is the display name; ShipBob sends return-to-sender as either \"RTS\" or \"ReturnToSender\" and both read \"Return to Sender\". `items[]` lists each returned inventory item with `requested_action` (+ `requested_action_type`, `instructions`) and what the warehouse did: `action_taken` (comma-joined actions), `action_taken_quantity`, `action_taken_reason` — null until processed."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/returns","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","returns"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 2201,\n      \"integration_instance_id\": 12,\n      \"shipbob_return_id\": 660011,\n      \"reference_id\": \"RET-5567\",\n      \"status\": \"Processing\",\n      \"status_label\": \"Processing\",\n      \"status_color\": \"blue\",\n      \"return_type\": \"ReturnToSender\",\n      \"return_type_label\": \"Return to Sender\",\n      \"original_shipment_id\": 559900,\n      \"original_shipment\": {\n        \"id\": 771122,\n        \"shipbob_shipment_id\": 559900,\n        \"link_type_label\": \"ShipBob Shipment\",\n        \"link_id\": 771122,\n        \"link_reference\": 559900,\n        \"link_route\": \"/integrations/shipbob/12/shipments/771122\"\n      },\n      \"original_order\": {\n        \"id\": 88120,\n        \"link_type_label\": \"ShipBob Order\",\n        \"link_id\": 88120,\n        \"link_reference\": \"SO-10442\",\n        \"link_route\": \"/integrations/shipbob/12/orders/88120\"\n      },\n      \"sales_order\": {\n        \"id\": 10442,\n        \"link_type_label\": \"Sales Order\",\n        \"link_id\": 10442,\n        \"link_reference\": \"SO-10442\",\n        \"link_route\": \"/orders/sales-orders/10442\"\n      },\n      \"tracking_number\": \"1Z999AA10123456999\",\n      \"invoice_amount\": 3.1,\n      \"insert_date\": \"2024-06-01T14:22:05.000000Z\",\n      \"completed_date\": null,\n      \"items\": [\n        {\n          \"id\": 334455,\n          \"inventory_id\": 334455,\n          \"name\": \"Widget - Large\",\n          \"sku\": null,\n          \"quantity\": 2,\n          \"requested_action\": \"Restock\",\n          \"requested_action_type\": \"Default\",\n          \"instructions\": null,\n          \"action_taken\": \"Restock, Dispose\",\n          \"action_taken_quantity\": 2,\n          \"action_taken_reason\": \"Damaged\"\n        }\n      ],\n      \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n      \"updated_at\": \"2024-06-01T14:22:05.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 2,\n  \"per_page\": 10,\n  \"total\": 11,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"Get Return","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/returns/{return}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","returns",":return"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"return","value":"","description":"The return ID."}]},"description":"Retrieve a single ShipBob return by its SKU.io row id (falling back to the ShipBob return id), scoped to the instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nThe path id accepts EITHER the local record id (as returned by the list endpoint) OR the external ShipBob id, so both in-app links and external/deep links resolve.\n\nEach return carries cross-links: `original_shipment` (the synced ShipBob shipment, matched by ShipBob shipment id within this instance), `original_order` (its ShipBob order) and `sales_order` (the SKU.io sales order that shipment fulfilled) — each null when not resolvable. `return_type_label` is the display name; ShipBob sends return-to-sender as either \"RTS\" or \"ReturnToSender\" and both read \"Return to Sender\". `items[]` lists each returned inventory item with `requested_action` (+ `requested_action_type`, `instructions`) and what the warehouse did: `action_taken` (comma-joined actions), `action_taken_quantity`, `action_taken_reason` — null until processed.\n\nOn this detail endpoint only, `product_links` gives each item's ShipBob product page and mapped SKU.io product: `{inventory_id, shipbob_product: {id, sku, name, link_route}, sku_product: {id, sku, link_route} | null}`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/returns/{return}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","returns",":return"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"return","value":"","description":"The return ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 2201,\n    \"integration_instance_id\": 12,\n    \"shipbob_return_id\": 660011,\n    \"reference_id\": \"RET-5567\",\n    \"status\": \"Processing\",\n    \"status_label\": \"Processing\",\n    \"status_color\": \"blue\",\n    \"return_type\": \"ReturnToSender\",\n    \"return_type_label\": \"Return to Sender\",\n    \"original_shipment_id\": 559900,\n    \"original_shipment\": {\n      \"id\": 771122,\n      \"shipbob_shipment_id\": 559900,\n      \"link_type_label\": \"ShipBob Shipment\",\n      \"link_id\": 771122,\n      \"link_reference\": 559900,\n      \"link_route\": \"/integrations/shipbob/12/shipments/771122\"\n    },\n    \"original_order\": {\n      \"id\": 88120,\n      \"link_type_label\": \"ShipBob Order\",\n      \"link_id\": 88120,\n      \"link_reference\": \"SO-10442\",\n      \"link_route\": \"/integrations/shipbob/12/orders/88120\"\n    },\n    \"sales_order\": {\n      \"id\": 10442,\n      \"link_type_label\": \"Sales Order\",\n      \"link_id\": 10442,\n      \"link_reference\": \"SO-10442\",\n      \"link_route\": \"/orders/sales-orders/10442\"\n    },\n    \"tracking_number\": \"1Z999AA10123456999\",\n    \"invoice_amount\": 3.1,\n    \"insert_date\": \"2024-06-01T14:22:05.000000Z\",\n    \"completed_date\": null,\n    \"items\": [\n      {\n        \"id\": 334455,\n        \"inventory_id\": 334455,\n        \"name\": \"Widget - Large\",\n        \"sku\": null,\n        \"quantity\": 2,\n        \"requested_action\": \"Restock\",\n        \"requested_action_type\": \"Default\",\n        \"instructions\": null,\n        \"action_taken\": \"Restock, Dispose\",\n        \"action_taken_quantity\": 2,\n        \"action_taken_reason\": \"Damaged\"\n      }\n    ],\n    \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"updated_at\": \"2024-06-01T14:22:05.000000Z\"\n  }\n}"}]},{"name":"Get Raw Return Data","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/returns/{return}/raw","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","returns",":return","raw"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"return","value":"","description":"The return ID."}]},"description":"Fetch the raw ShipBob return payload live from ShipBob (fetch-on-demand, never persisted).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nThe path id accepts EITHER the local record id (as returned by the list endpoint) OR the external ShipBob id, so both in-app links and external/deep links resolve."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/returns/{return}/raw","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","returns",":return","raw"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"return","value":"","description":"The return ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product\": {\n      \"id\": 660011,\n      \"status\": \"Processing\",\n      \"reference_id\": \"RET-5567\"\n    }\n  },\n  \"fetched_at\": \"2024-06-01T14:22:05.000000Z\"\n}"}]},{"name":"Sync Returns","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/returns/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","returns","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Queue a background return sync and return the tracked job id.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/returns/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","returns","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 90218\n  },\n  \"message\": \"Return sync queued\"\n}"}]},{"name":"List Receiving Orders","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/receiving","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","receiving"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Paginated, filterable, sortable list of ShipBob receiving orders (WROs — inbound shipments to a fulfillment center).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nDefault page size is 10.\n\n`fulfillment_center` is the fulfillment center's name (the synced FC, else the name on the WRO payload). `purchase_order_number` is the number the merchant gave the WRO in ShipBob; `purchase_order` links the SKU.io purchase order with that number when one exists (else null)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/receiving","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","receiving"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3301,\n      \"integration_instance_id\": 12,\n      \"shipbob_receiving_id\": 770022,\n      \"status\": \"Awaiting\",\n      \"status_label\": \"Awaiting Arrival\",\n      \"status_color\": \"orange\",\n      \"shipbob_fulfillment_center_id\": 8,\n      \"fulfillment_center\": \"Cicero (IL)\",\n      \"purchase_order_number\": \"PO-SB-2041\",\n      \"purchase_order\": {\n        \"id\": 5120,\n        \"link_type_label\": \"Purchase Order\",\n        \"link_id\": 5120,\n        \"link_reference\": \"PO-SB-2041\",\n        \"link_route\": \"/orders/purchase-orders/5120\"\n      },\n      \"package_type\": \"Package\",\n      \"box_packaging_type\": \"EverythingInOneBox\",\n      \"expected_arrival_date\": \"2024-06-01T14:22:05.000000Z\",\n      \"insert_date\": \"2024-06-01T14:22:05.000000Z\",\n      \"total_expected_qty\": 500,\n      \"total_received_qty\": 0,\n      \"total_stowed_qty\": 0,\n      \"box_count\": 5,\n      \"variance_items\": [\n        {\n          \"inventory_id\": 334455,\n          \"name\": \"Widget - Large\",\n          \"expected\": 500,\n          \"received\": 0,\n          \"variance\": -500,\n          \"has_variance\": true\n        }\n      ],\n      \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n      \"updated_at\": \"2024-06-01T14:22:05.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 3,\n  \"from\": 1,\n  \"to\": 3\n}"}]},{"name":"Get Receiving Order","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/receiving/{receiving}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","receiving",":receiving"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"receiving","value":"","description":"The receiving ID."}]},"description":"Retrieve a single ShipBob receiving order (WRO) by its SKU.io row id (falling back to the ShipBob receiving id), scoped to the instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\n`fulfillment_center` is the fulfillment center's name (the synced FC, else the name on the WRO payload). `purchase_order_number` is the number the merchant gave the WRO in ShipBob; `purchase_order` links the SKU.io purchase order with that number when one exists (else null).\n\nAlso returns `package_type_label` / `box_packaging_type_label` (plain words, e.g. `One SKU per box`), `box_labels_available` (false once the WRO is Completed or Cancelled) and, on this detail endpoint only, `product_links`: one entry per item `{inventory_id, shipbob_product: {id, sku, name, link_route}, sku_product: {id, sku, link_route} | null}`.\n\n`variance_applicable` is false until stock has started arriving (Awaiting and Cancelled orders): each `variance_items[].variance` is then null and `has_variance` false, and the page shows \"Not received yet\" instead of a shortfall."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/receiving/{receiving}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","receiving",":receiving"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"receiving","value":"","description":"The receiving ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3301,\n    \"integration_instance_id\": 12,\n    \"shipbob_receiving_id\": 770022,\n    \"status\": \"Awaiting\",\n    \"status_label\": \"Awaiting Arrival\",\n    \"status_color\": \"orange\",\n    \"shipbob_fulfillment_center_id\": 8,\n    \"fulfillment_center\": \"Cicero (IL)\",\n    \"purchase_order_number\": \"PO-SB-2041\",\n    \"purchase_order\": {\n      \"id\": 5120,\n      \"link_type_label\": \"Purchase Order\",\n      \"link_id\": 5120,\n      \"link_reference\": \"PO-SB-2041\",\n      \"link_route\": \"/orders/purchase-orders/5120\"\n    },\n    \"package_type\": \"Package\",\n    \"box_packaging_type\": \"EverythingInOneBox\",\n    \"expected_arrival_date\": \"2024-06-01T14:22:05.000000Z\",\n    \"insert_date\": \"2024-06-01T14:22:05.000000Z\",\n    \"total_expected_qty\": 500,\n    \"total_received_qty\": 0,\n    \"total_stowed_qty\": 0,\n    \"box_count\": 5,\n    \"variance_applicable\": true,\n    \"variance_items\": [\n      {\n        \"inventory_id\": 334455,\n        \"name\": \"Widget - Large\",\n        \"expected\": 500,\n        \"received\": 0,\n        \"variance\": -500,\n        \"has_variance\": true\n      }\n    ],\n    \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"updated_at\": \"2024-06-01T14:22:05.000000Z\"\n  }\n}"}]},{"name":"Get Raw Receiving Order Data","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/receiving/{receiving}/raw","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","receiving",":receiving","raw"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"receiving","value":"","description":"The receiving ID."}]},"description":"Fetch the raw ShipBob receiving order payload live from ShipBob (fetch-on-demand, never persisted).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/receiving/{receiving}/raw","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","receiving",":receiving","raw"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"receiving","value":"","description":"The receiving ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product\": {\n      \"id\": 770022,\n      \"status\": \"Awaiting\",\n      \"fulfillment_center\": {\n        \"id\": 8,\n        \"name\": \"Cicero (IL)\"\n      }\n    }\n  },\n  \"fetched_at\": \"2024-06-01T14:22:05.000000Z\"\n}"}]},{"name":"Sync Receiving Orders","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/receiving/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","receiving","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Queue a background receiving order sync and return the tracked job id.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/receiving/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","receiving","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 90219\n  },\n  \"message\": \"Receiving order sync queued\"\n}"}]},{"name":"Get Box Labels","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/receiving/{receiving}/box-labels","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","receiving",":receiving","box-labels"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"receiving","value":"","description":"The receiving ID."}]},"description":"Download the box labels for a single receiving order (WRO) — one label per box/pallet, proxied live from ShipBob (`GET /receiving/{id}/labels`). `{receiving}` is the ShipBob WRO id.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nResponse: normally the PDF itself (`Content-Type: application/pdf`, attachment named `shipbob-wro-{receiving}-box-labels.pdf`). If ShipBob answers with a link on its own API host, SKU.io follows it and still returns the PDF; if it answers with a link anywhere else, the response is JSON `{\"data\": {\"url\": \"…\"}}` for the client to open. A ShipBob body that is not a PDF is never passed through as one.\n\nErrors (JSON, with a readable `message`):\n- 404 — receiving order not found for this instance, or ShipBob reports no labels for it.\n- 422 — ShipBob rejected the request, did not return a PDF, or could not be reached."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/receiving/{receiving}/box-labels","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","receiving",":receiving","box-labels"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"receiving","value":"","description":"The receiving ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"url\": \"https://labels.example.com/wro/770022.pdf\"\n  }\n}"}]},{"name":"List Bills","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"List ShipBob billing transactions (every charge, credit and payment ShipBob billed) with where each fee was attributed: a sales order, a product (storage), a purchase order (receiving) or the unattributed bucket. Same shape as the Trackstar bills list: charge_type is the ShipBob fee name.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFilterable columns:\n- Text: fee_name, family (shipping, fulfillment, surcharge, storage, receiving, returns, credit, payment, account, other), reference_type (Shipment, Return), reference_id, store_order_id, attribution_status (pending, attributed, unattributed, excluded), unattributed_reason (order_not_imported, order_not_found, shipment_not_found, return_not_found, product_unmapped, po_not_found, ambiguous_order, no_reference, treatment_unattributed, period_locked, manually_voided), sales_channel, shipbob_invoice_id, transaction_type (Charge, Credit, Payment), currency_code, attributed_to (sales_order, product, purchase_order, cost_entry, none).\n- Date: charge_date.\n- Numeric: id, amount.\n- filter[search] matches id, reference id, store order id, transaction id or fee name.\n\nFiltering uses the canonical filter tree: send filter_groups as base64-encoded JSON {\"conjunction\":\"and\",\"children\":[{\"type\":\"condition\",\"condition\":{\"column\":\"fee_name\",\"operator\":\"is\",\"value\":\"Per Pick Fee\"}}]}, or flat filter[column.operator]=value params. Text operators: contains, does_not_contain, is, is_not, is_one_of, is_not_one_of, starts_with, ends_with, is_empty, is_not_empty. Numeric operators: is, is_not, greater_than, less_than, greater_than_or_equal, less_than_or_equal, between, is_empty, is_not_empty. Date operators: is, before, after, on_or_before, on_or_after, between, today, past_week, past_month, past_days, etc.\n\nAllowed sorts: id, charge_date, amount, fee_name (prefix with - for descending; default -charge_date).\nPagination: page, per_page (default 10). Add include_json=1 to include the raw ShipBob payload."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 123,\n      \"integration_instance_id\": 4,\n      \"shipbob_transaction_id\": \"01FAKEEXAMPLE0000000000001\",\n      \"charge_type\": \"Per Pick Fee\",\n      \"fee_name\": \"Per Pick Fee\",\n      \"family\": \"fulfillment\",\n      \"treatment\": \"order_fee\",\n      \"transaction_type\": \"Charge\",\n      \"charge_date\": \"2026-10-03\",\n      \"charge_start_date\": \"2026-10-03\",\n      \"charge_end_date\": \"2026-10-03\",\n      \"amount\": 0.35,\n      \"currency_code\": \"USD\",\n      \"formatted_amount\": \"$0.35\",\n      \"amount_in_tenant_currency\": 0.35,\n      \"tax_amount\": 0,\n      \"reference_type\": \"Shipment\",\n      \"reference_id\": \"512345678\",\n      \"inventory_id\": null,\n      \"po_id\": null,\n      \"store_order_id\": \"154164\",\n      \"master_order_id\": 401055055,\n      \"sales_channel\": \"Shopify\",\n      \"order_type\": \"D2C\",\n      \"fulfillment_center\": \"Cicero (IL)\",\n      \"invoice\": {\n        \"id\": 77,\n        \"shipbob_invoice_id\": 90000004,\n        \"invoice_date\": \"2026-10-06\"\n      },\n      \"shipbob_invoice_id\": 90000004,\n      \"invoiced_status\": \"true\",\n      \"attribution_status\": \"attributed\",\n      \"unattributed_reason\": null,\n      \"unattributed_reason_label\": null,\n      \"sales_order\": {\n        \"id\": 991,\n        \"sales_order_number\": \"SO-10421\"\n      },\n      \"sales_order_fulfillment_id\": 1402,\n      \"product\": null,\n      \"purchase_order\": null,\n      \"cost_entry_id\": null,\n      \"cost_allocation_id\": null,\n      \"cost_entry_type_name\": null,\n      \"financial_line_ids\": [\n        5501\n      ],\n      \"shipbob_url\": \"https://web.shipbob.com/app/Merchant/#/shipments/512345678\",\n      \"attributed_at\": \"2026-10-03T14:02:11.000000Z\",\n      \"created_at\": \"2026-10-03T13:58:40.000000Z\",\n      \"updated_at\": \"2026-10-03T14:02:11.000000Z\",\n      \"is_voided\": false,\n      \"voided_at\": null\n    }\n  ],\n  \"first_page_url\": \"https://sku6.test/api/...?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://sku6.test/api/...?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://sku6.test/api/...\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"Get Bill","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/{transaction}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills",":transaction"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"transaction","value":"","description":"The transaction ID."}]},"description":"Get one ShipBob billing transaction with its raw ShipBob payload, the attribution resolution trace (which lookup matched or failed and why), its fee mapping and the financial lines it wrote.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nEach financial line carries sales_order_line (id, sku, product_id, product_name) when the fee was split onto specific order lines of a multi-shipment order.\n\nFinancial line amounts are in the currency of the document they sit on (currency_code — the order's or PO's), with amount_in_tenant_currency alongside; tenant_currency_code names the tenant currency.\n\n`financial_lines` lists what the bill wrote. For a freight charge it includes the fulfillment's single freight line, flagged `is_shared_freight: true` — that line carries the net of every freight charge on the shipment, not this charge alone.\n\n`attribution_trace[].detail` names fulfillments and sales orders by their numbers (SO-SB-1003.2, SO-SB-1003), never internal ids."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/{transaction}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills",":transaction"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"transaction","value":"","description":"The transaction ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"Success\",\n  \"data\": {\n    \"id\": 123,\n    \"integration_instance_id\": 4,\n    \"shipbob_transaction_id\": \"01FAKEEXAMPLE0000000000001\",\n    \"charge_type\": \"Per Pick Fee\",\n    \"fee_name\": \"Per Pick Fee\",\n    \"family\": \"fulfillment\",\n    \"treatment\": \"order_fee\",\n    \"transaction_type\": \"Charge\",\n    \"charge_date\": \"2026-10-03\",\n    \"charge_start_date\": \"2026-10-03\",\n    \"charge_end_date\": \"2026-10-03\",\n    \"amount\": 0.35,\n    \"currency_code\": \"USD\",\n    \"formatted_amount\": \"$0.35\",\n    \"amount_in_tenant_currency\": 0.35,\n    \"tax_amount\": 0,\n    \"reference_type\": \"Shipment\",\n    \"reference_id\": \"512345678\",\n    \"inventory_id\": null,\n    \"po_id\": null,\n    \"store_order_id\": \"154164\",\n    \"master_order_id\": 401055055,\n    \"sales_channel\": \"Shopify\",\n    \"order_type\": \"D2C\",\n    \"fulfillment_center\": \"Cicero (IL)\",\n    \"invoice\": {\n      \"id\": 77,\n      \"shipbob_invoice_id\": 90000004,\n      \"invoice_date\": \"2026-10-06\"\n    },\n    \"shipbob_invoice_id\": 90000004,\n    \"invoiced_status\": \"true\",\n    \"attribution_status\": \"attributed\",\n    \"unattributed_reason\": null,\n    \"unattributed_reason_label\": null,\n    \"sales_order\": {\n      \"id\": 991,\n      \"sales_order_number\": \"SO-10421\"\n    },\n    \"sales_order_fulfillment_id\": 1402,\n    \"product\": null,\n    \"purchase_order\": null,\n    \"cost_entry_id\": null,\n    \"cost_allocation_id\": null,\n    \"cost_entry_type_name\": null,\n    \"financial_line_ids\": [\n      5501\n    ],\n    \"shipbob_url\": \"https://web.shipbob.com/app/Merchant/#/shipments/512345678\",\n    \"attributed_at\": \"2026-10-03T14:02:11.000000Z\",\n    \"created_at\": \"2026-10-03T13:58:40.000000Z\",\n    \"updated_at\": \"2026-10-03T14:02:11.000000Z\",\n    \"json_object\": {\n      \"transaction_id\": \"01FAKEEXAMPLE0000000000001\",\n      \"amount\": 0.35,\n      \"currency_code\": \"USD\",\n      \"charge_date\": \"2026-10-03\",\n      \"invoiced_status\": true,\n      \"transaction_fee\": \"Per Pick Fee\",\n      \"invoice_id\": 90000004,\n      \"invoice_type\": \"Shipping\",\n      \"reference_id\": \"512345678\",\n      \"reference_type\": \"Shipment\",\n      \"transaction_type\": \"Charge\",\n      \"fulfillment_center\": \"Cicero (IL)\",\n      \"taxes\": [],\n      \"additional_details\": {\n        \"TrackingId\": \"\",\n        \"Comment\": \"\"\n      },\n      \"po_id\": null,\n      \"store_order_id\": \"154164\",\n      \"master_order_id\": \"401055055\",\n      \"sales_channel\": \"Shopify\",\n      \"order_type\": \"D2C\"\n    },\n    \"attribution_trace\": [\n      {\n        \"step\": \"shipment\",\n        \"matched\": true,\n        \"detail\": \"Shipment 512345678 → fulfillment SO-SB-1003.2 → sales order SO-SB-1003.\"\n      }\n    ],\n    \"fee_mapping\": {\n      \"id\": 12,\n      \"family\": \"fulfillment\",\n      \"treatment\": \"order_fee\",\n      \"financial_line_type_id\": 31,\n      \"financial_line_type_name\": \"3PL Fulfillment Fee\",\n      \"cost_entry_type_name\": null,\n      \"version\": 1\n    },\n    \"financial_lines\": [\n      {\n        \"id\": 5501,\n        \"link_type\": \"SalesOrder\",\n        \"link_id\": 991,\n        \"description\": \"ShipBob Per Pick Fee · shipment 512345678\",\n        \"amount\": 0.35,\n        \"proration_strategy\": \"quantity_based\",\n        \"allocate_to_id\": null,\n        \"sales_order_line\": null,\n        \"currency_code\": \"USD\",\n        \"amount_in_tenant_currency\": 0.35,\n        \"is_shared_freight\": false\n      },\n      {\n        \"sales_order_line\": {\n          \"id\": 4412,\n          \"sku\": \"MEL-SERUM-30\",\n          \"product_id\": 88,\n          \"product_name\": \"Mello Serum 30ml\"\n        },\n        \"id\": 5502,\n        \"link_type\": \"SalesOrder\",\n        \"link_id\": 991,\n        \"description\": \"ShipBob Per Pick Fee · shipment 512345678\",\n        \"amount\": 0.23,\n        \"proration_strategy\": \"specific_line\",\n        \"allocate_to_id\": 4412,\n        \"currency_code\": \"USD\",\n        \"amount_in_tenant_currency\": 0.23,\n        \"is_shared_freight\": false\n      }\n    ],\n    \"is_voided\": false,\n    \"voided_at\": null,\n    \"tenant_currency_code\": \"USD\"\n  }\n}"}]},{"name":"Void Bill","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/{transaction}/void","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills",":transaction","void"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"transaction","value":"","description":"The transaction ID."}]},"description":"Mark a billing transaction void — use it when ShipBob dropped a charge, since no later sync will report the removal. Its attribution is removed (order or purchase order cost lines deleted, its monthly cost entry rebuilt, billed freight re-applied to the fulfillment, the order's margin recalculated) and it is left excluded with reason manually_voided. Re-syncs and re-attribution never touch a voided transaction until it is un-voided. Returns the updated bill.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nReturns 422 when the bill's fee was capitalised into a purchase order whose receipts fall in a locked accounting period."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/{transaction}/void","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills",":transaction","void"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"transaction","value":"","description":"The transaction ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"Success\",\n  \"data\": {\n    \"id\": 123,\n    \"integration_instance_id\": 4,\n    \"shipbob_transaction_id\": \"01FAKEEXAMPLE0000000000001\",\n    \"charge_type\": \"Per Pick Fee\",\n    \"fee_name\": \"Per Pick Fee\",\n    \"family\": \"fulfillment\",\n    \"treatment\": \"order_fee\",\n    \"transaction_type\": \"Charge\",\n    \"charge_date\": \"2026-10-03\",\n    \"charge_start_date\": \"2026-10-03\",\n    \"charge_end_date\": \"2026-10-03\",\n    \"amount\": 0.35,\n    \"currency_code\": \"USD\",\n    \"formatted_amount\": \"$0.35\",\n    \"amount_in_tenant_currency\": 0.35,\n    \"tax_amount\": 0,\n    \"reference_type\": \"Shipment\",\n    \"reference_id\": \"512345678\",\n    \"inventory_id\": null,\n    \"po_id\": null,\n    \"store_order_id\": \"154164\",\n    \"master_order_id\": 401055055,\n    \"sales_channel\": \"Shopify\",\n    \"order_type\": \"D2C\",\n    \"fulfillment_center\": \"Cicero (IL)\",\n    \"invoice\": {\n      \"id\": 77,\n      \"shipbob_invoice_id\": 90000004,\n      \"invoice_date\": \"2026-10-06\"\n    },\n    \"shipbob_invoice_id\": 90000004,\n    \"invoiced_status\": \"true\",\n    \"attribution_status\": \"excluded\",\n    \"unattributed_reason\": \"manually_voided\",\n    \"unattributed_reason_label\": \"Voided manually\",\n    \"sales_order\": null,\n    \"sales_order_fulfillment_id\": null,\n    \"product\": null,\n    \"purchase_order\": null,\n    \"cost_entry_id\": null,\n    \"cost_allocation_id\": null,\n    \"cost_entry_type_name\": null,\n    \"financial_line_ids\": [],\n    \"shipbob_url\": \"https://web.shipbob.com/app/Merchant/#/shipments/512345678\",\n    \"attributed_at\": \"2026-10-03T14:02:11.000000Z\",\n    \"created_at\": \"2026-10-03T13:58:40.000000Z\",\n    \"updated_at\": \"2026-10-03T14:02:11.000000Z\",\n    \"json_object\": {\n      \"transaction_id\": \"01FAKEEXAMPLE0000000000001\",\n      \"amount\": 0.35,\n      \"currency_code\": \"USD\",\n      \"charge_date\": \"2026-10-03\",\n      \"invoiced_status\": true,\n      \"transaction_fee\": \"Per Pick Fee\",\n      \"invoice_id\": 90000004,\n      \"invoice_type\": \"Shipping\",\n      \"reference_id\": \"512345678\",\n      \"reference_type\": \"Shipment\",\n      \"transaction_type\": \"Charge\",\n      \"fulfillment_center\": \"Cicero (IL)\",\n      \"taxes\": [],\n      \"additional_details\": {\n        \"TrackingId\": \"\",\n        \"Comment\": \"\"\n      },\n      \"po_id\": null,\n      \"store_order_id\": \"154164\",\n      \"master_order_id\": \"401055055\",\n      \"sales_channel\": \"Shopify\",\n      \"order_type\": \"D2C\"\n    },\n    \"attribution_trace\": [\n      {\n        \"step\": \"shipment\",\n        \"matched\": true,\n        \"detail\": \"Shipment 512345678 → fulfillment #1402 → sales order #991.\"\n      },\n      {\n        \"step\": \"void\",\n        \"matched\": false,\n        \"detail\": \"Voided manually by Jane Doe on 2026-10-08; excluded from attribution.\"\n      }\n    ],\n    \"fee_mapping\": {\n      \"id\": 12,\n      \"family\": \"fulfillment\",\n      \"treatment\": \"order_fee\",\n      \"financial_line_type_id\": 31,\n      \"financial_line_type_name\": \"3PL Fulfillment Fee\",\n      \"cost_entry_type_name\": null,\n      \"version\": 1\n    },\n    \"financial_lines\": [],\n    \"is_voided\": true,\n    \"voided_at\": \"2026-10-08T12:00:00.000000Z\",\n    \"tenant_currency_code\": \"USD\"\n  },\n  \"message\": \"Billing transaction voided\"\n}"}]},{"name":"Unvoid Bill","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/{transaction}/unvoid","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills",":transaction","unvoid"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"transaction","value":"","description":"The transaction ID."}]},"description":"Undo a manual void: the billing transaction returns to pending and is attributed again immediately. Returns the updated bill.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/{transaction}/unvoid","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills",":transaction","unvoid"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"transaction","value":"","description":"The transaction ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"Success\",\n  \"data\": {\n    \"id\": 123,\n    \"integration_instance_id\": 4,\n    \"shipbob_transaction_id\": \"01FAKEEXAMPLE0000000000001\",\n    \"charge_type\": \"Per Pick Fee\",\n    \"fee_name\": \"Per Pick Fee\",\n    \"family\": \"fulfillment\",\n    \"treatment\": \"order_fee\",\n    \"transaction_type\": \"Charge\",\n    \"charge_date\": \"2026-10-03\",\n    \"charge_start_date\": \"2026-10-03\",\n    \"charge_end_date\": \"2026-10-03\",\n    \"amount\": 0.35,\n    \"currency_code\": \"USD\",\n    \"formatted_amount\": \"$0.35\",\n    \"amount_in_tenant_currency\": 0.35,\n    \"tax_amount\": 0,\n    \"reference_type\": \"Shipment\",\n    \"reference_id\": \"512345678\",\n    \"inventory_id\": null,\n    \"po_id\": null,\n    \"store_order_id\": \"154164\",\n    \"master_order_id\": 401055055,\n    \"sales_channel\": \"Shopify\",\n    \"order_type\": \"D2C\",\n    \"fulfillment_center\": \"Cicero (IL)\",\n    \"invoice\": {\n      \"id\": 77,\n      \"shipbob_invoice_id\": 90000004,\n      \"invoice_date\": \"2026-10-06\"\n    },\n    \"shipbob_invoice_id\": 90000004,\n    \"invoiced_status\": \"true\",\n    \"attribution_status\": \"attributed\",\n    \"unattributed_reason\": null,\n    \"unattributed_reason_label\": null,\n    \"sales_order\": {\n      \"id\": 991,\n      \"sales_order_number\": \"SO-10421\"\n    },\n    \"sales_order_fulfillment_id\": 1402,\n    \"product\": null,\n    \"purchase_order\": null,\n    \"cost_entry_id\": null,\n    \"cost_allocation_id\": null,\n    \"cost_entry_type_name\": null,\n    \"financial_line_ids\": [\n      5501\n    ],\n    \"shipbob_url\": \"https://web.shipbob.com/app/Merchant/#/shipments/512345678\",\n    \"attributed_at\": \"2026-10-03T14:02:11.000000Z\",\n    \"created_at\": \"2026-10-03T13:58:40.000000Z\",\n    \"updated_at\": \"2026-10-03T14:02:11.000000Z\",\n    \"json_object\": {\n      \"transaction_id\": \"01FAKEEXAMPLE0000000000001\",\n      \"amount\": 0.35,\n      \"currency_code\": \"USD\",\n      \"charge_date\": \"2026-10-03\",\n      \"invoiced_status\": true,\n      \"transaction_fee\": \"Per Pick Fee\",\n      \"invoice_id\": 90000004,\n      \"invoice_type\": \"Shipping\",\n      \"reference_id\": \"512345678\",\n      \"reference_type\": \"Shipment\",\n      \"transaction_type\": \"Charge\",\n      \"fulfillment_center\": \"Cicero (IL)\",\n      \"taxes\": [],\n      \"additional_details\": {\n        \"TrackingId\": \"\",\n        \"Comment\": \"\"\n      },\n      \"po_id\": null,\n      \"store_order_id\": \"154164\",\n      \"master_order_id\": \"401055055\",\n      \"sales_channel\": \"Shopify\",\n      \"order_type\": \"D2C\"\n    },\n    \"attribution_trace\": [\n      {\n        \"step\": \"shipment\",\n        \"matched\": true,\n        \"detail\": \"Shipment 512345678 → fulfillment #1402 → sales order #991.\"\n      }\n    ],\n    \"fee_mapping\": {\n      \"id\": 12,\n      \"family\": \"fulfillment\",\n      \"treatment\": \"order_fee\",\n      \"financial_line_type_id\": 31,\n      \"financial_line_type_name\": \"3PL Fulfillment Fee\",\n      \"cost_entry_type_name\": null,\n      \"version\": 1\n    },\n    \"financial_lines\": [\n      {\n        \"id\": 5501,\n        \"link_type\": \"SalesOrder\",\n        \"link_id\": 991,\n        \"description\": \"ShipBob Per Pick Fee · shipment 512345678\",\n        \"amount\": 0.35,\n        \"proration_strategy\": \"quantity_based\",\n        \"allocate_to_id\": null,\n        \"currency_code\": \"USD\",\n        \"amount_in_tenant_currency\": 0.35,\n        \"is_shared_freight\": false\n      }\n    ],\n    \"is_voided\": false,\n    \"voided_at\": null,\n    \"tenant_currency_code\": \"USD\"\n  },\n  \"message\": \"Billing transaction restored\"\n}"}]},{"name":"Get Bills Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/summary","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills","summary"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Totals for a charge-date range: per-fee rows in the same shape as the Trackstar bills summary (charge_type, total_amount, bill_count, currency_code, plus share of the billed cost), the attribution breakdown (on orders, on products, on purchase orders, pending, unattributed, excluded payments, unattributed by reason, billed-vs-estimated freight variance) and the unattributed work queue grouped by reason and sales channel with a suggested action.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nQuery: from / to (date, Y-m-d; default the last 30 days; to must be on or after from).\n\ncurrency_code is the tenant currency every total, attribution amount and share is expressed in (per-fee total_amount stays in that fee's billing currency_code).\n\n`unconverted` lists bills whose charge-date exchange rate is not published yet, grouped by billed currency (`currency_code`, `amount`, `count`). They are left out of every tenant-currency total, and an item's `amount_in_tenant_currency` is `null` when none of its bills could be converted yet.\n\n`attribution.attributed_cost_entries` is the charges attributed to a cost entry alone (e.g. a return fee split across the returned products), so `attributed_orders + attributed_products + attributed_purchase_orders + attributed_cost_entries + unattributed + pending = total`. Fee-type `items` count billed rows only: excluded rows of a billed fee (a voided charge) are left out of `total_amount`/`bill_count` and reported as `left_out_count` (`voided_count` says how many are voided). A fee whose rows are all excluded (payments) keeps its amount with `is_excluded: true`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/summary","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills","summary"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"Success\",\n  \"data\": {\n    \"currency_code\": \"USD\",\n    \"from\": \"2026-09-08\",\n    \"to\": \"2026-10-08\",\n    \"total\": 7846.18,\n    \"transaction_count\": 4631,\n    \"items\": [\n      {\n        \"charge_type\": \"Shipping\",\n        \"total_amount\": 6770,\n        \"bill_count\": 883,\n        \"currency_code\": \"USD\",\n        \"amount_in_tenant_currency\": 6770,\n        \"is_excluded\": false,\n        \"share\": 0.8628\n      },\n      {\n        \"charge_type\": \"Payment\",\n        \"total_amount\": -8579,\n        \"bill_count\": 27,\n        \"currency_code\": \"USD\",\n        \"amount_in_tenant_currency\": -8579,\n        \"is_excluded\": true,\n        \"share\": 0\n      }\n    ],\n    \"attribution\": {\n      \"attributed_orders\": 2867.31,\n      \"attributed_products\": 377.05,\n      \"attributed_purchase_orders\": 0,\n      \"pending\": 61.4,\n      \"unattributed\": 4540.42,\n      \"excluded\": -8579,\n      \"unattributed_by_reason\": {\n        \"order_not_imported\": 4508.24,\n        \"product_unmapped\": 12\n      },\n      \"freight_variance\": 14.22\n    },\n    \"unconverted\": [\n      {\n        \"currency_code\": \"EUR\",\n        \"amount\": 42.5,\n        \"count\": 3\n      }\n    ],\n    \"unattributed_groups\": [\n      {\n        \"reason\": \"order_not_imported\",\n        \"reason_label\": \"Order not imported\",\n        \"sales_channel\": \"Ulta\",\n        \"count\": 463,\n        \"amount\": 3110.4,\n        \"suggested_action\": \"connect_sales_channel\"\n      },\n      {\n        \"reason\": \"product_unmapped\",\n        \"reason_label\": \"Product unmapped\",\n        \"sales_channel\": null,\n        \"count\": 31,\n        \"amount\": 12,\n        \"suggested_action\": \"map_products\"\n      }\n    ],\n    \"billing_enabled\": true,\n    \"last_synced_at\": \"2026-10-08T10:15:00.000000Z\",\n    \"last_error\": null,\n    \"needs_review_count\": 3\n  }\n}"}]},{"name":"List Bill Fee Types","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/fee-types","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills","fee-types"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"List every ShipBob fee name seen in billing transactions or the fee mapping (the equivalent of Trackstar's charge types).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/fee-types","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills","fee-types"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"Success\",\n  \"data\": [\n    \"Outsourced Fuel Surcharge\",\n    \"Packaging Fee\",\n    \"Per Pick Fee\",\n    \"Shipping\",\n    \"Warehousing Fee\"\n  ]\n}"}]},{"name":"List Bill Reference Types","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/reference-types","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills","reference-types"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"List the reference types billing transactions point at (the equivalent of Trackstar's object types).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/reference-types","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills","reference-types"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"Success\",\n  \"data\": [\n    \"Return\",\n    \"Shipment\"\n  ]\n}"}]},{"name":"List Bill Sales Channels","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/sales-channels","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills","sales-channels"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"List the distinct ShipBob sales channels (e.g. Shopify, Ulta, Nordstrom) that appear on billing transactions — the options for the sales_channel filter.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/sales-channels","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills","sales-channels"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"Success\",\n  \"data\": [\n    \"Nordstrom\",\n    \"Shopify\",\n    \"Ulta\"\n  ]\n}"}]},{"name":"Sync Bills","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Queue a background sync of ShipBob billing transactions and invoices, followed by attribution of new, changed and still-pending fees. Without from_date the run re-reads the last 14 days (or 48 hours before the previous sync, whichever is earlier); with from_date it backfills from that date (never before the billing start date) in 7-day windows. Returns the tracked job id. The ShipBob token needs the billing_read scope.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- from_date (date, optional, not in the future) — backfill start date.\n\nReturns 422 when billing sync is disabled for the instance.\n\nA run that reaches the page limit records where it stopped and the next run continues from there (the last-synced checkpoint only moves once everything was read). While such an unfinished window is outstanding, a backfill whose `from` date is later than it finishes the outstanding window first (that window runs to today, so it covers the requested range); a backfill from an earlier date re-reads and replaces it.","body":{"mode":"raw","raw":"{\n  \"from_date\": \"2026-09-01\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/sync","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills","sync"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 8812\n  },\n  \"message\": \"ShipBob billing sync started\"\n}"}]},{"name":"Re-attribute Bills","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/reattribute","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills","reattribute"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Queue re-attribution of ShipBob billing transactions: the given transaction ids, every transaction of one fee name, or (when neither is sent) every transaction since the billing start date. Previous targets are removed and rebuilt, so a re-run never double counts. Returns the tracked job id.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- transaction_ids (integer[], optional, max 5000) — local transaction ids.\n- fee_name (string, optional, max 150) — re-attribute one fee.\n\nReturns 422 when billing sync is disabled for the instance.","body":{"mode":"raw","raw":"{\n  \"transaction_ids\": [\n    123,\n    124,\n    130\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/bills/reattribute","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","bills","reattribute"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 8813\n  },\n  \"message\": \"ShipBob billing re-attribution started\"\n}"}]},{"name":"List Billing Invoices","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/invoices","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","invoices"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"List ShipBob invoices with reconciliation columns: ShipBob's amount, the sum and count of member transactions, how much is attributed / unattributed / excluded / pending, the variance, a reconciliation status (reconciled when |variance| < 0.01, variance, or awaiting_transactions), a shipbob_url deep link, and accounting_posting: status (posted, draft, pending, blocked, not_posted, or disabled when the instance does not post invoices or the invoice is a Payment), label (\"Off\" when posting is switched off for the instance; \"Not posted\" for a Payment, which is never posted), error (why posting is blocked, e.g. a locked accounting period), journal_entry_id, journal_entry_status, posted_at and reference.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFilterable columns:\n- Text: invoice_type, reconciliation_status, currency_code.\n- Numeric: id, shipbob_invoice_id, amount, transactions_total.\n- Date: invoice_date.\n- filter[search] matches id, invoice # or type.\n\nFiltering uses the canonical filter tree: send filter_groups as base64-encoded JSON {\"conjunction\":\"and\",\"children\":[{\"type\":\"condition\",\"condition\":{\"column\":\"invoice_type\",\"operator\":\"is\",\"value\":\"Shipping\"}}]}, or flat filter[column.operator]=value params. Text operators: contains, does_not_contain, is, is_not, is_one_of, is_not_one_of, starts_with, ends_with, is_empty, is_not_empty. Numeric operators: is, is_not, greater_than, less_than, greater_than_or_equal, less_than_or_equal, between, is_empty, is_not_empty. Date operators: is, before, after, on_or_before, on_or_after, between, today, past_week, past_month, past_days, etc.\n\nAllowed sorts: id, shipbob_invoice_id, invoice_date, amount, transactions_total, invoice_type (default -invoice_date).\nPagination: page, per_page (default 10)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/invoices","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","invoices"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 77,\n      \"integration_instance_id\": 4,\n      \"shipbob_invoice_id\": 90000004,\n      \"invoice_date\": \"2026-10-06\",\n      \"invoice_type\": \"Shipping\",\n      \"amount\": 1812.2,\n      \"currency_code\": \"USD\",\n      \"running_balance\": 1812.2,\n      \"transactions_total\": 1812.2,\n      \"transaction_count\": 412,\n      \"attributed\": 1640,\n      \"unattributed\": 172.2,\n      \"excluded\": 0,\n      \"pending\": 0,\n      \"variance\": 0,\n      \"reconciliation_status\": \"reconciled\",\n      \"reconciliation_status_label\": \"Reconciled\",\n      \"is_payment\": false,\n      \"created_at\": \"2026-10-07T02:00:12.000000Z\",\n      \"updated_at\": \"2026-10-07T04:00:12.000000Z\",\n      \"accounting_posting\": {\n        \"status\": \"posted\",\n        \"label\": \"Posted\",\n        \"error\": null,\n        \"journal_entry_id\": 5521,\n        \"journal_entry_status\": \"posted\",\n        \"posted_at\": \"2026-10-07T04:00:12.000000Z\",\n        \"reference\": \"90000004\"\n      },\n      \"shipbob_url\": \"https://web.shipbob.com/app/Merchant/#/invoices/90000004\"\n    }\n  ],\n  \"first_page_url\": \"https://sku6.test/api/...?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://sku6.test/api/...?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://sku6.test/api/...\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"List Fee Mappings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/fee-mappings","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","fee-mappings"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"List the instance's ShipBob fee mapping: every fee name with its family, treatment (order_freight, order_fee, return_fee, product_storage, po_receiving, unattributed, exclude), the cost line type or monthly cost entry type it lands on, a needs_review flag for newly seen fees, and 30-day usage.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\n`usage_30d` counts the last 30 days of charges for the fee, leaving out voided charges."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/fee-mappings","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","fee-mappings"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"Success\",\n  \"data\": [\n    {\n      \"id\": 12,\n      \"integration_instance_id\": 4,\n      \"fee_name\": \"Per Pick Fee\",\n      \"charge_type\": \"Per Pick Fee\",\n      \"family\": \"fulfillment\",\n      \"family_label\": \"Fulfillment\",\n      \"treatment\": \"order_fee\",\n      \"treatment_label\": \"Order fee\",\n      \"financial_line_type_id\": 31,\n      \"financial_line_type_name\": \"3PL Fulfillment Fee\",\n      \"cost_entry_type_name\": null,\n      \"needs_review\": false,\n      \"is_seeded\": true,\n      \"version\": 1,\n      \"usage_30d\": {\n        \"count\": 162,\n        \"amount\": 74\n      },\n      \"created_at\": \"2026-10-07T02:00:12.000000Z\",\n      \"updated_at\": \"2026-10-07T02:00:12.000000Z\"\n    }\n  ]\n}"}]},{"name":"Update Fee Mapping","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/fee-mappings/{mapping}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","fee-mappings",":mapping"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"mapping","value":"","description":"The mapping ID."}]},"description":"Re-point one ShipBob fee. Saving bumps the mapping version and, when billing is enabled and the fee has transactions, queues re-attribution of that fee's transactions. Returns the saved mapping plus how many transactions (and how much) the change touches.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nRequest Body:\n- treatment (string, required) — order_freight, order_fee, return_fee, product_storage, po_receiving, unattributed or exclude.\n- financial_line_type_id (integer, optional, nullable) — a COST financial line type.\n- cost_entry_type_name (string, optional, nullable, max 100) — monthly cost entry type for storage fees.\n\n`order_freight` is accepted for Shipping-family fees only; any other family returns 422 with `errors.treatment`.","body":{"mode":"raw","raw":"{\n  \"treatment\": \"order_fee\",\n  \"financial_line_type_id\": 31,\n  \"cost_entry_type_name\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/fee-mappings/{mapping}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","fee-mappings",":mapping"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"mapping","value":"","description":"The mapping ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"Success\",\n  \"data\": {\n    \"mapping\": {\n      \"id\": 12,\n      \"integration_instance_id\": 4,\n      \"fee_name\": \"Per Pick Fee\",\n      \"charge_type\": \"Per Pick Fee\",\n      \"family\": \"fulfillment\",\n      \"family_label\": \"Fulfillment\",\n      \"treatment\": \"order_fee\",\n      \"treatment_label\": \"Order fee\",\n      \"financial_line_type_id\": 31,\n      \"financial_line_type_name\": \"3PL Fulfillment Fee\",\n      \"cost_entry_type_name\": null,\n      \"needs_review\": false,\n      \"is_seeded\": true,\n      \"version\": 1,\n      \"usage_30d\": {\n        \"count\": 162,\n        \"amount\": 74\n      },\n      \"created_at\": \"2026-10-07T02:00:12.000000Z\",\n      \"updated_at\": \"2026-10-07T02:00:12.000000Z\"\n    },\n    \"affected_transactions\": 162,\n    \"affected_amount\": 74,\n    \"tracked_job_log_id\": 8814\n  }\n}"}]},{"name":"List Webhook Subscriptions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/webhooks/subscriptions","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","webhooks","subscriptions"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"List the ShipBob webhook subscriptions registered for the instance (one per topic).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\n`webhook_url` is the address ShipBob delivers to; SKU.io creates its routing token when missing. If no token can be assigned, `webhook_url` is null and `webhook_url_error` says why."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/webhooks/subscriptions","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","webhooks","subscriptions"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 91,\n      \"integration_instance_id\": 12,\n      \"shipbob_subscription_id\": 55501,\n      \"topic\": \"order_shipped\",\n      \"url\": \"https://your-domain.example.com/webhooks/shipbob/whk_abc123\",\n      \"status\": \"active\",\n      \"last_error\": null,\n      \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n      \"updated_at\": \"2024-06-01T14:22:05.000000Z\"\n    }\n  ],\n  \"webhook_url_error\": null\n}"}]},{"name":"Subscribe Webhooks","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/webhooks/subscribe","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","webhooks","subscribe"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Queues a background job to (re-)subscribe to every ShipBob webhook topic for this instance, then returns immediately. The instance's existing subscription rows are flagged `pending`; poll List Webhook Subscriptions to see each topic settle to `active` or `failed`.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\n`webhook_url` is the address ShipBob delivers to; SKU.io creates its routing token when missing. If no token can be assigned, `webhook_url` is null and `webhook_url_error` says why.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/webhooks/subscribe","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","webhooks","subscribe"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 91,\n      \"integration_instance_id\": 12,\n      \"shipbob_subscription_id\": 55501,\n      \"topic\": \"order_shipped\",\n      \"url\": \"https://your-domain.example.com/webhooks/shipbob/whk_abc123\",\n      \"status\": \"pending\",\n      \"last_error\": null,\n      \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n      \"updated_at\": \"2024-06-01T14:22:05.000000Z\"\n    }\n  ],\n  \"webhook_url\": \"https://your-domain.example.com/webhooks/shipbob/whk_abc123\",\n  \"webhook_url_error\": null,\n  \"message\": \"Re-subscribing to every ShipBob notification — refresh in a moment to see the result.\"\n}"}]},{"name":"List Webhook Events","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/webhooks/events","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","webhooks","events"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]},"description":"Paginated, filterable, sortable log of inbound ShipBob webhook events for the instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nDefault page size is 10.\n\nTopics are ShipBob's dotted topic names (`order.shipped`, `order.shipment.delivered`, `return.updated`, `wro.completed`, …); filter with `filter[topic]=order.shipped`. Payloads are not included in the list unless `include_payload=1` — use Get Webhook Event for one event's payload."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/webhooks/events","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","webhooks","events"],"variable":[{"key":"instance","value":"","description":"The instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 7781,\n      \"integration_instance_id\": 12,\n      \"dedup_key\": \"order.shipped:559900:2024-06-01\",\n      \"topic\": \"order.shipped\",\n      \"shipbob_order_id\": 448899123,\n      \"shipbob_shipment_id\": 559900,\n      \"signature_valid\": true,\n      \"processed\": true,\n      \"processed_at\": \"2024-06-01T14:22:05.000000Z\",\n      \"error_message\": null,\n      \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n      \"updated_at\": \"2024-06-01T14:22:05.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 4,\n  \"per_page\": 10,\n  \"total\": 38,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"Get Webhook Event","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/webhooks/events/{event}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","webhooks","events",":event"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"event","value":"","description":"The event ID."}]},"description":"A single stored ShipBob webhook event with its full payload — what the events list's \"View payload\" dialog loads. Scoped to the instance; `{event}` is the event's SKU.io id.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nErrors:\n- 404 — the event does not belong to this instance."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/webhooks/events/{event}","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","webhooks","events",":event"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"event","value":"","description":"The event ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 7781,\n    \"integration_instance_id\": 12,\n    \"dedup_key\": \"order.shipped:559900:2024-06-01\",\n    \"topic\": \"order.shipped\",\n    \"shipbob_order_id\": 448899123,\n    \"shipbob_shipment_id\": 559900,\n    \"signature_valid\": true,\n    \"processed\": true,\n    \"processed_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"error_message\": null,\n    \"created_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"updated_at\": \"2024-06-01T14:22:05.000000Z\",\n    \"payload\": {\n      \"id\": 448899123,\n      \"reference_id\": \"SO-100245\",\n      \"status\": \"Fulfilled\",\n      \"shipments\": [\n        {\n          \"id\": 559900,\n          \"status\": \"Completed\",\n          \"tracking\": {\n            \"tracking_number\": \"1Z999AA10123456784\",\n            \"carrier\": \"UPS\"\n          }\n        }\n      ]\n    }\n  }\n}"}]},{"name":"Replay Webhook Event","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/webhooks/events/{event}/replay","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","webhooks","events",":event","replay"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"event","value":"","description":"The event ID."}]},"description":"Re-process a stored webhook event idempotently (queued in the background).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shipbob/{instance}/webhooks/events/{event}/replay","host":["{{baseUrl}}"],"path":["api","shipbob",":instance","webhooks","events",":event","replay"],"variable":[{"key":"instance","value":"","description":"The instance ID."},{"key":"event","value":"","description":"The event ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Webhook event replay queued\"\n}"}]}]},{"name":"ShipRush","item":[{"name":"Create Integration Instance","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances","host":["{{baseUrl}}"],"path":["api","shiprush","instances"]},"description":"Creates and connects a new ShipRush integration instance for the current account, then returns it together with a live dashboard summary and the ONE-TIME plaintext store credentials (username, password, feed URL) the merchant pastes into ShipRush. The credentials block is shown only once — it is never returned again on Get.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nBody fields:\n- name (string, required): a label for this connection. Max 255 characters.\n- environment (string, optional): \"production\" or \"sandbox\". Defaults to \"production\".\n- shipping_api_token (string, optional, nullable): a ShipRush Shipping API token that enables the token-gated tracking/service-name sync. Max 255 characters.\n- record_shipping_costs (boolean, optional): capture ShipRush shipping costs against orders. Defaults to true.\n\nReturns 201 with the created instance, its dashboard summary, and the one-time credentials block. Returns 422 with field errors when validation fails.\n\nShipRush is single-instance: a tenant can connect one ShipRush account. Creating a second instance returns 422.\n\nAuthentication: Requires a Bearer token.","body":{"mode":"raw","raw":"{\n  \"name\": \"ShipRush Main\",\n  \"environment\": \"production\",\n  \"shipping_api_token\": \"sr_live_9f2c1a7b4e6d0c3a8f5b2e91\",\n  \"record_shipping_costs\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shiprush/instances","host":["{{baseUrl}}"],"path":["api","shiprush","instances"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"name\": \"ShipRush Main\",\n    \"environment\": \"production\",\n    \"is_sandbox\": false,\n    \"is_automatic_sync_enabled\": true,\n    \"record_shipping_costs\": true,\n    \"tracking_refresh_enabled\": true,\n    \"sync_start_date\": null,\n    \"sync_status\": \"idle\",\n    \"feed_page_size\": 100,\n    \"store_username\": \"sku_shiprush_ab12cd34\",\n    \"has_shipping_api_token\": true,\n    \"last_poll_at\": null,\n    \"last_poll_order_count\": null,\n    \"last_synced_at\": null,\n    \"dashboard\": {\n      \"awaiting_shipment\": 0,\n      \"on_hold\": 0,\n      \"error\": 0,\n      \"never_exported\": 0,\n      \"shipped_last_7_days\": 0,\n      \"writeback_errors\": 0,\n      \"shipment_writeback_errors\": 0,\n      \"event_writeback_errors\": 0,\n      \"unmapped_services\": 0,\n      \"total_orders\": 0,\n      \"last_poll_at\": null,\n      \"last_poll_order_count\": null,\n      \"has_recent_credential_failure\": false,\n      \"has_shipping_api_token\": true\n    },\n    \"created_at\": \"2026-07-08T21:31:36+00:00\",\n    \"updated_at\": \"2026-07-08T21:31:36+00:00\"\n  },\n  \"dashboard\": {\n    \"awaiting_shipment\": 0,\n    \"on_hold\": 0,\n    \"error\": 0,\n    \"never_exported\": 0,\n    \"shipped_last_7_days\": 0,\n    \"writeback_errors\": 0,\n    \"shipment_writeback_errors\": 0,\n    \"event_writeback_errors\": 0,\n    \"unmapped_services\": 0,\n    \"total_orders\": 0,\n    \"last_poll_at\": null,\n    \"last_poll_order_count\": null,\n    \"has_recent_credential_failure\": false,\n    \"has_shipping_api_token\": true\n  },\n  \"credentials\": {\n    \"store_username\": \"sku_shiprush_ab12cd34\",\n    \"store_password\": \"pW7x9Qk2Lm4vR8sT\",\n    \"feed_url\": \"https://acme.sku.io/api/webhooks/shiprush/abc123def456\"\n  }\n}"}]},{"name":"Get Integration Instance","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Returns a single ShipRush integration instance together with a live dashboard summary: per-status order counts, feed-health flags, writeback errors, unmapped service count, poll observability, and whether a Shipping API token is set. The plaintext store password is NEVER returned here — only the store username and the has-token flag.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n\nReturns 404 when the instance does not exist.\n\nAuthentication: Requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"name\": \"ShipRush Main\",\n    \"environment\": \"production\",\n    \"is_sandbox\": false,\n    \"is_automatic_sync_enabled\": true,\n    \"record_shipping_costs\": true,\n    \"tracking_refresh_enabled\": true,\n    \"sync_start_date\": \"2026-01-01T00:00:00+00:00\",\n    \"sync_status\": \"idle\",\n    \"feed_page_size\": 100,\n    \"store_username\": \"sku_shiprush_ab12cd34\",\n    \"has_shipping_api_token\": true,\n    \"last_poll_at\": \"2026-07-08T01:30:00+00:00\",\n    \"last_poll_order_count\": 14,\n    \"last_synced_at\": \"2026-07-08T01:30:00+00:00\",\n    \"dashboard\": {\n      \"awaiting_shipment\": 27,\n      \"on_hold\": 3,\n      \"error\": 1,\n      \"never_exported\": 2,\n      \"shipped_last_7_days\": 96,\n      \"writeback_errors\": 0,\n      \"shipment_writeback_errors\": 0,\n      \"event_writeback_errors\": 0,\n      \"unmapped_services\": 4,\n      \"total_orders\": 1540,\n      \"last_poll_at\": \"2026-07-08T01:30:00+00:00\",\n      \"last_poll_order_count\": 14,\n      \"has_recent_credential_failure\": false,\n      \"has_shipping_api_token\": true\n    },\n    \"created_at\": \"2026-03-10T09:00:00+00:00\",\n    \"updated_at\": \"2026-07-08T01:30:01+00:00\"\n  },\n  \"dashboard\": {\n    \"awaiting_shipment\": 27,\n    \"on_hold\": 3,\n    \"error\": 1,\n    \"never_exported\": 2,\n    \"shipped_last_7_days\": 96,\n    \"writeback_errors\": 0,\n    \"shipment_writeback_errors\": 0,\n    \"event_writeback_errors\": 0,\n    \"unmapped_services\": 4,\n    \"total_orders\": 1540,\n    \"last_poll_at\": \"2026-07-08T01:30:00+00:00\",\n    \"last_poll_order_count\": 14,\n    \"has_recent_credential_failure\": false,\n    \"has_shipping_api_token\": true\n  }\n}"}]},{"name":"Update Integration Instance","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Updates settings on an existing ShipRush integration instance. Every field is optional (the settings UI POSTs partial per-tab payloads) — send only what should change; omitted fields are left untouched. Accepts PUT or PATCH. Returns the updated instance with a refreshed dashboard summary.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n\nBody fields (all optional):\n- name (string): connection label. Max 255 characters.\n- environment (string): \"production\" or \"sandbox\".\n- shipping_api_token (string, nullable): rotate or clear the ShipRush Shipping API token. Max 255 characters. Send null to clear it.\n- record_shipping_costs (boolean): capture ShipRush shipping costs against orders.\n- sync_start_date (date, nullable): earliest order date ShipRush can pull from the export feed (YYYY-MM-DD). When empty, the feed uses a rolling 90-day window.\n- tracking_refresh_enabled (boolean): whether the scheduled tracking refresh (every 4 hours) runs for this instance. Only has an effect when a Shipping API token is configured. Default true.\n\nReturns 422 with field errors when validation fails; 404 when the instance does not exist.\n\nAuthentication: Requires a Bearer token.","body":{"mode":"raw","raw":"{\n  \"name\": \"ShipRush Main (Primary)\",\n  \"environment\": \"production\",\n  \"shipping_api_token\": \"sr_live_1a2b3c4d5e6f7g8h9i0j1k2l\",\n  \"record_shipping_costs\": true,\n  \"sync_start_date\": \"2026-01-01\",\n  \"tracking_refresh_enabled\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"name\": \"ShipRush Main (Primary)\",\n    \"environment\": \"production\",\n    \"is_sandbox\": false,\n    \"is_automatic_sync_enabled\": true,\n    \"record_shipping_costs\": true,\n    \"tracking_refresh_enabled\": true,\n    \"sync_start_date\": \"2026-01-01T00:00:00+00:00\",\n    \"sync_status\": \"idle\",\n    \"feed_page_size\": 100,\n    \"store_username\": \"sku_shiprush_ab12cd34\",\n    \"has_shipping_api_token\": true,\n    \"last_poll_at\": \"2026-07-08T01:30:00+00:00\",\n    \"last_poll_order_count\": 14,\n    \"last_synced_at\": \"2026-07-08T01:30:00+00:00\",\n    \"dashboard\": {\n      \"awaiting_shipment\": 27,\n      \"on_hold\": 3,\n      \"error\": 1,\n      \"never_exported\": 2,\n      \"shipped_last_7_days\": 96,\n      \"writeback_errors\": 0,\n      \"shipment_writeback_errors\": 0,\n      \"event_writeback_errors\": 0,\n      \"unmapped_services\": 4,\n      \"total_orders\": 1540,\n      \"last_poll_at\": \"2026-07-08T01:30:00+00:00\",\n      \"last_poll_order_count\": 14,\n      \"has_recent_credential_failure\": false,\n      \"has_shipping_api_token\": true\n    },\n    \"created_at\": \"2026-03-10T09:00:00+00:00\",\n    \"updated_at\": \"2026-07-08T02:05:11+00:00\"\n  },\n  \"dashboard\": {\n    \"awaiting_shipment\": 27,\n    \"on_hold\": 3,\n    \"error\": 1,\n    \"never_exported\": 2,\n    \"shipped_last_7_days\": 96,\n    \"writeback_errors\": 0,\n    \"shipment_writeback_errors\": 0,\n    \"event_writeback_errors\": 0,\n    \"unmapped_services\": 4,\n    \"total_orders\": 1540,\n    \"last_poll_at\": \"2026-07-08T01:30:00+00:00\",\n    \"last_poll_order_count\": 14,\n    \"has_recent_credential_failure\": false,\n    \"has_shipping_api_token\": true\n  }\n}"}]},{"name":"Delete Integration Instance","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Disconnects and removes a ShipRush integration instance. The teardown runs asynchronously: this endpoint records the disconnect and hands the cascade cleanup (feed orders, shipments, webhook events, and related records) to a background job, then returns immediately with no content.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n\nReturns 204 No Content on success. Returns 404 when the instance does not exist.\n\nAuthentication: Requires a Bearer token."},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{}"}]},{"name":"Get Dashboard Metrics","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/dashboard","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","dashboard"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Returns just the live dashboard summary for a ShipRush integration instance — the lightweight refresh endpoint for the dashboard widget, without the full instance settings payload.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n\nThe response \"data\" object contains:\n- awaiting_shipment / on_hold / error: per-status feed-order counts.\n- never_exported: feed rows created but never polled out by ShipRush yet (feed health).\n- shipped_last_7_days: parcels shipped in the trailing week.\n- writeback_errors: shipment writebacks that need review (failed processing, unknown orders, labels printed for orders cancelled in SKU.io). Credential failures are counted separately.\n- shipment_writeback_errors: the part of writeback_errors recorded on shipments (e.g. a label printed for an order cancelled in SKU.io) — review and re-process these from the shipments list.\n- event_writeback_errors: the part of writeback_errors recorded only on webhook events (e.g. a shipment for an order ShipRush did not get from the feed) — list them with filter[error.is_not_empty]=1 on the webhook events endpoint.\n- unmapped_services: carrier/service pairs with no SKU shipping method mapped.\n- total_orders: total feed rows for the instance.\n- last_poll_at / last_poll_order_count: when ShipRush last polled the export feed and how many rows it took.\n- has_recent_credential_failure: whether ShipRush was rejected with bad store credentials in the last 24h and has NOT polled successfully since.\n- credential_failures_last_24h: number of store-credential rejections in the last 24 hours.\n- has_shipping_api_token: whether a Shipping API token is configured (enables token-gated sync).\n\nReturns 404 when the instance does not exist.\n\nAuthentication: Requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/dashboard","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","dashboard"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"awaiting_shipment\": 27,\n    \"on_hold\": 3,\n    \"error\": 1,\n    \"never_exported\": 2,\n    \"shipped_last_7_days\": 96,\n    \"writeback_errors\": 0,\n    \"shipment_writeback_errors\": 0,\n    \"event_writeback_errors\": 0,\n    \"unmapped_services\": 4,\n    \"total_orders\": 1540,\n    \"last_poll_at\": \"2026-07-08T01:30:00+00:00\",\n    \"last_poll_order_count\": 14,\n    \"has_recent_credential_failure\": false,\n    \"credential_failures_last_24h\": 0,\n    \"has_shipping_api_token\": true\n  }\n}"}]},{"name":"Get Daily Order Activity","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/dashboard/daily-activity","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","dashboard","daily-activity"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Returns a 30-day daily series for a ShipRush integration instance: how many orders entered the export feed and how many parcels shipped on each day. Days are calendar days in the account's configured timezone, oldest first, and days with no activity are returned with zeros so the series is always continuous (30 entries). Voided shipments are excluded.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n\nEach entry in \"data\" contains:\n- date (string, YYYY-MM-DD): the calendar day.\n- orders (integer): orders added to the export feed that day.\n- shipments (integer): parcels shipped that day.\n\nReturns 404 when the instance does not exist.\n\nAuthentication: Requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/dashboard/daily-activity","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","dashboard","daily-activity"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"date\": \"2026-09-08\",\n      \"orders\": 0,\n      \"shipments\": 0\n    },\n    {\n      \"date\": \"2026-09-09\",\n      \"orders\": 4,\n      \"shipments\": 3\n    },\n    {\n      \"date\": \"2026-09-10\",\n      \"orders\": 7,\n      \"shipments\": 5\n    },\n    {\n      \"date\": \"2026-10-07\",\n      \"orders\": 12,\n      \"shipments\": 9\n    }\n  ]\n}"}]},{"name":"Regenerate Credentials","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/regenerate-credentials","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","regenerate-credentials"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Regenerates the SS store credentials the merchant uses to authenticate the ShipRush export feed. The previous username/password stop working immediately, so the merchant must paste the new values into ShipRush. Returns the new ONE-TIME plaintext pair (username, password) plus the feed URL — shown only once.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n\nNo request body is required.\n\nReturns 200 with the new credentials; 404 when the instance does not exist.\n\nAuthentication: Requires a Bearer token.","body":{"mode":"raw","raw":"","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/regenerate-credentials","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","regenerate-credentials"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"store_username\": \"sku_shiprush_9zx8yw76\",\n    \"store_password\": \"Bk3Rm7Ct2Xq9Ld5P\",\n    \"feed_url\": \"https://acme.sku.io/api/webhooks/shiprush/abc123def456\"\n  },\n  \"message\": \"New store credentials generated — copy them now, they are shown only once.\"\n}"}]},{"name":"Get Setup Readiness","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/readiness","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","readiness"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Returns the checklist of steps a ShipRush instance needs before it can fulfill orders.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n\nThe response \"data\" object contains:\n- ready: true once every required step is complete.\n- items: one entry per step with key (warehouse, connection, shipping_methods), label, description, required, complete, action_label and action_route.\n\nRequired steps: at least one warehouse routed to the instance, and ShipRush has polled the order feed at least once. Mapping carrier services is recommended but never blocks fulfillment.\n\nReturns 404 when the instance does not exist.\n\nAuthentication: Requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/readiness","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","readiness"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"ready\": false,\n    \"items\": [\n      {\n        \"key\": \"warehouse\",\n        \"label\": \"Route a warehouse to ShipRush\",\n        \"description\": \"Fulfillment orders are dispatched to ShipRush from the warehouses routed to it. Until one is routed, orders can only reach ShipRush by picking it by hand on each fulfillment order.\",\n        \"required\": true,\n        \"complete\": false,\n        \"action_label\": \"Route warehouses\",\n        \"action_route\": \"/integrations/ship-rush/5/warehouses\"\n      },\n      {\n        \"key\": \"connection\",\n        \"label\": \"Connect ShipRush to the order feed\",\n        \"description\": \"Paste the feed URL, username and password into ShipRush Web → Settings → Webstores → Custom SS Style Store. This completes when ShipRush polls the feed for the first time.\",\n        \"required\": true,\n        \"complete\": true,\n        \"action_label\": \"View feed credentials\",\n        \"action_route\": \"/integrations/ship-rush/5/settings\"\n      },\n      {\n        \"key\": \"shipping_methods\",\n        \"label\": \"Map ShipRush carrier services\",\n        \"description\": \"2 carrier service(s) posted back by ShipRush are not mapped to a SKU shipping method, so those shipments are recorded without one.\",\n        \"required\": false,\n        \"complete\": false,\n        \"action_label\": \"Map services\",\n        \"action_route\": \"/integrations/ship-rush/5/shipping-methods\"\n      }\n    ]\n  }\n}"}]},{"name":"List Warehouse Routing","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/warehouses","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","warehouses"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Lists every warehouse you can ship from (your own direct warehouses and 3PL warehouses) and whether its fulfillment orders route to this ShipRush instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n\nEach row contains id, name, type, linked (routes to this instance), order_fulfillment (the warehouse's current dispatch target, e.g. manual or shiprush), and routed_integration_instance_id / routed_integration_instance_name when the warehouse routes to an integration.\n\nAuthentication: Requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/warehouses","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","warehouses"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\",\n      \"type\": \"direct\",\n      \"linked\": true,\n      \"order_fulfillment\": \"shiprush\",\n      \"routed_integration_instance_id\": 5,\n      \"routed_integration_instance_name\": \"ShipRush E2E Warehouse\"\n    },\n    {\n      \"id\": 3,\n      \"name\": \"Overflow Warehouse\",\n      \"type\": \"direct\",\n      \"linked\": false,\n      \"order_fulfillment\": \"manual\",\n      \"routed_integration_instance_id\": null,\n      \"routed_integration_instance_name\": null\n    }\n  ]\n}"}]},{"name":"Update Warehouse Routing","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/warehouses/{warehouse}","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","warehouses",":warehouse"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"warehouse","value":"","description":"The warehouse ID."}]},"description":"Routes a warehouse's fulfillment orders to this ShipRush instance, or stops routing them.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameters:\n- instance: the numeric ID of the ShipRush integration instance.\n- warehouse: the numeric ID of the warehouse.\n\nBody:\n- linked (boolean, required): true routes the warehouse to ShipRush; false returns it to manual fulfillment (only when it currently routes to this instance).\n\nRouting changes only the warehouse's dispatch target — its type and stock are unchanged. Supplier, virtual and marketplace-fulfilled warehouses cannot be routed (422).\n\nThe response returns the updated warehouse row in \"data\" and the refreshed setup checklist in \"readiness\".\n\nAuthentication: Requires a Bearer token.","body":{"mode":"raw","raw":"{\n  \"linked\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/warehouses/{warehouse}","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","warehouses",":warehouse"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"warehouse","value":"","description":"The warehouse ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Main Warehouse\",\n    \"type\": \"direct\",\n    \"linked\": true,\n    \"order_fulfillment\": \"shiprush\",\n    \"routed_integration_instance_id\": 5,\n    \"routed_integration_instance_name\": \"ShipRush E2E Warehouse\"\n  },\n  \"readiness\": {\n    \"ready\": true,\n    \"items\": [\n      {\n        \"key\": \"warehouse\",\n        \"label\": \"Route a warehouse to ShipRush\",\n        \"description\": \"Fulfillment orders are dispatched to ShipRush from the warehouses routed to it. Until one is routed, orders can only reach ShipRush by picking it by hand on each fulfillment order.\",\n        \"required\": true,\n        \"complete\": true,\n        \"action_label\": \"Route warehouses\",\n        \"action_route\": \"/integrations/ship-rush/5/warehouses\"\n      },\n      {\n        \"key\": \"connection\",\n        \"label\": \"Connect ShipRush to the order feed\",\n        \"description\": \"Paste the feed URL, username and password into ShipRush Web → Settings → Webstores → Custom SS Style Store. This completes when ShipRush polls the feed for the first time.\",\n        \"required\": true,\n        \"complete\": true,\n        \"action_label\": \"View feed credentials\",\n        \"action_route\": \"/integrations/ship-rush/5/settings\"\n      },\n      {\n        \"key\": \"shipping_methods\",\n        \"label\": \"Map ShipRush carrier services\",\n        \"description\": \"2 carrier service(s) posted back by ShipRush are not mapped to a SKU shipping method, so those shipments are recorded without one.\",\n        \"required\": false,\n        \"complete\": false,\n        \"action_label\": \"Map services\",\n        \"action_route\": \"/integrations/ship-rush/5/shipping-methods\"\n      }\n    ]\n  }\n}"}]},{"name":"List Orders","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Paginated, filterable list of ShipRush feed orders for the integration instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nEvery filter[&lt;column>] parameter also accepts an operator suffix as filter[&lt;column>.&lt;operator>]=&lt;value>; a bare filter[&lt;column>]=&lt;value> is treated as \"is\".\n\n- Text columns support: contains, does_not_contain, is, is_not, is_one_of, is_not_one_of, starts_with, does_not_start_with, ends_with, does_not_end_with, is_empty, is_not_empty.\n- Numeric columns support: is, is_not, is_one_of, is_not_one_of, greater_than, less_than, greater_than_or_equal, less_than_or_equal, between, is_empty, is_not_empty.\n- Date columns support: is, is_not, before, after, on_or_before, on_or_after, between, today, yesterday, tomorrow, past_week, past_month, past_year, next_week, next_month, next_year, days_ago, days_from_now, past_days, next_days, is_empty, is_not_empty.\n\nfilter[search] does a fuzzy partial match across order_number, requested_shipping_method, and status_message.\n\nSort with the sort parameter (prefix - for descending). Allowed sort fields: id, order_number, status, requested_shipping_method, order_total, order_date, last_modified_at, first_exported_at, last_exported_at, export_count, shipped_at, created_at, updated_at. The default sort is -id (newest first).\n\nResults are paginated; per_page defaults to 10.\n\nAuthentication: requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 4821,\n      \"integration_instance_id\": 3,\n      \"order_number\": \"SO-100482\",\n      \"status\": \"awaiting_shipment\",\n      \"status_label\": \"Awaiting Shipment\",\n      \"status_message\": null,\n      \"requested_shipping_method\": \"Standard Shipping\",\n      \"ship_to\": {\n        \"name\": \"Jane Doe\",\n        \"address1\": \"1 Smith Street\",\n        \"city\": \"Richmond\",\n        \"state\": \"VIC\",\n        \"postal_code\": \"3121\",\n        \"country\": \"AU\"\n      },\n      \"order_total\": 49,\n      \"order_date\": \"2026-07-01T00:00:00+00:00\",\n      \"last_modified_at\": \"2026-07-08T04:15:00+00:00\",\n      \"first_exported_at\": \"2026-07-02T06:00:00+00:00\",\n      \"last_exported_at\": \"2026-07-08T06:00:00+00:00\",\n      \"export_count\": 3,\n      \"never_exported\": false,\n      \"shipped_at\": null,\n      \"is_shipped\": false,\n      \"is_cancelled\": false,\n      \"external_url\": \"https://www.shiprush.com/orders/SO-100482\",\n      \"sku_fulfillment_order_id\": 55210,\n      \"sales_order_id\": 8842,\n      \"sales_order_number\": \"SO-100482\",\n      \"link_type_label\": \"Fulfillment Order\",\n      \"link_id\": 55210,\n      \"link_reference\": \"SO-100482\",\n      \"link_route\": \"/orders/sales-orders/8842?tab=fulfillment\",\n      \"created_at\": \"2026-07-01T22:40:00+00:00\",\n      \"updated_at\": \"2026-07-08T04:15:01+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 5,\n  \"per_page\": 10,\n  \"total\": 48,\n  \"from\": 1,\n  \"to\": 10,\n  \"first_page_url\": \"{{protocol}}://{{domain}}/api/shiprush/instances/3/orders?page=1\",\n  \"last_page_url\": \"{{protocol}}://{{domain}}/api/shiprush/instances/3/orders?page=5\",\n  \"next_page_url\": \"{{protocol}}://{{domain}}/api/shiprush/instances/3/orders?page=2\",\n  \"prev_page_url\": null,\n  \"path\": \"{{protocol}}://{{domain}}/api/shiprush/instances/3/orders\"\n}"}]},{"name":"Sync Orders","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/sync","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders","sync"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Rebuild the export feed as a tracked background job. ShipRush has no order-ingest API — the local row IS the feed — so this re-snapshots every open row and bumps its last_modified_at so ShipRush re-imports it on its next poll; there is no outbound HTTP. The response returns the tracked job log id, which you can poll for progress.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nNo request body is required.\n\nAuthentication: requires a Bearer token.","body":{"mode":"raw","raw":"","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/sync","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders","sync"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 4321\n  },\n  \"message\": \"Feed rebuild started — track progress in the job tray.\"\n}"}]},{"name":"Get Sync Info","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/sync-info","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders","sync-info"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Returns the sync-window info for the orders refresh flow: when the feed was last synced and the configured sync start date. Both are ISO-8601 timestamps or null.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n\nAuthentication: requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/sync-info","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders","sync-info"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"last_synced_at\": \"2026-07-08T01:30:00+00:00\",\n    \"sync_start_date\": \"2026-01-01T00:00:00+00:00\"\n  }\n}"}]},{"name":"Get Order","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/{order}?include_json=1","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders",":order"],"query":[{"key":"include_json","value":"1","description":"Set to 1 to include the stored export snapshot as json_object in the response.","disabled":true}],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"order","value":"4821","description":"Internal ShipRush order id (scoped to the instance)."}]},"description":"Returns the full details for a single ShipRush feed order scoped to the integration instance. Includes the raw status (machine value + human label), requested shipping method, ship-to, totals, export/feed-health timestamps (first/last exported, export count, never_exported), a \"View in ShipRush\" portal link (external_url), the tracking rows minted by shipnotify writebacks (shipments), and cross-link fields to the originating SKU FulfillmentOrder / parent Sales Order.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nWhen include_json=1 is supplied, the response also includes the stored export snapshot as json_object.\n\nAn order id / instance mismatch returns 404.\n\nAuthentication: requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/{order}?include_json=1","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders",":order"],"query":[{"key":"include_json","value":"1","description":"Set to 1 to include the stored export snapshot as json_object in the response.","disabled":true}],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"order","value":"4821","description":"Internal ShipRush order id (scoped to the instance)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 4821,\n    \"integration_instance_id\": 3,\n    \"order_number\": \"SO-100482\",\n    \"status\": \"shipped\",\n    \"status_label\": \"Shipped\",\n    \"status_message\": null,\n    \"requested_shipping_method\": \"Standard Shipping\",\n    \"ship_to\": {\n      \"name\": \"Jane Doe\",\n      \"address1\": \"1 Smith Street\",\n      \"city\": \"Richmond\",\n      \"state\": \"VIC\",\n      \"postal_code\": \"3121\",\n      \"country\": \"AU\"\n    },\n    \"order_total\": 49,\n    \"order_date\": \"2026-07-01T00:00:00+00:00\",\n    \"last_modified_at\": \"2026-07-08T04:15:00+00:00\",\n    \"first_exported_at\": \"2026-07-02T06:00:00+00:00\",\n    \"last_exported_at\": \"2026-07-08T06:00:00+00:00\",\n    \"export_count\": 3,\n    \"never_exported\": false,\n    \"shipped_at\": \"2026-07-08T03:40:00+00:00\",\n    \"is_shipped\": true,\n    \"is_cancelled\": false,\n    \"external_url\": \"https://www.shiprush.com/orders/SO-100482\",\n    \"sku_fulfillment_order_id\": 55210,\n    \"sales_order_id\": 8842,\n    \"sales_order_number\": \"SO-100482\",\n    \"link_type_label\": \"Fulfillment Order\",\n    \"link_id\": 55210,\n    \"link_reference\": \"SO-100482\",\n    \"link_route\": \"/orders/sales-orders/8842?tab=fulfillment\",\n    \"shipments\": [\n      {\n        \"id\": 771,\n        \"tracking_number\": \"DZ1000456789\",\n        \"tracking_url\": \"https://www.shiprush.com/tracking/DZ1000456789\",\n        \"carrier_code\": \"AusPost\",\n        \"service_code\": \"Parcel Post\",\n        \"shipment_cost\": 8.95,\n        \"ship_date\": \"2026-07-08T03:40:00+00:00\",\n        \"voided\": false,\n        \"sku_sales_order_fulfillment_id\": 256917\n      }\n    ],\n    \"json_object\": {\n      \"OrderNumber\": \"SO-100482\",\n      \"OrderStatus\": \"awaiting_shipment\",\n      \"Items\": [\n        {\n          \"SKU\": \"ACME-WIDGET-01\",\n          \"Name\": \"Acme Widget\",\n          \"Quantity\": 2,\n          \"UnitPrice\": 24.5\n        }\n      ]\n    },\n    \"created_at\": \"2026-07-01T22:40:00+00:00\",\n    \"updated_at\": \"2026-07-08T04:15:01+00:00\"\n  }\n}"}]},{"name":"Get Raw Snapshot","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/{order}/raw","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders",":order","raw"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"order","value":"4821","description":"Internal ShipRush order id (scoped to the instance)."}]},"description":"Returns the stored export snapshot for the order — the last-exported feed payload ShipRush polls out — wrapped for the shared raw-data viewer as { data: { product }, fetched_at }.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n- order: internal ShipRush order id.\n\nAn order id / instance mismatch returns 404.\n\nAuthentication: requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/{order}/raw","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders",":order","raw"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"order","value":"4821","description":"Internal ShipRush order id (scoped to the instance)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product\": {\n      \"OrderNumber\": \"SO-100482\",\n      \"OrderStatus\": \"awaiting_shipment\",\n      \"OrderDate\": \"2026-07-01T00:00:00\",\n      \"ShipTo\": {\n        \"Name\": \"Jane Doe\",\n        \"Address1\": \"1 Smith Street\",\n        \"City\": \"Richmond\",\n        \"State\": \"VIC\",\n        \"PostalCode\": \"3121\",\n        \"Country\": \"AU\"\n      },\n      \"Items\": [\n        {\n          \"SKU\": \"ACME-WIDGET-01\",\n          \"Name\": \"Acme Widget\",\n          \"Quantity\": 2,\n          \"UnitPrice\": 24.5\n        }\n      ]\n    }\n  },\n  \"fetched_at\": \"2026-07-08T04:20:00+00:00\"\n}"}]},{"name":"Re-export Order","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/{order}/re-export","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders",":order","re-export"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"order","value":"4821","description":"Internal ShipRush order id (scoped to the instance)."}]},"description":"Manually re-export the order: re-snapshots the payload and bumps last_modified_at so the row re-appears in ShipRush's next poll. Returns the updated order. Runs inline (no background job).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n- order: internal ShipRush order id.\n\nNo request body is required. An order id / instance mismatch returns 404.\n\nAuthentication: requires a Bearer token.","body":{"mode":"raw","raw":"","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/{order}/re-export","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders",":order","re-export"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"order","value":"4821","description":"Internal ShipRush order id (scoped to the instance)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 4821,\n    \"integration_instance_id\": 3,\n    \"order_number\": \"SO-100482\",\n    \"status\": \"awaiting_shipment\",\n    \"status_label\": \"Awaiting Shipment\",\n    \"status_message\": null,\n    \"requested_shipping_method\": \"Standard Shipping\",\n    \"ship_to\": {\n      \"name\": \"Jane Doe\",\n      \"address1\": \"1 Smith Street\",\n      \"city\": \"Richmond\",\n      \"state\": \"VIC\",\n      \"postal_code\": \"3121\",\n      \"country\": \"AU\"\n    },\n    \"order_total\": 49,\n    \"order_date\": \"2026-07-01T00:00:00+00:00\",\n    \"last_modified_at\": \"2026-07-08T05:00:00+00:00\",\n    \"first_exported_at\": \"2026-07-02T06:00:00+00:00\",\n    \"last_exported_at\": \"2026-07-08T06:00:00+00:00\",\n    \"export_count\": 3,\n    \"never_exported\": false,\n    \"shipped_at\": null,\n    \"is_shipped\": false,\n    \"is_cancelled\": false,\n    \"external_url\": \"https://www.shiprush.com/orders/SO-100482\",\n    \"sku_fulfillment_order_id\": 55210,\n    \"sales_order_id\": 8842,\n    \"sales_order_number\": \"SO-100482\",\n    \"link_type_label\": \"Fulfillment Order\",\n    \"link_id\": 55210,\n    \"link_reference\": \"SO-100482\",\n    \"link_route\": \"/orders/sales-orders/8842?tab=fulfillment\",\n    \"created_at\": \"2026-07-01T22:40:00+00:00\",\n    \"updated_at\": \"2026-07-08T05:00:00+00:00\"\n  }\n}"}]},{"name":"Cancel Order","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/{order}/cancel","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders",":order","cancel"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"order","value":"4821","description":"Internal ShipRush order id (scoped to the instance)."}]},"description":"Cancel the feed row (operator \"Cancel\" row action). Marks the order cancelled and bumps last_modified_at so ShipRush observes the cancellation and drops it on its next poll (passive propagation). Returns the updated order.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n- order: internal ShipRush order id.\n\nNo request body is required. An order id / instance mismatch returns 404.\n\nAuthentication: requires a Bearer token.","body":{"mode":"raw","raw":"","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/{order}/cancel","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders",":order","cancel"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"order","value":"4821","description":"Internal ShipRush order id (scoped to the instance)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 4821,\n    \"integration_instance_id\": 3,\n    \"order_number\": \"SO-100482\",\n    \"status\": \"cancelled\",\n    \"status_label\": \"Cancelled\",\n    \"status_message\": \"Cancelled by operator\",\n    \"requested_shipping_method\": \"Standard Shipping\",\n    \"ship_to\": {\n      \"name\": \"Jane Doe\",\n      \"address1\": \"1 Smith Street\",\n      \"city\": \"Richmond\",\n      \"state\": \"VIC\",\n      \"postal_code\": \"3121\",\n      \"country\": \"AU\"\n    },\n    \"order_total\": 49,\n    \"order_date\": \"2026-07-01T00:00:00+00:00\",\n    \"last_modified_at\": \"2026-07-08T05:10:00+00:00\",\n    \"first_exported_at\": \"2026-07-02T06:00:00+00:00\",\n    \"last_exported_at\": \"2026-07-08T06:00:00+00:00\",\n    \"export_count\": 3,\n    \"never_exported\": false,\n    \"shipped_at\": null,\n    \"is_shipped\": false,\n    \"is_cancelled\": true,\n    \"external_url\": \"https://www.shiprush.com/orders/SO-100482\",\n    \"sku_fulfillment_order_id\": 55210,\n    \"sales_order_id\": 8842,\n    \"sales_order_number\": \"SO-100482\",\n    \"link_type_label\": \"Fulfillment Order\",\n    \"link_id\": 55210,\n    \"link_reference\": \"SO-100482\",\n    \"link_route\": \"/orders/sales-orders/8842?tab=fulfillment\",\n    \"created_at\": \"2026-07-01T22:40:00+00:00\",\n    \"updated_at\": \"2026-07-08T05:10:00+00:00\"\n  }\n}"}]},{"name":"Get Order Activity","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/{order}/activity","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders",":order","activity"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"order","value":"4821","description":"Internal ShipRush order id (scoped to the instance)."}]},"description":"Returns the paginated activity-log feed for a single ShipRush order, newest-first — the audit trail of actions taken against the order (registered, re-exported, cancelled, shipped, etc.). Each entry carries the acting user's name (null for system-generated entries), a human description, the event name, and a properties bag of contextual data.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n- order: internal ShipRush order id.\n\nDefault page size is 15. An order id / instance mismatch returns 404.\n\nAuthentication: requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/{order}/activity","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders",":order","activity"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"order","value":"4821","description":"Internal ShipRush order id (scoped to the instance)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 14032,\n      \"description\": \"Order re-exported to ShipRush feed\",\n      \"event\": \"re_exported\",\n      \"properties\": {\n        \"integration_instance_id\": 3,\n        \"ship_rush_order_id\": 4821\n      },\n      \"causer_name\": \"Dana Ops\",\n      \"created_at\": \"2026-07-08T05:00:00+00:00\"\n    },\n    {\n      \"id\": 14001,\n      \"description\": \"Order registered for fulfillment\",\n      \"event\": \"registered\",\n      \"properties\": {\n        \"integration_instance_id\": 3,\n        \"sku_fulfillment_order_id\": 55210\n      },\n      \"causer_name\": null,\n      \"created_at\": \"2026-07-01T22:40:00+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 2,\n  \"from\": 1,\n  \"to\": 2\n}"}]},{"name":"Bulk Re-export Orders","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/bulk-re-export","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders","bulk-re-export"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Re-export several orders at once: bumps each open order's last-modified time so ShipRush re-imports it on its next poll. Shipped and cancelled orders are skipped.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nBody fields:\n- ids (array of integers, required unless apply_to_all is true): the order ids to act on. Ids belonging to another integration instance are ignored.\n- apply_to_all (boolean, optional): when true, act on every order matching filters instead of ids.\n- filters (object, optional): the same filter keys the List Orders endpoint accepts (e.g. order_number.contains, status.is), plus filter_groups for grouped conditions. Only used with apply_to_all.\n\nReturns counts: processed (acted on), skipped (the action does not apply to the row's state), failed (the action errored), and errors keyed by row id. Returns 422 when neither ids nor apply_to_all is sent.\n\nAuthentication: Requires a Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    27,\n    28,\n    31\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/bulk-re-export","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders","bulk-re-export"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"processed\": 2,\n    \"skipped\": 1,\n    \"failed\": 0,\n    \"errors\": []\n  },\n  \"message\": \"2 re-exported, 1 skipped.\"\n}"}]},{"name":"Bulk Cancel Orders","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/bulk-cancel","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders","bulk-cancel"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Cancel several orders at once so ShipRush drops them on its next poll. Orders that have already shipped cannot be recalled and are skipped, as are orders already cancelled.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nBody fields:\n- ids (array of integers, required unless apply_to_all is true): the order ids to act on. Ids belonging to another integration instance are ignored.\n- apply_to_all (boolean, optional): when true, act on every order matching filters instead of ids.\n- filters (object, optional): the same filter keys the List Orders endpoint accepts (e.g. order_number.contains, status.is), plus filter_groups for grouped conditions. Only used with apply_to_all.\n\nReturns counts: processed (acted on), skipped (the action does not apply to the row's state), failed (the action errored), and errors keyed by row id. Returns 422 when neither ids nor apply_to_all is sent.\n\nAuthentication: Requires a Bearer token.","body":{"mode":"raw","raw":"{\n  \"apply_to_all\": true,\n  \"filters\": {\n    \"status.is\": \"awaiting_shipment\",\n    \"order_number.contains\": \"SO-00\"\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/orders/bulk-cancel","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","orders","bulk-cancel"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"processed\": 5,\n    \"skipped\": 0,\n    \"failed\": 0,\n    \"errors\": []\n  },\n  \"message\": \"5 cancelled.\"\n}"}]},{"name":"List Shipments","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipments","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipments"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Paginated, filterable list of ShipRush shipments (one row per parcel) for the integration instance. Each shipment carries the carrier/service (free text as received), cost, tracking number and URL, ship date, voided flag, the writeback outcome (writeback_status / writeback_error), a \"View in ShipRush\" portal link, and a cross-link to the SalesOrderFulfillment it produced. Shipments belonging to another instance are not returned.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nEvery filter[&lt;column>] parameter also accepts an operator suffix as filter[&lt;column>.&lt;operator>]=&lt;value>; a bare filter[&lt;column>]=&lt;value> is treated as \"is\". filter[search] does a fuzzy partial match across tracking_number, external_shipment_id, carrier_code, and service_code.\n\nSort field. Prefix with - for descending. Allowed: id, external_shipment_id, tracking_number, carrier_code, service_code, shipment_cost, ship_date, voided, writeback_status, created_at, updated_at. Default: -id (newest first).\n\nResults are paginated; per_page defaults to 10.\n\nAuthenticate with a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipments","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipments"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 771,\n      \"integration_instance_id\": 3,\n      \"ship_rush_order_id\": 4821,\n      \"order_number\": \"SO-100482\",\n      \"external_shipment_id\": \"SR-SHP-88213345\",\n      \"tracking_number\": \"DZ1000456789\",\n      \"tracking_url\": \"https://www.shiprush.com/tracking/DZ1000456789\",\n      \"carrier_code\": \"AusPost\",\n      \"service_code\": \"Parcel Post\",\n      \"shipment_cost\": 8.95,\n      \"ship_date\": \"2026-07-08T03:40:00+00:00\",\n      \"voided\": false,\n      \"writeback_status\": \"processed\",\n      \"writeback_error\": null,\n      \"mapped_shipping_method\": \"Standard Shipping\",\n      \"sku_sales_order_fulfillment_id\": 256917,\n      \"link_type_label\": \"Sales Order Fulfillment\",\n      \"link_id\": 256917,\n      \"link_reference\": \"F-256917\",\n      \"link_route\": \"/orders/sales-orders/fulfillments/256917\",\n      \"created_at\": \"2026-07-08T03:40:05+00:00\",\n      \"updated_at\": \"2026-07-08T03:40:05+00:00\"\n    },\n    {\n      \"id\": 762,\n      \"integration_instance_id\": 3,\n      \"ship_rush_order_id\": 4799,\n      \"order_number\": \"SO-100455\",\n      \"external_shipment_id\": \"SR-SHP-88210012\",\n      \"tracking_number\": \"CP99001122334455\",\n      \"tracking_url\": \"https://www.shiprush.com/tracking/CP99001122334455\",\n      \"carrier_code\": \"CouriersPlease\",\n      \"service_code\": \"Express\",\n      \"shipment_cost\": 18.9,\n      \"ship_date\": \"2026-07-07T23:41:00+00:00\",\n      \"voided\": false,\n      \"writeback_status\": \"error\",\n      \"writeback_error\": \"Fulfillment order not found for exported order number.\",\n      \"mapped_shipping_method\": null,\n      \"sku_sales_order_fulfillment_id\": null,\n      \"link_type_label\": null,\n      \"link_id\": null,\n      \"link_reference\": null,\n      \"link_route\": null,\n      \"created_at\": \"2026-07-07T23:41:03+00:00\",\n      \"updated_at\": \"2026-07-07T23:41:03+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 10,\n  \"total\": 27,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"Sync Shipments","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipments/sync","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipments","sync"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Kick off the token-gated tracking/scan refresh as a tracked background job. Requires a Shipping API token on the instance — without one the job no-ops server-side (the pull model already records tracking at shipnotify). The response returns the tracked job log id, which you can poll for progress.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nNo request body is required.\n\nAuthenticate with a Bearer token.","body":{"mode":"raw","raw":"","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipments/sync","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipments","sync"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 90211\n  },\n  \"message\": \"Shipment sync started — track progress in the job tray.\"\n}"}]},{"name":"Get Sync Info","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipments/sync-info","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipments","sync-info"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Returns the sync-window info for the shipments refresh flow: when the instance was last synced and the configured sync start date. Both are ISO-8601 timestamps or null.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n\nAuthenticate with a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipments/sync-info","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipments","sync-info"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"last_synced_at\": \"2026-07-08T01:30:00+00:00\",\n    \"sync_start_date\": \"2026-01-01T00:00:00+00:00\"\n  }\n}"}]},{"name":"Get Shipment","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipments/{shipment}?include_json=1","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipments",":shipment"],"query":[{"key":"include_json","value":"1","description":"Set to 1 to include the stored shipnotify payload (json_object) in the response.","disabled":true}],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"shipment","value":"771","description":"The shipment id (scoped to the instance)."}]},"description":"Retrieve a single ShipRush shipment scoped to the instance. Returns 404 if the shipment does not exist or belongs to another instance. Pass include_json=1 to include the stored shipnotify payload.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthenticate with a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipments/{shipment}?include_json=1","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipments",":shipment"],"query":[{"key":"include_json","value":"1","description":"Set to 1 to include the stored shipnotify payload (json_object) in the response.","disabled":true}],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"shipment","value":"771","description":"The shipment id (scoped to the instance)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 771,\n    \"integration_instance_id\": 3,\n    \"ship_rush_order_id\": 4821,\n    \"order_number\": \"SO-100482\",\n    \"external_shipment_id\": \"SR-SHP-88213345\",\n    \"tracking_number\": \"DZ1000456789\",\n    \"tracking_url\": \"https://www.shiprush.com/tracking/DZ1000456789\",\n    \"carrier_code\": \"AusPost\",\n    \"service_code\": \"Parcel Post\",\n    \"shipment_cost\": 8.95,\n    \"ship_date\": \"2026-07-08T03:40:00+00:00\",\n    \"voided\": false,\n    \"writeback_status\": \"processed\",\n    \"writeback_error\": null,\n    \"mapped_shipping_method\": \"Standard Shipping\",\n    \"sku_sales_order_fulfillment_id\": 256917,\n    \"link_type_label\": \"Sales Order Fulfillment\",\n    \"link_id\": 256917,\n    \"link_reference\": \"F-256917\",\n    \"link_route\": \"/orders/sales-orders/fulfillments/256917\",\n    \"json_object\": {\n      \"OrderNumber\": \"SO-100482\",\n      \"TrackingNumber\": \"DZ1000456789\",\n      \"Carrier\": \"AusPost\",\n      \"Service\": \"Parcel Post\",\n      \"ShippingCharges\": 8.95,\n      \"ShipDate\": \"2026-07-08T03:40:00\"\n    },\n    \"created_at\": \"2026-07-08T03:40:05+00:00\",\n    \"updated_at\": \"2026-07-08T03:40:05+00:00\"\n  }\n}"}]},{"name":"Get Raw Snapshot","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipments/{shipment}/raw","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipments",":shipment","raw"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"shipment","value":"771","description":"The shipment id (scoped to the instance)."}]},"description":"Returns the stored shipnotify payload for the shipment — the exact writeback ShipRush POSTed — wrapped for the shared raw-data viewer as { data: { product }, fetched_at }.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n- shipment: the shipment id.\n\nA shipment id / instance mismatch returns 404.\n\nAuthenticate with a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipments/{shipment}/raw","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipments",":shipment","raw"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"shipment","value":"771","description":"The shipment id (scoped to the instance)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product\": {\n      \"OrderNumber\": \"SO-100482\",\n      \"TrackingNumber\": \"DZ1000456789\",\n      \"Carrier\": \"AusPost\",\n      \"Service\": \"Parcel Post\",\n      \"ShippingCharges\": 8.95,\n      \"ShipDate\": \"2026-07-08T03:40:00\"\n    }\n  },\n  \"fetched_at\": \"2026-07-08T04:20:00+00:00\"\n}"}]},{"name":"Re-process Shipment","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipments/{shipment}/re-process","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipments",":shipment","re-process"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"shipment","value":"762","description":"The shipment id (scoped to the instance)."}]},"description":"Re-process a failed shipment writeback. For a row in the error state this resets its writeback status to \"processed\" and clears the error so it drops off the error KPI. For a true re-mint of the fulfillment, replay the underlying shipnotify from the Webhooks tab instead. A row not in the error state is returned unchanged.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n- shipment: the shipment id.\n\nNo request body is required. A shipment id / instance mismatch returns 404.\n\nAuthenticate with a Bearer token.","body":{"mode":"raw","raw":"","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipments/{shipment}/re-process","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipments",":shipment","re-process"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"shipment","value":"762","description":"The shipment id (scoped to the instance)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 762,\n    \"integration_instance_id\": 3,\n    \"ship_rush_order_id\": 4799,\n    \"order_number\": \"SO-100455\",\n    \"external_shipment_id\": \"SR-SHP-88210012\",\n    \"tracking_number\": \"CP99001122334455\",\n    \"tracking_url\": \"https://www.shiprush.com/tracking/CP99001122334455\",\n    \"carrier_code\": \"CouriersPlease\",\n    \"service_code\": \"Express\",\n    \"shipment_cost\": 18.9,\n    \"ship_date\": \"2026-07-07T23:41:00+00:00\",\n    \"voided\": false,\n    \"writeback_status\": \"processed\",\n    \"writeback_error\": null,\n    \"sku_sales_order_fulfillment_id\": null,\n    \"link_type_label\": null,\n    \"link_id\": null,\n    \"link_reference\": null,\n    \"link_route\": null,\n    \"created_at\": \"2026-07-07T23:41:03+00:00\",\n    \"updated_at\": \"2026-07-08T05:20:00+00:00\"\n  }\n}"}]},{"name":"Get Shipment Activity","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipments/{shipment}/activity","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipments",":shipment","activity"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"shipment","value":"9310","description":"Internal ShipRush shipment id (scoped to the instance)."}]},"description":"Returns the paginated activity-log feed for a single ShipRush shipment, newest-first: every change to the shipment's tracking number, tracking URL, carrier, service, cost, ship date, voided flag, writeback status/error and linked fulfillment. Each entry carries the acting user's name (null for system changes), the event (created / updated), and a properties bag with the new values (attributes) and, for updates, the previous values (old).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameters:\n- instance: the numeric ID of the ShipRush integration instance.\n- shipment: internal ShipRush shipment id.\n\nDefault page size is 15. A shipment that belongs to a different instance returns 404.\n\nAuthentication: requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipments/{shipment}/activity","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipments",":shipment","activity"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"shipment","value":"9310","description":"Internal ShipRush shipment id (scoped to the instance)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 20511,\n      \"description\": \"updated\",\n      \"event\": \"updated\",\n      \"properties\": {\n        \"attributes\": {\n          \"writeback_status\": \"processed\",\n          \"writeback_error\": null,\n          \"sku_sales_order_fulfillment_id\": 88123\n        },\n        \"old\": {\n          \"writeback_status\": \"error\",\n          \"writeback_error\": \"Fulfillment order is cancelled\",\n          \"sku_sales_order_fulfillment_id\": null\n        }\n      },\n      \"causer_name\": \"Dana Ops\",\n      \"created_at\": \"2026-10-07T14:12:00+00:00\"\n    },\n    {\n      \"id\": 20490,\n      \"description\": \"created\",\n      \"event\": \"created\",\n      \"properties\": {\n        \"attributes\": {\n          \"tracking_number\": \"1Z999AA10123456784\",\n          \"tracking_url\": \"https://www.ups.com/track?tracknum=1Z999AA10123456784\",\n          \"carrier_code\": \"UPS\",\n          \"service_code\": \"Ground\",\n          \"shipment_cost\": \"12.48\",\n          \"ship_date\": \"2026-10-07T13:55:00.000000Z\",\n          \"voided\": false,\n          \"writeback_status\": \"error\",\n          \"writeback_error\": \"Fulfillment order is cancelled\",\n          \"sku_sales_order_fulfillment_id\": null\n        }\n      },\n      \"causer_name\": null,\n      \"created_at\": \"2026-10-07T13:55:04+00:00\"\n    }\n  ],\n  \"first_page_url\": \"https://acme.sku.io/api/shiprush/instances/3/shipments/9310/activity?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://acme.sku.io/api/shiprush/instances/3/shipments/9310/activity?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://acme.sku.io/api/shiprush/instances/3/shipments/9310/activity\",\n  \"per_page\": 15,\n  \"prev_page_url\": null,\n  \"to\": 2,\n  \"total\": 2\n}"}]},{"name":"Bulk Re-process Shipments","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipments/bulk-re-process","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipments","bulk-re-process"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Re-process several shipments at once: clears the writeback error on every selected shipment whose writeback failed. Shipments without a writeback error are skipped.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nBody fields:\n- ids (array of integers, required unless apply_to_all is true): the shipment ids to act on. Ids belonging to another integration instance are ignored.\n- apply_to_all (boolean, optional): when true, act on every shipment matching filters instead of ids.\n- filters (object, optional): the same filter keys the List Shipments endpoint accepts (e.g. writeback_status.is, carrier_code.is), plus filter_groups for grouped conditions. Only used with apply_to_all.\n\nReturns counts: processed (acted on), skipped (the action does not apply to the row's state), failed (the action errored), and errors keyed by row id. Returns 422 when neither ids nor apply_to_all is sent.\n\nAuthentication: Requires a Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    14,\n    15\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipments/bulk-re-process","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipments","bulk-re-process"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"processed\": 1,\n    \"skipped\": 1,\n    \"failed\": 0,\n    \"errors\": []\n  },\n  \"message\": \"1 re-processed, 1 skipped.\"\n}"}]},{"name":"List Shipping Methods","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipping-methods","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipping-methods"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Returns the full carrier/service → SKU shipping-method mapping set for the instance (not paginated — the set is small and fully loaded for the client-side editor), plus the SKU shipping-method options for the selector.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nThe response \"data\" object contains:\n- services: the mapping rows. Each carries carrier_code / service_code, is_default (wildcard fallback flag), source (observed / synced / manual), the mapped shipping_method_id, the resolved shipping_method_name, is_mapped, and last_synced_at.\n- sku_shipping_methods: the list of SKU shipping methods ({ id, name }) to map to.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n\nAuthentication: Requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipping-methods","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipping-methods"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"services\": [\n      {\n        \"id\": 91,\n        \"integration_instance_id\": 3,\n        \"carrier_code\": \"AusPost\",\n        \"service_code\": \"Parcel Post\",\n        \"is_default\": false,\n        \"source\": \"synced\",\n        \"shipping_method_id\": 12,\n        \"shipping_method_name\": \"Standard Shipping\",\n        \"is_mapped\": true,\n        \"last_synced_at\": \"2026-07-08T01:30:00+00:00\",\n        \"created_at\": \"2026-06-10T08:00:00+00:00\",\n        \"updated_at\": \"2026-06-28T15:30:00+00:00\"\n      },\n      {\n        \"id\": 92,\n        \"integration_instance_id\": 3,\n        \"carrier_code\": \"CouriersPlease\",\n        \"service_code\": \"Express\",\n        \"is_default\": false,\n        \"source\": \"observed\",\n        \"shipping_method_id\": null,\n        \"shipping_method_name\": null,\n        \"is_mapped\": false,\n        \"last_synced_at\": null,\n        \"created_at\": \"2026-06-10T08:00:00+00:00\",\n        \"updated_at\": \"2026-06-10T08:00:00+00:00\"\n      }\n    ],\n    \"sku_shipping_methods\": [\n      {\n        \"id\": 12,\n        \"name\": \"Standard Shipping\"\n      },\n      {\n        \"id\": 13,\n        \"name\": \"Express Shipping\"\n      }\n    ]\n  }\n}"}]},{"name":"Create Manual Mapping","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipping-methods","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipping-methods"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Add a mapping row manually (source = manual).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nBody fields:\n- carrier_code (string, nullable): the ShipRush carrier code. Required unless is_default is true (a default row uses the wildcard pair). Max 255 characters.\n- service_code (string, nullable): the ShipRush service code. Required unless is_default is true. Max 255 characters.\n- shipping_method_id (integer, nullable): the SKU shipping method the pair maps to. Must reference an existing shipping method; null leaves the row unmapped.\n- is_default (boolean, optional): mark this row the default wildcard fallback. When set, the manager forces the wildcard carrier/service pair so carrier_code / service_code become optional.\n\nReturns 201 with the created row. Returns 422 with field errors when validation fails.\n\nAuthentication: Requires a Bearer token.","body":{"mode":"raw","raw":"{\n  \"carrier_code\": \"AusPost\",\n  \"service_code\": \"Parcel Post\",\n  \"shipping_method_id\": 12,\n  \"is_default\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipping-methods","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipping-methods"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 93,\n    \"integration_instance_id\": 3,\n    \"carrier_code\": \"AusPost\",\n    \"service_code\": \"Parcel Post\",\n    \"is_default\": false,\n    \"source\": \"manual\",\n    \"shipping_method_id\": 12,\n    \"shipping_method_name\": \"Standard Shipping\",\n    \"is_mapped\": true,\n    \"last_synced_at\": null,\n    \"created_at\": \"2026-07-08T05:30:00+00:00\",\n    \"updated_at\": \"2026-07-08T05:30:00+00:00\"\n  }\n}"}]},{"name":"Update Mapping","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipping-methods/{shippingMethod}","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipping-methods",":shippingMethod"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"shippingMethod","value":"91","description":"The mapping row id (scoped to the instance)."}]},"description":"Edit a single mapping row. Only the mapped SKU shipping method is editable; accepts PUT or PATCH. Returns the updated row.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n- shippingMethod: the mapping row id.\n\nBody fields:\n- shipping_method_id (integer, nullable): the SKU shipping method to map to. Must reference an existing shipping method; a null value clears the mapping.\n\nReturns 404 when the row cannot be found, and 422 when shipping_method_id does not reference an existing shipping method.\n\nAuthentication: Requires a Bearer token.","body":{"mode":"raw","raw":"{\n  \"shipping_method_id\": 13\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipping-methods/{shippingMethod}","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipping-methods",":shippingMethod"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"shippingMethod","value":"91","description":"The mapping row id (scoped to the instance)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 91,\n    \"integration_instance_id\": 3,\n    \"carrier_code\": \"AusPost\",\n    \"service_code\": \"Parcel Post\",\n    \"is_default\": false,\n    \"source\": \"synced\",\n    \"shipping_method_id\": 13,\n    \"shipping_method_name\": \"Express Shipping\",\n    \"is_mapped\": true,\n    \"last_synced_at\": \"2026-07-08T01:30:00+00:00\",\n    \"created_at\": \"2026-06-10T08:00:00+00:00\",\n    \"updated_at\": \"2026-07-08T05:35:00+00:00\"\n  }\n}"}]},{"name":"Delete Mapping","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipping-methods/{shippingMethod}","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipping-methods",":shippingMethod"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"shippingMethod","value":"93","description":"The mapping row id to remove (scoped to the instance)."}]},"description":"Delete a mapping row.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n- shippingMethod: the mapping row id.\n\nReturns 204 No Content on success. Returns 404 when the row does not exist or belongs to a different integration instance.\n\nAuthentication: Requires a Bearer token."},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipping-methods/{shippingMethod}","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipping-methods",":shippingMethod"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"shippingMethod","value":"93","description":"The mapping row id to remove (scoped to the instance)."}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{}"}]},{"name":"Sync Services","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipping-methods/sync","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipping-methods","sync"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Kick off the token-gated service-name sync as a tracked background job — pulls the carrier/service names available to the merchant and upserts them as mapping rows (source = synced). Requires a Shipping API token on the instance; without one the job no-ops server-side. The response returns the tracked job log id, which you can poll for progress.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nNo request body is required.\n\nAuthentication: Requires a Bearer token.","body":{"mode":"raw","raw":"","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipping-methods/sync","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipping-methods","sync"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 90233\n  },\n  \"message\": \"Shipping-method sync started — track progress in the job tray.\"\n}"}]},{"name":"Bulk Map Shipping Methods","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipping-methods/bulk-map","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipping-methods","bulk-map"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Bulk-map a set of rows to one SKU shipping method, and/or auto-match every still-unmapped row by normalized name. Two paths share the endpoint and may be combined in one call.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nBody fields (all optional):\n- ids (array of integers): the mapping row ids to map. When present, each is set to shipping_method_id.\n- shipping_method_id (integer, nullable): the SKU shipping method to map the ids to. Must reference an existing shipping method; a null value clears the mapping on those rows.\n- auto_match (boolean): when true, runs a name-match sweep over every still-unmapped row and maps the ones it can resolve.\n\nReturns the affected counts: mapped (rows set via ids), matched / unmatched (rows the auto-match sweep resolved / could not resolve). Returns 422 when shipping_method_id does not reference an existing shipping method.\n\nAuthentication: Requires a Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    91,\n    92\n  ],\n  \"shipping_method_id\": 12,\n  \"auto_match\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipping-methods/bulk-map","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipping-methods","bulk-map"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"mapped\": 2,\n    \"matched\": 3,\n    \"unmatched\": 1\n  },\n  \"message\": \"Mappings updated.\"\n}"}]},{"name":"Export Shipping Methods CSV","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipping-methods/export","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipping-methods","export"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Export the current carrier/service → SKU shipping-method mappings as a re-importable CSV file download. The response is text/csv (Content-Disposition: attachment; filename=\"shiprush-shipping-methods.csv\") with the columns: carrier_code, service_code, is_default, source, shipping_method_id, shipping_method_name. Edit the mapping columns and re-upload via Import Shipping Methods CSV.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n\nAuthentication: Requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipping-methods/export","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipping-methods","export"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"carrier_code,service_code,is_default,source,shipping_method_id,shipping_method_name\nAusPost,Parcel Post,0,synced,12,Standard Shipping\nCouriersPlease,Express,0,observed,,\n*,*,1,manual,12,Standard Shipping\n"}]},{"name":"Import Shipping Methods CSV","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipping-methods/import","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipping-methods","import"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Import mapping edits from an uploaded CSV (multipart/form-data). The file is parsed into header-keyed rows and applied — existing pairs are updated, new pairs created. Use the columns produced by Export Shipping Methods CSV.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n\nForm fields:\n- file (file, required): a .csv or .txt file. The header row must match the export columns.\n\nReturns 200 with a summary: created (new rows), updated (existing rows changed), errors (per-row error messages). Returns 422 when no file is supplied or the file is not a CSV.\n\nAuthentication: Requires a Bearer token.","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/shipping-methods/import","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","shipping-methods","import"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"created\": 1,\n    \"updated\": 2,\n    \"errors\": []\n  },\n  \"message\": \"Import processed.\"\n}"}]},{"name":"List Webhook Events","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/webhooks","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","webhooks"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Paginated, filterable list of inbound store-endpoint events received from ShipRush for the instance, newest first. Every inbound writeback (shipnotify / credential_failed / duplicate) and every rejected export poll (export_rejected — e.g. a start_date or end_date ShipRush sent in an unparseable format; the poll gets HTTP 400) is persisted so you can audit and re-run deliveries; each row records the event type, dedup key, order/tracking numbers, the raw payload, whether the credentials were valid, and whether it has been processed (processed / processed_at) plus any error.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nEvery filter[&lt;column>] parameter also accepts an operator suffix as filter[&lt;column>.&lt;operator>]=&lt;value>; a bare filter[&lt;column>]=&lt;value> is treated as \"is\". filter[search] does a fuzzy partial match across order_number, tracking_number, and event_type.\n\nSort field. Prefix with - for descending. Allowed: id, event_type, order_number, tracking_number, credential_valid, processed, received_at, processed_at, created_at, updated_at. Default: -id (newest first).\n\nResults are paginated; per_page defaults to 10.\n\nAuthentication: Requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/webhooks","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","webhooks"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 9011,\n      \"integration_instance_id\": 3,\n      \"event_type\": \"shipnotify\",\n      \"dedup_key\": \"3:shipnotify:SO-100482:DZ1000456789\",\n      \"order_number\": \"SO-100482\",\n      \"tracking_number\": \"DZ1000456789\",\n      \"credential_valid\": true,\n      \"payload\": {\n        \"OrderNumber\": \"SO-100482\",\n        \"TrackingNumber\": \"DZ1000456789\",\n        \"Carrier\": \"AusPost\",\n        \"Service\": \"Parcel Post\",\n        \"ShippingCharges\": 8.95\n      },\n      \"processed\": true,\n      \"processed_at\": \"2026-07-08T03:40:06+00:00\",\n      \"error\": null,\n      \"is_reprocessable\": false,\n      \"received_at\": \"2026-07-08T03:40:05+00:00\",\n      \"created_at\": \"2026-07-08T03:40:05+00:00\",\n      \"updated_at\": \"2026-07-08T03:40:06+00:00\"\n    },\n    {\n      \"id\": 9008,\n      \"integration_instance_id\": 3,\n      \"event_type\": \"shipnotify\",\n      \"dedup_key\": \"3:shipnotify:SO-100455:CP99001122334455\",\n      \"order_number\": \"SO-100455\",\n      \"tracking_number\": \"CP99001122334455\",\n      \"credential_valid\": true,\n      \"payload\": {\n        \"OrderNumber\": \"SO-100455\",\n        \"TrackingNumber\": \"CP99001122334455\",\n        \"Carrier\": \"CouriersPlease\",\n        \"Service\": \"Express\"\n      },\n      \"processed\": false,\n      \"processed_at\": null,\n      \"error\": null,\n      \"is_reprocessable\": true,\n      \"received_at\": \"2026-07-07T23:41:03+00:00\",\n      \"created_at\": \"2026-07-07T23:41:03+00:00\",\n      \"updated_at\": \"2026-07-07T23:41:03+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 34,\n  \"per_page\": 10,\n  \"total\": 502,\n  \"from\": 1,\n  \"to\": 10,\n  \"first_page_url\": \"{{protocol}}://{{domain}}/api/shiprush/instances/3/webhooks?page=1\",\n  \"last_page_url\": \"{{protocol}}://{{domain}}/api/shiprush/instances/3/webhooks?page=34\",\n  \"next_page_url\": \"{{protocol}}://{{domain}}/api/shiprush/instances/3/webhooks?page=2\",\n  \"prev_page_url\": null,\n  \"path\": \"{{protocol}}://{{domain}}/api/shiprush/instances/3/webhooks\"\n}"}]},{"name":"Get Webhook Event","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/webhooks/{webhookEvent}","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","webhooks",":webhookEvent"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"webhookEvent","value":"9011","description":"The webhook event id (scoped to the instance)."}]},"description":"Returns a single store-endpoint event received from ShipRush, including its full raw payload. processed / processed_at reflect whether the ingest pipeline handled the event; is_reprocessable drives the row's re-process action (only an unprocessed shipnotify can be replayed).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n- webhookEvent: the event id.\n\nReturns 404 when the event does not exist or belongs to a different integration instance.\n\nAuthentication: Requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/webhooks/{webhookEvent}","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","webhooks",":webhookEvent"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"webhookEvent","value":"9011","description":"The webhook event id (scoped to the instance)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 9011,\n    \"integration_instance_id\": 3,\n    \"event_type\": \"shipnotify\",\n    \"dedup_key\": \"3:shipnotify:SO-100482:DZ1000456789\",\n    \"order_number\": \"SO-100482\",\n    \"tracking_number\": \"DZ1000456789\",\n    \"credential_valid\": true,\n    \"payload\": {\n      \"OrderNumber\": \"SO-100482\",\n      \"TrackingNumber\": \"DZ1000456789\",\n      \"Carrier\": \"AusPost\",\n      \"Service\": \"Parcel Post\",\n      \"ShippingCharges\": 8.95,\n      \"ShipDate\": \"2026-07-08T03:40:00\"\n    },\n    \"processed\": true,\n    \"processed_at\": \"2026-07-08T03:40:06+00:00\",\n    \"error\": null,\n    \"is_reprocessable\": false,\n    \"received_at\": \"2026-07-08T03:40:05+00:00\",\n    \"created_at\": \"2026-07-08T03:40:05+00:00\",\n    \"updated_at\": \"2026-07-08T03:40:06+00:00\"\n  }\n}"}]},{"name":"Re-process Webhook Event","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/webhooks/{webhookEvent}/re-process","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","webhooks",":webhookEvent","re-process"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"webhookEvent","value":"9008","description":"The webhook event id (scoped to the instance)."}]},"description":"Replay a failed/unprocessed shipnotify: re-resolves its order and re-mints the SalesOrderFulfillment. Only a shipnotify event can be reprocessed — a credential_failed / duplicate / export_rejected row is informational and rejected with 422. Returns the refreshed event.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n- webhookEvent: the event id.\n\nNo request body is required. Returns 404 when the event does not exist or belongs to a different instance; 422 when the event is not a shipnotify.\n\nAuthentication: Requires a Bearer token.","body":{"mode":"raw","raw":"","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/webhooks/{webhookEvent}/re-process","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","webhooks",":webhookEvent","re-process"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."},{"key":"webhookEvent","value":"9008","description":"The webhook event id (scoped to the instance)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 9008,\n    \"integration_instance_id\": 3,\n    \"event_type\": \"shipnotify\",\n    \"dedup_key\": \"3:shipnotify:SO-100455:CP99001122334455\",\n    \"order_number\": \"SO-100455\",\n    \"tracking_number\": \"CP99001122334455\",\n    \"credential_valid\": true,\n    \"payload\": {\n      \"OrderNumber\": \"SO-100455\",\n      \"TrackingNumber\": \"CP99001122334455\",\n      \"Carrier\": \"CouriersPlease\",\n      \"Service\": \"Express\"\n    },\n    \"processed\": true,\n    \"processed_at\": \"2026-07-08T05:45:00+00:00\",\n    \"error\": null,\n    \"is_reprocessable\": false,\n    \"received_at\": \"2026-07-07T23:41:03+00:00\",\n    \"created_at\": \"2026-07-07T23:41:03+00:00\",\n    \"updated_at\": \"2026-07-08T05:45:00+00:00\"\n  }\n}"}]},{"name":"Get Endpoint Info","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/webhooks/endpoint","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","webhooks","endpoint"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Returns the store-endpoint info card the merchant needs to configure ShipRush: the feed URL to paste in, the store username, and a masked password placeholder. The plaintext password is NEVER returned here — regenerate it via Regenerate Credentials to obtain a fresh one-time value.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPath parameter:\n- instance: the numeric ID of the ShipRush integration instance.\n\nThe response \"data\" object contains:\n- feed_url: the store-endpoint URL the merchant pastes into ShipRush (or null when no webhook route is provisioned).\n- store_username: the SS-UserName for the feed.\n- store_password_masked: a masked placeholder for the password (dots), never the real value.\n\nAuthentication: Requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/webhooks/endpoint","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","webhooks","endpoint"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"feed_url\": \"https://acme.sku.io/api/webhooks/shiprush/abc123def456\",\n    \"store_username\": \"sku_shiprush_ab12cd34\",\n    \"store_password_masked\": \"••••••••••••\"\n  }\n}"}]},{"name":"Bulk Re-process Webhook Events","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/webhooks/bulk-re-process","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","webhooks","bulk-re-process"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]},"description":"Replay several shipnotify events at once, re-resolving each event's order and recording the shipment again. Only shipnotify events are replayed; credential-failed, duplicate and export-rejected events are skipped. An event whose replay errors is counted as failed with its message.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nBody fields:\n- ids (array of integers, required unless apply_to_all is true): the event ids to act on. Ids belonging to another integration instance are ignored.\n- apply_to_all (boolean, optional): when true, act on every event matching filters instead of ids.\n- filters (object, optional): the same filter keys the List Webhook Events endpoint accepts (e.g. event_type.is, status.is), plus filter_groups for grouped conditions. Only used with apply_to_all.\n\nReturns counts: processed (acted on), skipped (the action does not apply to the row's state), failed (the action errored), and errors keyed by row id. Returns 422 when neither ids nor apply_to_all is sent.\n\nAuthentication: Requires a Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    101,\n    102,\n    103\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/shiprush/instances/{integration_instance}/webhooks/bulk-re-process","host":["{{baseUrl}}"],"path":["api","shiprush","instances",":integration_instance","webhooks","bulk-re-process"],"variable":[{"key":"integration_instance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"processed\": 1,\n    \"skipped\": 1,\n    \"failed\": 1,\n    \"errors\": {\n      \"103\": \"Unknown order — no ShipRush order matched SO-0099\"\n    }\n  },\n  \"message\": \"1 re-processed, 1 skipped, 1 failed.\"\n}"}]}]},{"name":"EasyPost","item":[{"name":"Get Current EasyPost Connection","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/easypost/instance","host":["{{baseUrl}}"],"path":["api","easypost","instance"]},"description":"Returns the account's EasyPost connection, or `data: null` when EasyPost is not connected. `error` holds the last authentication failure, if any.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `integrations.show`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/easypost/instance","host":["{{baseUrl}}"],"path":["api","easypost","instance"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 22,\n    \"integration_id\": 31,\n    \"name\": \"EasyPost\",\n    \"mode\": \"production\",\n    \"api_key_last4\": \"9f3K\",\n    \"error\": null,\n    \"created_at\": \"2026-10-08T14:00:00+00:00\",\n    \"updated_at\": \"2026-10-08T14:00:00+00:00\"\n  }\n}"}]},{"name":"Test EasyPost API Key","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/easypost/test","host":["{{baseUrl}}"],"path":["api","easypost","test"]},"description":"Checks an unsaved EasyPost API key live against EasyPost. Nothing is persisted. `mode` is derived from the key prefix.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody fields:\n- api_key (string, required): EasyPost API key, 16–255 characters, starting with `EZTK` (test mode) or `EZAK` (production mode). Whitespace is trimmed.\n\nEasyPost failures return 422 (or 502 when EasyPost is unreachable) with `message`, `code` (EasyPost's error code, e.g. `APIKEY.INACTIVE`, `CONNECTION_FAILED`) and `errors.api_key`.\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `integrations.create`","body":{"mode":"raw","raw":"{\n  \"api_key\": \"EZTK<your-test-api-key>\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/easypost/test","host":["{{baseUrl}}"],"path":["api","easypost","test"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"mode\": \"test\"\n  },\n  \"message\": \"Connection successful\"\n}"}]},{"name":"Create EasyPost Connection","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/easypost/instances","host":["{{baseUrl}}"],"path":["api","easypost","instances"]},"description":"Connects EasyPost. The key is verified live first — an invalid key creates nothing. Returns 422 with `code: already_connected` when the account already has an EasyPost connection.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody fields:\n- api_key (string, required): EasyPost API key, 16–255 characters, starting with `EZTK` (test mode) or `EZAK` (production mode). Whitespace is trimmed.\n- name (string, optional, max 255): display name\n\nEasyPost failures return 422 (or 502 when EasyPost is unreachable) with `message`, `code` (EasyPost's error code, e.g. `APIKEY.INACTIVE`, `CONNECTION_FAILED`) and `errors.api_key`.\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `integrations.create`","body":{"mode":"raw","raw":"{\n  \"api_key\": \"EZAK<your-production-api-key>\",\n  \"name\": \"EasyPost\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/easypost/instances","host":["{{baseUrl}}"],"path":["api","easypost","instances"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 22,\n    \"integration_id\": 31,\n    \"name\": \"EasyPost\",\n    \"mode\": \"production\",\n    \"api_key_last4\": \"9f3K\",\n    \"error\": null,\n    \"created_at\": \"2026-10-08T14:00:00+00:00\",\n    \"updated_at\": \"2026-10-08T14:00:00+00:00\"\n  },\n  \"message\": \"EasyPost connected.\"\n}"}]},{"name":"Get EasyPost Connection","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/easypost/instances/{integrationInstance}","host":["{{baseUrl}}"],"path":["api","easypost","instances",":integrationInstance"],"variable":[{"key":"integrationInstance","value":"22","description":"The integration instance ID."}]},"description":"Returns an EasyPost connection by integration instance id. Returns 404 when the instance is not an EasyPost connection.\n\n**Required scope: `integrations:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `integrations.show`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/easypost/instances/{integrationInstance}","host":["{{baseUrl}}"],"path":["api","easypost","instances",":integrationInstance"],"variable":[{"key":"integrationInstance","value":"22","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 22,\n    \"integration_id\": 31,\n    \"name\": \"EasyPost\",\n    \"mode\": \"production\",\n    \"api_key_last4\": \"9f3K\",\n    \"error\": null,\n    \"created_at\": \"2026-10-08T14:00:00+00:00\",\n    \"updated_at\": \"2026-10-08T14:00:00+00:00\"\n  }\n}"}]},{"name":"Update EasyPost Connection","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/easypost/instances/{integrationInstance}","host":["{{baseUrl}}"],"path":["api","easypost","instances",":integrationInstance"],"variable":[{"key":"integrationInstance","value":"22","description":"The integration instance ID."}]},"description":"Renames the connection and/or replaces its API key. A new key is verified live before it is saved.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody fields (all optional):\n- api_key (string): EasyPost API key, 16–255 characters, starting with `EZTK` (test mode) or `EZAK` (production mode). Whitespace is trimmed.\n- name (string, max 255)\n\nEasyPost failures return 422 (or 502 when EasyPost is unreachable) with `message`, `code` (EasyPost's error code, e.g. `APIKEY.INACTIVE`, `CONNECTION_FAILED`) and `errors.api_key`.\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `integrations.update`","body":{"mode":"raw","raw":"{\n  \"name\": \"EasyPost - Dropship\",\n  \"api_key\": \"EZAK<your-new-production-api-key>\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/easypost/instances/{integrationInstance}","host":["{{baseUrl}}"],"path":["api","easypost","instances",":integrationInstance"],"variable":[{"key":"integrationInstance","value":"22","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 22,\n    \"integration_id\": 31,\n    \"name\": \"EasyPost - Dropship\",\n    \"mode\": \"production\",\n    \"api_key_last4\": \"2bQ7\",\n    \"error\": null,\n    \"created_at\": \"2026-10-08T14:00:00+00:00\",\n    \"updated_at\": \"2026-10-08T14:00:00+00:00\"\n  },\n  \"message\": \"Settings saved\"\n}"}]},{"name":"Delete EasyPost Connection","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/easypost/instances/{integrationInstance}","host":["{{baseUrl}}"],"path":["api","easypost","instances",":integrationInstance"],"variable":[{"key":"integrationInstance","value":"22","description":"The integration instance ID."}]},"description":"Disconnects EasyPost and deletes the stored API key.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `integrations.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/easypost/instances/{integrationInstance}","host":["{{baseUrl}}"],"path":["api","easypost","instances",":integrationInstance"],"variable":[{"key":"integrationInstance","value":"22","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"EasyPost disconnected.\"\n}"}]},{"name":"Test EasyPost Connection","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/easypost/instances/{integrationInstance}/test-connection","host":["{{baseUrl}}"],"path":["api","easypost","instances",":integrationInstance","test-connection"],"variable":[{"key":"integrationInstance","value":"22","description":"The integration instance ID."}]},"description":"Checks the saved API key live against EasyPost. A successful check clears any stored authentication error; an authentication failure is recorded on the connection's `error`.\n\n**Required scope: `integrations:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEasyPost failures return 422 (or 502 when EasyPost is unreachable) with `message`, `code` (EasyPost's error code, e.g. `APIKEY.INACTIVE`, `CONNECTION_FAILED`) and `errors.api_key`.\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `integrations.show`"},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/easypost/instances/{integrationInstance}/test-connection","host":["{{baseUrl}}"],"path":["api","easypost","instances",":integrationInstance","test-connection"],"variable":[{"key":"integrationInstance","value":"22","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"mode\": \"production\",\n    \"checked_at\": \"2026-10-08T15:30:00+00:00\"\n  },\n  \"message\": \"Connection successful\"\n}"}]}]},{"name":"PayPal","item":[{"name":"Get PayPal Availability","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/paypal/availability","host":["{{baseUrl}}"],"path":["api","paypal","availability"]},"description":"Returns whether PayPal can be used on this SKU.io account, and whether one-click Connect with PayPal is available.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token.\n\n`available` is always true: an account can always be connected with its own PayPal REST app keys (see **Connect PayPal With API Keys**). `paypal_connect_available` is true when the one-click Connect with PayPal sign-in is available; while it is false, `message` explains it, and accounts connected that way cannot create pay links, refund or import transactions (those calls return `409`)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/paypal/availability","host":["{{baseUrl}}"],"path":["api","paypal","availability"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"available\": true,\n    \"paypal_connect_available\": true,\n    \"message\": null\n  }\n}"}]},{"name":"Get PayPal Integration","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/paypal/integrations/{id}","host":["{{baseUrl}}"],"path":["api","paypal","integrations",":id"],"variable":[{"key":"id","value":"12","description":"PayPal integration instance ID"}]},"description":"Returns one PayPal integration with its connection status, merchant identity and granted permissions.\n\n**Authentication:** Requires Bearer token.\n\n`transaction_search_granted` is true when the merchant granted transaction-search access while connecting; `can_import_transactions` is true when transactions can be imported (connected, and transaction search granted).\n\n`platform_available` is false when PayPal is not yet available on SKU.io; connecting, refreshing status, creating pay links, refunding and importing then return `409`. `connected_at` is when the account last finished connecting with PayPal (null when not connected).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n`connection_method` is `paypal_connect` (signed in with Connect with PayPal), `api_keys` (connected with the merchant's own PayPal REST app keys) or null when not connected. For an `api_keys` account, `client_id` is the app's Client ID (the secret is never returned), `webhook_registered` says whether SKU.io's webhook is subscribed on that app and `webhook_url` is where PayPal delivers its events. `last_client_id` is the Client ID of a disconnected `api_keys` account. `can_switch_to_paypal_connect` is true for an `api_keys` account once Connect with PayPal is available; switching keeps the account's pay links and payments."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/paypal/integrations/{id}","host":["{{baseUrl}}"],"path":["api","paypal","integrations",":id"],"variable":[{"key":"id","value":"12","description":"PayPal integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"name\": \"Acme Wholesale\",\n    \"sync_status\": \"active\",\n    \"connected\": true,\n    \"connection_method\": \"api_keys\",\n    \"ready_to_transact\": true,\n    \"client_id\": \"AYx9kQ2mT7vR4pL8nB3cW6zH1sD5fG0jK\",\n    \"last_client_id\": null,\n    \"webhook_registered\": true,\n    \"webhook_url\": \"https://acme.sku.io/api/webhooks/paypal/12\",\n    \"environment\": \"production\",\n    \"merchant_id\": \"7QHN2KX4T9Y2E\",\n    \"merchant_email\": null,\n    \"currency\": null,\n    \"permissions_granted\": true,\n    \"transaction_search_granted\": true,\n    \"can_import_transactions\": true,\n    \"payments_receivable\": true,\n    \"primary_email_confirmed\": false,\n    \"onboarding_status\": \"api_keys\",\n    \"platform_available\": true,\n    \"can_switch_to_paypal_connect\": true,\n    \"connected_at\": \"2026-10-10T21:28:49.000000Z\",\n    \"last_reconciled_at\": \"2026-10-11T02:04:40.000000Z\",\n    \"disconnected_at\": null,\n    \"created_at\": \"2026-10-10T21:20:00.000000Z\",\n    \"updated_at\": \"2026-10-11T02:04:40.000000Z\"\n  }\n}"}]},{"name":"Connect PayPal With API Keys","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/paypal/integrations/{id}/api-keys","host":["{{baseUrl}}"],"path":["api","paypal","integrations",":id","api-keys"],"variable":[{"key":"id","value":"12","description":"PayPal integration ID"}]},"description":"Connect a PayPal integration (or replace its keys) with the merchant's own PayPal REST app credentials. Create the app on developer.paypal.com under Apps & Credentials (type Merchant) and copy its Client ID and Secret.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token with permission to manage integrations.\n\n**Body:**\n- `environment` (required) — `production` (Live keys) or `sandbox`.\n- `client_id` (required, string, 20–255 characters) — the app's Client ID.\n- `client_secret` (required, string, 20–255 characters) — the app's Secret. Stored encrypted and never returned.\n\nThe keys are checked with PayPal before anything is saved; keys PayPal rejects return `422`. SKU.io then subscribes a webhook on the app so payments, refunds and disputes are recorded instantly. If PayPal refuses the webhook (for example, the app already has 10 webhooks) the account still connects and `webhook_warning` explains what to fix; payments are still recorded by SKU.io's periodic checks. Turning on the app's Transaction search feature lets SKU.io import and reconcile the account's PayPal activity (`can_import_transactions`).\n\nThis connection does not need SKU.io's Connect with PayPal sign-in. An account connected this way can later switch to Connect with PayPal and keep its pay links and payment history.","body":{"mode":"raw","raw":"{\n  \"environment\": \"production\",\n  \"client_id\": \"AYx9kQ2mT7vR4pL8nB3cW6zH1sD5fG0jK\",\n  \"client_secret\": \"EKq3nV8sR2wT5yU9iO1pL4kJ7hG0fD6sA\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/paypal/integrations/{id}/api-keys","host":["{{baseUrl}}"],"path":["api","paypal","integrations",":id","api-keys"],"variable":[{"key":"id","value":"12","description":"PayPal integration ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"name\": \"Acme Wholesale\",\n    \"sync_status\": \"active\",\n    \"connected\": true,\n    \"connection_method\": \"api_keys\",\n    \"ready_to_transact\": true,\n    \"client_id\": \"AYx9kQ2mT7vR4pL8nB3cW6zH1sD5fG0jK\",\n    \"last_client_id\": null,\n    \"webhook_registered\": true,\n    \"webhook_url\": \"https://acme.sku.io/api/webhooks/paypal/12\",\n    \"environment\": \"production\",\n    \"merchant_id\": \"7QHN2KX4T9Y2E\",\n    \"merchant_email\": null,\n    \"currency\": null,\n    \"permissions_granted\": true,\n    \"transaction_search_granted\": true,\n    \"can_import_transactions\": true,\n    \"payments_receivable\": true,\n    \"primary_email_confirmed\": false,\n    \"onboarding_status\": \"api_keys\",\n    \"platform_available\": true,\n    \"can_switch_to_paypal_connect\": true,\n    \"connected_at\": \"2026-10-10T21:28:49.000000Z\",\n    \"last_reconciled_at\": \"2026-10-11T02:04:40.000000Z\",\n    \"disconnected_at\": null,\n    \"created_at\": \"2026-10-10T21:20:00.000000Z\",\n    \"updated_at\": \"2026-10-11T02:04:40.000000Z\"\n  },\n  \"message\": \"PayPal account connected.\",\n  \"webhook_warning\": null\n}"}]},{"name":"List PayPal Pay Links","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/paypal/integrations/{id}/pay-links","host":["{{baseUrl}}"],"path":["api","paypal","integrations",":id","pay-links"],"variable":[{"key":"id","value":"12","description":"PayPal integration instance ID"}]},"description":"Lists the pay links created for one PayPal integration, newest first, with paging, sorting, search and filters.\n\n**Authentication:** Requires Bearer token.\n\n`link_reference` and `link_route` identify the sales order each link collects payment for. `pay_url` is the full customer-facing pay page address on the tenant's own domain; `pay_path` is the same address relative to that domain.\n\n`status` is the link's current state: an active link past its `expires_at` is reported as `expired`.\n\nReturns 404 when the integration does not exist.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/paypal/integrations/{id}/pay-links","host":["{{baseUrl}}"],"path":["api","paypal","integrations",":id","pay-links"],"variable":[{"key":"id","value":"12","description":"PayPal integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 301,\n      \"integration_instance_id\": 12,\n      \"amount\": 1284.5,\n      \"currency\": \"USD\",\n      \"status\": \"paid\",\n      \"token\": \"Hq2v9Xb3kLm8Pz1Rt6Wy0Ac4De7Fg5Jn2Qs8Uv3X\",\n      \"pay_path\": \"/pay/Hq2v9Xb3kLm8Pz1Rt6Wy0Ac4De7Fg5Jn2Qs8Uv3X\",\n      \"pay_url\": \"https://acme.sku.io/pay/Hq2v9Xb3kLm8Pz1Rt6Wy0Ac4De7Fg5Jn2Qs8Uv3X\",\n      \"link_reference\": \"SO-10482\",\n      \"link_route\": \"/orders/sales-orders/10482\",\n      \"paypal_order_id\": \"5O190127TN364715T\",\n      \"checkout_attempts\": 1,\n      \"is_expired\": false,\n      \"expires_at\": \"2026-11-03T15:00:00.000000Z\",\n      \"created_by\": 3,\n      \"created_at\": \"2026-10-04T15:00:00.000000Z\"\n    }\n  ],\n  \"first_page_url\": \"https://acme.sku.io/api/paypal/integrations/12/pay-links?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://acme.sku.io/api/paypal/integrations/12/pay-links?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://acme.sku.io/api/paypal/integrations/12/pay-links\",\n  \"per_page\": 15,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"Create PayPal Pay Link","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/sales-orders/{salesOrderId}/paypal-payment-links","host":["{{baseUrl}}"],"path":["api","sales-orders",":salesOrderId","paypal-payment-links"],"variable":[{"key":"salesOrderId","value":"10483","description":"Sales order ID"}]},"description":"Creates a PayPal pay link for a sales order and, optionally, emails it to the customer.\n\n**Authentication:** Requires Bearer token with permission to manage sales order payments.\n\nAn order has at most one active pay link: creating a new one cancels the previous active link.\n\n**Body fields:**\n- `amount` (number, required) — at least 0.01 and no more than the order's amount due.\n- `integration_instance_id` (integer, required) — the connected PayPal account to collect into.\n- `expires_in_days` (integer, optional, 1–365, default 30).\n- `email_customer` (boolean, optional) — email the new link using the editable \"PayPal Payment Request\" email template.\n- `recipient_email` (string, optional) — address to email; defaults to the order's customer email.\n\nThe link is always created. If it cannot be emailed (no address, or the template is turned off), `email_error` explains why and `emailed_to` is null.\n\nReturns 409 when PayPal is not available on this account and 422 when the PayPal account is not connected or the amount is invalid.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.","body":{"mode":"raw","raw":"{\n  \"amount\": 79,\n  \"integration_instance_id\": 12,\n  \"expires_in_days\": 30,\n  \"email_customer\": true,\n  \"recipient_email\": \"billing@example.com\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/sales-orders/{salesOrderId}/paypal-payment-links","host":["{{baseUrl}}"],"path":["api","sales-orders",":salesOrderId","paypal-payment-links"],"variable":[{"key":"salesOrderId","value":"10483","description":"Sales order ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 302,\n    \"integration_instance_id\": 12,\n    \"amount\": 79,\n    \"currency\": \"USD\",\n    \"status\": \"active\",\n    \"token\": \"siBPpr1cQGeq4WNDTZM0nGZJBvm9l2b4ewenFbAM\",\n    \"pay_path\": \"/pay/siBPpr1cQGeq4WNDTZM0nGZJBvm9l2b4ewenFbAM\",\n    \"pay_url\": \"https://acme.sku.io/pay/siBPpr1cQGeq4WNDTZM0nGZJBvm9l2b4ewenFbAM\",\n    \"link_reference\": \"SO-10483\",\n    \"link_route\": \"/orders/sales-orders/10483\",\n    \"paypal_order_id\": null,\n    \"checkout_attempts\": 0,\n    \"is_expired\": false,\n    \"expires_at\": \"2026-11-09T00:34:55.000000Z\",\n    \"created_by\": 3,\n    \"created_at\": \"2026-10-10T00:34:55.000000Z\"\n  },\n  \"emailed_to\": \"billing@example.com\",\n  \"email_error\": null,\n  \"message\": \"PayPal pay-link created and emailed to billing@example.com.\"\n}"}]},{"name":"Email PayPal Pay Link","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/paypal-payment-links/{id}/email","host":["{{baseUrl}}"],"path":["api","paypal-payment-links",":id","email"],"variable":[{"key":"id","value":"302","description":"PayPal pay link ID"}]},"description":"Emails an active PayPal pay link to the customer using the editable \"PayPal Payment Request\" email template (sender, CC and BCC follow the account's outbound email settings).\n\n**Authentication:** Requires Bearer token with permission to manage sales order payments.\n\n**Body fields:**\n- `recipient_email` (string, optional) — address to email; defaults to the order's customer email.\n\nThe template's placeholders `{{paypal_pay_link}}`, `{{amount_due}}` and `{{paypal_pay_link_expires_at}}` are filled with the link's address, the order's amount due and the link's expiry date.\n\nReturns 422 when the link is cancelled, paid or expired, when no address is available, or when the template is turned off; 404 when the link does not exist.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.","body":{"mode":"raw","raw":"{\n  \"recipient_email\": \"billing@example.com\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/paypal-payment-links/{id}/email","host":["{{baseUrl}}"],"path":["api","paypal-payment-links",":id","email"],"variable":[{"key":"id","value":"302","description":"PayPal pay link ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 302,\n    \"integration_instance_id\": 12,\n    \"amount\": 79,\n    \"currency\": \"USD\",\n    \"status\": \"active\",\n    \"token\": \"siBPpr1cQGeq4WNDTZM0nGZJBvm9l2b4ewenFbAM\",\n    \"pay_path\": \"/pay/siBPpr1cQGeq4WNDTZM0nGZJBvm9l2b4ewenFbAM\",\n    \"pay_url\": \"https://acme.sku.io/pay/siBPpr1cQGeq4WNDTZM0nGZJBvm9l2b4ewenFbAM\",\n    \"link_reference\": \"SO-10483\",\n    \"link_route\": \"/orders/sales-orders/10483\",\n    \"paypal_order_id\": null,\n    \"checkout_attempts\": 0,\n    \"is_expired\": false,\n    \"expires_at\": \"2026-11-09T00:34:55.000000Z\",\n    \"created_by\": 3,\n    \"created_at\": \"2026-10-10T00:34:55.000000Z\"\n  },\n  \"emailed_to\": \"billing@example.com\",\n  \"message\": \"Pay link emailed to billing@example.com.\"\n}"}]},{"name":"List PayPal Transactions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/paypal/integrations/{id}/transactions","host":["{{baseUrl}}"],"path":["api","paypal","integrations",":id","transactions"],"variable":[{"key":"id","value":"12","description":"PayPal integration instance ID"}]},"description":"Lists PayPal account activity imported for one PayPal integration: payments, refunds, chargebacks, bank transfers, holds and fees, each classified against the payments SKU.io has recorded.\n\n**Authentication:** Requires Bearer token.\n\n**match_status values:** `matched` (linked to a payment SKU.io recorded, or a refund of one), `recovered` (a pay-link payment whose notification was missed; the import recorded the payment), `unmatched` (a completed payment no SKU.io pay link references), `pending` (e.g. an eCheck still clearing), `denied`, `disputed` (chargeback), `not_applicable` (transfers, holds, currency conversions).\n\n**type values:** payment, refund, chargeback, transfer, fee, hold, currency_conversion, other (derived from PayPal's T-code in `event_code`).\n\n**Amounts:** signed as PayPal reports them; `fee_amount` is negative for fees charged, `net_amount` = gross + fee.\n\n**Advanced filters:** pass a base64-encoded `filter_groups` tree with `{column}.{operator}` conditions on transaction_id, event_code, payer_email, payer_name (text operators), gross_amount, fee_amount, net_amount (numeric operators) and transaction_date (datetime operators).\n\n**Sorts:** transaction_date, gross_amount, fee_amount, net_amount, id (prefix with - for descending; default -transaction_date).\n\n**Pagination:** `page`, `per_page` (default 15).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/paypal/integrations/{id}/transactions","host":["{{baseUrl}}"],"path":["api","paypal","integrations",":id","transactions"],"variable":[{"key":"id","value":"12","description":"PayPal integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 5821,\n      \"transaction_id\": \"8MC58591AB1234567\",\n      \"event_code\": \"T0006\",\n      \"type\": \"payment\",\n      \"type_label\": \"Payment\",\n      \"transaction_status\": \"S\",\n      \"gross_amount\": 1284.5,\n      \"fee_amount\": -45.27,\n      \"net_amount\": 1239.23,\n      \"currency\": \"USD\",\n      \"paypal_reference_id\": null,\n      \"payer_email\": \"orders@harborcoffee.co\",\n      \"payer_name\": \"Harbor Coffee Co.\",\n      \"invoice_id\": null,\n      \"transaction_date\": \"2026-10-04T15:42:00.000000Z\",\n      \"match_status\": \"matched\",\n      \"matched_at\": \"2026-10-05T02:03:11.000000Z\",\n      \"capture_id\": \"8MC58591AB1234567\",\n      \"payment_id\": 4412,\n      \"dispute_status\": null,\n      \"link_reference\": \"SO-10482\",\n      \"link_route\": \"/orders/sales-orders/10482\",\n      \"created_at\": \"2026-10-05T02:03:11.000000Z\"\n    },\n    {\n      \"id\": 5822,\n      \"transaction_id\": \"5WQ90021GH3344556\",\n      \"event_code\": \"T0006\",\n      \"type\": \"payment\",\n      \"type_label\": \"Payment\",\n      \"transaction_status\": \"S\",\n      \"gross_amount\": 215.75,\n      \"fee_amount\": -7.98,\n      \"net_amount\": 207.77,\n      \"currency\": \"USD\",\n      \"paypal_reference_id\": null,\n      \"payer_email\": \"mia.chen@example.com\",\n      \"payer_name\": \"Mia Chen\",\n      \"invoice_id\": null,\n      \"transaction_date\": \"2026-10-03T09:55:00.000000Z\",\n      \"match_status\": \"unmatched\",\n      \"matched_at\": null,\n      \"capture_id\": null,\n      \"payment_id\": null,\n      \"dispute_status\": null,\n      \"link_reference\": null,\n      \"link_route\": null,\n      \"created_at\": \"2026-10-05T02:03:11.000000Z\"\n    }\n  ],\n  \"first_page_url\": \"https://acme.sku.io/api/paypal/integrations/12/transactions?page=1\",\n  \"from\": 1,\n  \"last_page\": 21,\n  \"last_page_url\": \"https://acme.sku.io/api/paypal/integrations/12/transactions?page=21\",\n  \"links\": [],\n  \"next_page_url\": \"https://acme.sku.io/api/paypal/integrations/12/transactions?page=2\",\n  \"path\": \"https://acme.sku.io/api/paypal/integrations/12/transactions\",\n  \"per_page\": 15,\n  \"prev_page_url\": null,\n  \"to\": 15,\n  \"total\": 312\n}"}]},{"name":"Get PayPal Transaction Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/paypal/integrations/{id}/transactions/summary","host":["{{baseUrl}}"],"path":["api","paypal","integrations",":id","transactions","summary"],"variable":[{"key":"id","value":"12","description":"PayPal integration instance ID"}]},"description":"Totals for imported PayPal transactions on one integration. Accepts the same `filter[...]` and `filter_groups` parameters as List PayPal Transactions, so the totals always match the filtered list.\n\n**Authentication:** Requires Bearer token.\n\n`gross` and `net` exclude transfers to and from bank accounts, which are reported separately in `transfers`. `fees` is the signed sum of PayPal fees. `match_status_counts` counts rows per match status. `import` reports whether transaction import is enabled for this PayPal account (the merchant must have granted transaction-search access when connecting) and when it last ran.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/paypal/integrations/{id}/transactions/summary","host":["{{baseUrl}}"],"path":["api","paypal","integrations",":id","transactions","summary"],"variable":[{"key":"id","value":"12","description":"PayPal integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"gross\": 6975.85,\n    \"fees\": -256.05,\n    \"net\": 6719.8,\n    \"transfers\": -5400,\n    \"count\": 412,\n    \"match_status_counts\": {\n      \"matched\": 402,\n      \"recovered\": 3,\n      \"unmatched\": 1,\n      \"pending\": 1,\n      \"disputed\": 1,\n      \"denied\": 1,\n      \"not_applicable\": 3\n    },\n    \"import\": {\n      \"enabled\": true,\n      \"last_completed_at\": \"2026-10-05T02:04:40.000000Z\",\n      \"last_failed_at\": null,\n      \"last_error\": null,\n      \"imported_through\": \"2026-10-04T23:00:00.000000Z\"\n    }\n  }\n}"}]},{"name":"Import PayPal Transactions","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/paypal/integrations/{id}/transactions/import","host":["{{baseUrl}}"],"path":["api","paypal","integrations",":id","transactions","import"],"variable":[{"key":"id","value":"12","description":"PayPal integration instance ID"}]},"description":"Queues a background import of this PayPal account's transactions. The import resumes from where the last successful run stopped (re-reading the previous 3 days to pick up late status changes); a first import looks back 90 days. Imports run automatically every night; use this to refresh on demand.\n\n**Authentication:** Requires Bearer token with the integrations sync permission. Rate limited to 10 requests per minute.\n\nReturns `202` with the `tracked_job_log_id` of the queued job. Returns `422` when the PayPal account has not granted transaction-search access; reconnect the account with PayPal to grant it.\n\nReturns `409` when an import for this account is already queued or running; wait for it to finish (the nightly run included) before starting another.\n\nReturns `409` when PayPal is not yet available on SKU.io (see Get PayPal Availability).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/paypal/integrations/{id}/transactions/import","host":["{{baseUrl}}"],"path":["api","paypal","integrations",":id","transactions","import"],"variable":[{"key":"id","value":"12","description":"PayPal integration instance ID"}]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"PayPal transaction import queued.\",\n  \"data\": {\n    \"tracked_job_log_id\": 90211\n  }\n}"}]}]},{"name":"Google Sheets","item":[{"name":"Get Google Connection","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/google/connection","host":["{{baseUrl}}"],"path":["api","v2","google","connection"]},"description":"Return the authenticated user's Google connection status. The connection is per-user and grants access only to sheets the app creates or the user picks (drive.file scope).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/google/connection","host":["{{baseUrl}}"],"path":["api","v2","google","connection"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"connected\": true,\n    \"google_email\": \"jane@acme.com\",\n    \"scopes\": [\n      \"https://www.googleapis.com/auth/drive.file\"\n    ],\n    \"status\": \"connected\"\n  }\n}"}]},{"name":"Disconnect Google","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/google/connection","host":["{{baseUrl}}"],"path":["api","v2","google","connection"]},"description":"Revoke and remove the authenticated user's Google connection.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/google/connection","host":["{{baseUrl}}"],"path":["api","v2","google","connection"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"connected\": false,\n    \"google_email\": null,\n    \"scopes\": [],\n    \"status\": \"disconnected\"\n  },\n  \"message\": \"Google disconnected.\"\n}"}]},{"name":"Connect Google","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/google/connection/initiate","host":["{{baseUrl}}"],"path":["api","v2","google","connection","initiate"]},"description":"Start the Google authorization flow. Returns a consent URL to open in the browser; after the user consents, the connection is stored server-side. Only the non-sensitive drive.file scope is requested.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/google/connection/initiate","host":["{{baseUrl}}"],"path":["api","v2","google","connection","initiate"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"url\": \"https://accounts.google.com/o/oauth2/v2/auth?client_id=...&scope=https://www.googleapis.com/auth/drive.file&state=...\"\n  }\n}"}]},{"name":"Export to Google Sheet","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/google/sheets/export","host":["{{baseUrl}}"],"path":["api","v2","google","sheets","export"]},"description":"Create a new Google Sheet (or refresh an existing linked sheet when sheet_link_id is provided) populated with the supplied rows. entity_type must be a supported import entity (e.g. products). Include an `id` column so edits pulled back are applied as updates; rows with a blank id are inserts.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nRow sets above the sync limit are written as a background job — the response returns tracked_job_log_id and no url until it completes.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"entity_type\": \"products\",\n  \"rows\": [\n    {\n      \"id\": 5,\n      \"sku\": \"ABC-123\",\n      \"name\": \"Blue Widget\"\n    },\n    {\n      \"id\": \"\",\n      \"sku\": \"NEW-001\",\n      \"name\": \"New Product\"\n    }\n  ],\n  \"title\": \"SKU Products\",\n  \"filter_signature\": null,\n  \"sheet_link_id\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/google/sheets/export","host":["{{baseUrl}}"],"path":["api","v2","google","sheets","export"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"sheet_link_id\": 12,\n    \"spreadsheet_id\": \"1AbCdEfGhIjKlMnOpQrStUvWxYz0123456789ABCDEFG\",\n    \"url\": \"https://docs.google.com/spreadsheets/d/1AbCdEfGhIjKlMnOpQrStUvWxYz0123456789ABCDEFG/edit\",\n    \"row_count\": 2,\n    \"tracked_job_log_id\": null\n  }\n}"}]},{"name":"List Linked Google Sheets","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/google/sheets/links?entity_type=products","host":["{{baseUrl}}"],"path":["api","v2","google","sheets","links"],"query":[{"key":"entity_type","value":"products","description":"Entity type to list linked sheets for (required).","disabled":true}]},"description":"List the authenticated user's linked Google Sheets for an entity type.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/google/sheets/links?entity_type=products","host":["{{baseUrl}}"],"path":["api","v2","google","sheets","links"],"query":[{"key":"entity_type","value":"products","description":"Entity type to list linked sheets for (required).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"entity_type\": \"products\",\n      \"spreadsheet_id\": \"1AbCdEfGhIjKlMnOpQrStUvWxYz0123456789ABCDEFG\",\n      \"url\": \"https://docs.google.com/spreadsheets/d/1AbCdEfGhIjKlMnOpQrStUvWxYz0123456789ABCDEFG/edit\",\n      \"data_sheet_name\": \"Data\",\n      \"title\": \"SKU Products\",\n      \"column_mapping\": {\n        \"id\": \"id\",\n        \"SKU\": \"sku\",\n        \"Name\": \"name\"\n      },\n      \"filter_signature\": null,\n      \"row_count\": 2,\n      \"is_stale\": false,\n      \"created_by\": \"Jane\",\n      \"last_pulled_at\": null,\n      \"created_at\": \"2026-07-11T14:30:00.000000Z\",\n      \"updated_at\": \"2026-07-11T14:30:00.000000Z\"\n    }\n  ]\n}"}]},{"name":"Pull Changes From Google Sheet","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/google/sheets/{sheetLink}/pull","host":["{{baseUrl}}"],"path":["api","v2","google","sheets",":sheetLink","pull"],"variable":[{"key":"sheetLink","value":"12","description":"Linked sheet id (from List Linked Google Sheets)."}]},"description":"Read a linked sheet's current rows and start validation of the changes. Rows are matched on the `id` column (blank id inserts a new record). The response returns a data_import_id whose validation results and execution are handled through the standard import endpoints.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nReturns total_rows 0 with a null data_import_id when the sheet has only a header row.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/google/sheets/{sheetLink}/pull","host":["{{baseUrl}}"],"path":["api","v2","google","sheets",":sheetLink","pull"],"variable":[{"key":"sheetLink","value":"12","description":"Linked sheet id (from List Linked Google Sheets)."}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"data_import_id\": 45,\n    \"tracked_job_log_id\": 9881,\n    \"total_rows\": 2\n  },\n  \"message\": \"Sheet read; validation started.\"\n}"}]},{"name":"Rename Google Sheet","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/google/sheets/{sheetLink}","host":["{{baseUrl}}"],"path":["api","v2","google","sheets",":sheetLink"],"variable":[{"key":"sheetLink","value":"12","description":"Linked sheet id (from List Linked Google Sheets)."}]},"description":"Rename a linked Google Sheet. Renames the Google Sheet in your Drive and updates the stored label so the two stay in sync. Only the sheet's owner can rename it.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"title\": \"Q3 reorder planning\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/v2/google/sheets/{sheetLink}","host":["{{baseUrl}}"],"path":["api","v2","google","sheets",":sheetLink"],"variable":[{"key":"sheetLink","value":"12","description":"Linked sheet id (from List Linked Google Sheets)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"entity_type\": \"products\",\n    \"spreadsheet_id\": \"1ymf6PauZBgU0dWgLDWcZKUCBZ_bvPVVb1JOUWMTsM3A\",\n    \"url\": \"https://docs.google.com/spreadsheets/d/1ymf6PauZBgU0dWgLDWcZKUCBZ_bvPVVb1JOUWMTsM3A/edit\",\n    \"data_sheet_name\": \"Data\",\n    \"title\": \"Q3 reorder planning\",\n    \"column_mapping\": {\n      \"id\": \"id\",\n      \"SKU\": \"sku\",\n      \"Name\": \"name\"\n    },\n    \"filter_signature\": null,\n    \"row_count\": 27,\n    \"is_stale\": false,\n    \"created_by\": \"Jane\",\n    \"last_pulled_at\": \"2026-07-13T08:19:50.000000Z\",\n    \"created_at\": \"2026-07-13T08:10:00.000000Z\",\n    \"updated_at\": \"2026-07-13T09:02:00.000000Z\"\n  }\n}"}]},{"name":"Unlink Google Sheet","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/google/sheets/{sheetLink}","host":["{{baseUrl}}"],"path":["api","v2","google","sheets",":sheetLink"],"variable":[{"key":"sheetLink","value":"12","description":"Linked sheet id (from List Linked Google Sheets)."}]},"description":"Unlink a Google Sheet from SKU. This removes the link only — the Google Sheet itself stays untouched in your Google Drive. Only the sheet's owner can unlink it.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/google/sheets/{sheetLink}","host":["{{baseUrl}}"],"path":["api","v2","google","sheets",":sheetLink"],"variable":[{"key":"sheetLink","value":"12","description":"Linked sheet id (from List Linked Google Sheets)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Sheet unlinked.\"\n}"}]},{"name":"Export Products to Google Sheets by Filter","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/google/sheets/export-products","host":["{{baseUrl}}"],"path":["api","v2","google","sheets","export-products"]},"description":"Export products to a Google Sheet by the current filters instead of sending the rows. The sheet is built and written by a background job, so a large filtered export is not limited by request size or time. Returns a tracked_job_log_id to follow progress.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nscope: 'selected' exports only the given ids; 'filtered' exports every product matching filter / filter_groups / sort; 'all' exports the whole catalog (filters ignored). Provide ids only for the 'selected' scope. Pass sheet_link_id to overwrite an existing linked sheet.\n\ncolumns: optional comma-separated list of product column keys to place in the sheet (for example sku,name,unit_cost,min_stock_level,attribute_12,pricing_tier_3). Omit it to export every available column. id, sku and name are always included because they key the write-back. Unknown keys are ignored.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"scope\": \"filtered\",\n  \"filter\": {\n    \"search\": \"widget\"\n  },\n  \"filter_groups\": null,\n  \"sort\": \"-created_at\",\n  \"columns\": \"sku,name,unit_cost,min_stock_level,hts_code,country_of_origin\",\n  \"filter_signature\": null,\n  \"sheet_link_id\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/google/sheets/export-products","host":["{{baseUrl}}"],"path":["api","v2","google","sheets","export-products"]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 9876\n  },\n  \"message\": \"Exporting products to Google Sheets — track progress with the returned tracked_job_log_id.\"\n}"}]}]},{"name":"Airtable","item":[{"name":"Get OAuth Authorize URL","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/airtable/authorize?instance_id=7","host":["{{baseUrl}}"],"path":["api","airtable","authorize"],"query":[{"key":"instance_id","value":"7","disabled":true}]},"description":"Build the Airtable authorization URL that starts the connect flow. Open the returned authorization_url in a browser so the account owner can grant access; Airtable then redirects back to SKU.io to finish the connection. Pass the optional instance_id query parameter to re-authorize an existing connection instead of creating a new one.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/airtable/authorize?instance_id=7","host":["{{baseUrl}}"],"path":["api","airtable","authorize"],"query":[{"key":"instance_id","value":"7","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"authorization_url\": \"https://airtable.com/oauth2/v1/authorize?client_id=abc123&redirect_uri=https%3A%2F%2Facme.sku.io%2Fairtable%2Fauth%2Fcallback&response_type=code&scope=data.records%3Aread%20data.records%3Awrite%20schema.bases%3Aread%20schema.bases%3Awrite&state=acme.t0k3n&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256\"\n  }\n}"}]},{"name":"List Airtable Connections","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/airtable/integrations","host":["{{baseUrl}}"],"path":["api","airtable","integrations"]},"description":"List the Airtable connections for the current account, including connection status, the granting Airtable account, granted scopes, and monthly API usage against the plan cap.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/airtable/integrations","host":["{{baseUrl}}"],"path":["api","airtable","integrations"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 7,\n      \"name\": \"Ops Hub\",\n      \"connection_status\": \"connected\",\n      \"is_connected\": true,\n      \"account_id\": \"usr8Yh2Qw9Lm4Zx0\",\n      \"account_email\": \"jane@acme.com\",\n      \"granted_scopes\": [\n        \"data.records:read\",\n        \"data.records:write\",\n        \"schema.bases:read\",\n        \"schema.bases:write\"\n      ],\n      \"is_automatic_sync_enabled\": true,\n      \"api_usage\": {\n        \"used\": 1840,\n        \"cap\": 50000,\n        \"percent\": 4,\n        \"near_cap\": false,\n        \"over_cap\": false\n      },\n      \"created_at\": \"2026-07-01T09:15:00+00:00\",\n      \"updated_at\": \"2026-07-22T08:30:00+00:00\"\n    },\n    {\n      \"id\": 9,\n      \"name\": \"Analytics Base\",\n      \"connection_status\": \"connected\",\n      \"is_connected\": true,\n      \"account_id\": \"usr8Yh2Qw9Lm4Zx0\",\n      \"account_email\": \"jane@acme.com\",\n      \"granted_scopes\": [\n        \"data.records:read\",\n        \"data.records:write\",\n        \"schema.bases:read\",\n        \"schema.bases:write\"\n      ],\n      \"is_automatic_sync_enabled\": true,\n      \"api_usage\": {\n        \"used\": 1840,\n        \"cap\": 50000,\n        \"percent\": 4,\n        \"near_cap\": false,\n        \"over_cap\": false\n      },\n      \"created_at\": \"2026-07-01T09:15:00+00:00\",\n      \"updated_at\": \"2026-07-22T08:30:00+00:00\"\n    }\n  ]\n}"}]},{"name":"Verify Connection","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/whoami","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","whoami"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]},"description":"Verify a connection by calling Airtable with the stored token and returning the connected Airtable account id, email, and the scopes granted to the connection.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/whoami","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","whoami"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"account_id\": \"usr8Yh2Qw9Lm4Zx0\",\n    \"account_email\": \"jane@acme.com\",\n    \"scopes\": [\n      \"data.records:read\",\n      \"data.records:write\",\n      \"schema.bases:read\",\n      \"schema.bases:write\"\n    ]\n  }\n}"}]},{"name":"Disconnect Airtable","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]},"description":"Disconnect an Airtable connection. This revokes the stored token and removes every dataset mapping that belonged to the connection.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{}"}]},{"name":"List Syncable Datasets","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/airtable/datasets","host":["{{baseUrl}}"],"path":["api","airtable","datasets"]},"description":"List the catalog of SKU.io datasets that can be synced to Airtable. Each dataset lists the exportable fields available to map (with the target cell type) and whether it accepts inbound changes back from Airtable.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/airtable/datasets","host":["{{baseUrl}}"],"path":["api","airtable","datasets"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"dataset\": \"products\",\n      \"label\": \"Products\",\n      \"supports_inbound\": true,\n      \"fields\": [\n        {\n          \"source\": \"sku_io_id\",\n          \"label\": \"SKU.io ID\",\n          \"type\": \"text\",\n          \"key\": true\n        },\n        {\n          \"source\": \"name\",\n          \"label\": \"Name\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"barcode\",\n          \"label\": \"Barcode\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"average_cost\",\n          \"label\": \"Average Cost\",\n          \"type\": \"currency\"\n        },\n        {\n          \"source\": \"price\",\n          \"label\": \"Price\",\n          \"type\": \"currency\"\n        },\n        {\n          \"source\": \"brand.name\",\n          \"label\": \"Brand\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"gross_margin_pct\",\n          \"label\": \"Gross Margin %\",\n          \"type\": \"number\"\n        },\n        {\n          \"source\": \"on_hand\",\n          \"label\": \"On Hand\",\n          \"type\": \"number\"\n        },\n        {\n          \"source\": \"units_sold_90d\",\n          \"label\": \"Units Sold (90d)\",\n          \"type\": \"number\"\n        },\n        {\n          \"source\": \"days_of_cover\",\n          \"label\": \"Days of Cover\",\n          \"type\": \"number\"\n        },\n        {\n          \"source\": \"sell_through_pct\",\n          \"label\": \"Sell-through % (90d)\",\n          \"type\": \"number\"\n        },\n        {\n          \"source\": \"is_archived\",\n          \"label\": \"Archived\",\n          \"type\": \"checkbox\"\n        },\n        {\n          \"source\": \"updated_at\",\n          \"label\": \"Updated At\",\n          \"type\": \"datetime\"\n        }\n      ]\n    },\n    {\n      \"dataset\": \"sales_orders\",\n      \"label\": \"Sales Orders\",\n      \"supports_inbound\": true,\n      \"fields\": [\n        {\n          \"source\": \"sku_io_id\",\n          \"label\": \"SKU.io ID\",\n          \"type\": \"text\",\n          \"key\": true\n        },\n        {\n          \"source\": \"order_status\",\n          \"label\": \"Order Status\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"order_date\",\n          \"label\": \"Order Date\",\n          \"type\": \"datetime\"\n        },\n        {\n          \"source\": \"customer_name\",\n          \"label\": \"Customer Name\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"customer_email\",\n          \"label\": \"Customer Email\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"customer_company\",\n          \"label\": \"Customer Company\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"customer_phone\",\n          \"label\": \"Customer Phone\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"customer_po_number\",\n          \"label\": \"Customer PO\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"memo_for_customer\",\n          \"label\": \"Customer Memo\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"is_archived\",\n          \"label\": \"Archived\",\n          \"type\": \"checkbox\"\n        },\n        {\n          \"source\": \"updated_at\",\n          \"label\": \"Updated At\",\n          \"type\": \"datetime\"\n        }\n      ]\n    },\n    {\n      \"dataset\": \"suppliers\",\n      \"label\": \"Suppliers\",\n      \"supports_inbound\": true,\n      \"fields\": [\n        {\n          \"source\": \"sku_io_id\",\n          \"label\": \"SKU.io ID\",\n          \"type\": \"text\",\n          \"key\": true\n        },\n        {\n          \"source\": \"name\",\n          \"label\": \"Name\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"company_name\",\n          \"label\": \"Company\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"primary_contact_name\",\n          \"label\": \"Contact Name\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"email\",\n          \"label\": \"Email\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"messaging_phone\",\n          \"label\": \"Phone\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"website\",\n          \"label\": \"Website\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"is_archived\",\n          \"label\": \"Archived\",\n          \"type\": \"checkbox\"\n        },\n        {\n          \"source\": \"updated_at\",\n          \"label\": \"Updated At\",\n          \"type\": \"datetime\"\n        }\n      ]\n    },\n    {\n      \"dataset\": \"inventory_levels\",\n      \"label\": \"Inventory Levels\",\n      \"supports_inbound\": false,\n      \"fields\": [\n        {\n          \"source\": \"sku_io_id\",\n          \"label\": \"SKU.io ID\",\n          \"type\": \"text\",\n          \"key\": true\n        },\n        {\n          \"source\": \"product.sku\",\n          \"label\": \"SKU\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"warehouse.name\",\n          \"label\": \"Warehouse\",\n          \"type\": \"text\"\n        },\n        {\n          \"source\": \"inventory_available\",\n          \"label\": \"Available\",\n          \"type\": \"number\"\n        },\n        {\n          \"source\": \"updated_at\",\n          \"label\": \"Updated At\",\n          \"type\": \"datetime\"\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"List Airtable Bases","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/bases","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","bases"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]},"description":"List the Airtable bases the connection can access, with the permission level granted for each base.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/bases","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","bases"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": \"appXt3kQ9vB2mLpZ1\",\n      \"name\": \"Ops Hub\",\n      \"permissionLevel\": \"create\"\n    },\n    {\n      \"id\": \"appLm5nR8sT1uV4wY\",\n      \"name\": \"Warehouse Tracker\",\n      \"permissionLevel\": \"edit\"\n    }\n  ]\n}"}]},{"name":"List Base Tables","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/bases/{baseId}/tables","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","bases",":baseId","tables"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."},{"key":"baseId","value":"appXt3kQ9vB2mLpZ1","description":"Airtable base id (from List Airtable Bases)."}]},"description":"List the tables in an Airtable base along with each table's fields. Use the field ids to build a mapping's key field and field map.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/bases/{baseId}/tables","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","bases",":baseId","tables"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."},{"key":"baseId","value":"appXt3kQ9vB2mLpZ1","description":"Airtable base id (from List Airtable Bases)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": \"tbl9aRk2Pq7WnE5cX\",\n      \"name\": \"Products\",\n      \"primaryFieldId\": \"fldName0000000001\",\n      \"fields\": [\n        {\n          \"id\": \"fldName0000000001\",\n          \"name\": \"Name\",\n          \"type\": \"singleLineText\"\n        },\n        {\n          \"id\": \"fldSkuIoIdKey0001\",\n          \"name\": \"SKU.io ID\",\n          \"type\": \"singleLineText\"\n        },\n        {\n          \"id\": \"fldPrice000000001\",\n          \"name\": \"Price\",\n          \"type\": \"currency\"\n        },\n        {\n          \"id\": \"fldBrand000000001\",\n          \"name\": \"Brand\",\n          \"type\": \"singleLineText\"\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"List Mappings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/mappings","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","mappings"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]},"description":"List the dataset mappings configured on a connection, with their target base and table, sync schedule, last sync result, and error count.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/mappings","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","mappings"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 42,\n      \"integration_instance_id\": 7,\n      \"dataset\": \"products\",\n      \"dataset_label\": \"Products\",\n      \"direction\": \"outbound\",\n      \"base_id\": \"appXt3kQ9vB2mLpZ1\",\n      \"table_id\": \"tbl9aRk2Pq7WnE5cX\",\n      \"key_field_id\": \"fldSkuIoIdKey0001\",\n      \"field_map\": [\n        {\n          \"source\": \"name\",\n          \"target_field_id\": \"fldName0000000001\"\n        },\n        {\n          \"source\": \"price\",\n          \"target_field_id\": \"fldPrice000000001\"\n        },\n        {\n          \"source\": \"brand.name\",\n          \"target_field_id\": \"fldBrand000000001\"\n        }\n      ],\n      \"filters\": null,\n      \"delete_mode\": \"flag\",\n      \"schedule\": \"hourly\",\n      \"realtime_enabled\": true,\n      \"apply_mode\": null,\n      \"auto_create\": true,\n      \"webhook_active\": false,\n      \"is_enabled\": true,\n      \"error_count\": 0,\n      \"last_synced_at\": \"2026-07-22T08:00:00+00:00\",\n      \"last_result\": {\n        \"created\": 4,\n        \"updated\": 120,\n        \"deleted\": 0,\n        \"skipped\": 2,\n        \"finished_at\": \"2026-07-22T08:00:07+00:00\"\n      },\n      \"created_at\": \"2026-07-10T12:00:00+00:00\",\n      \"updated_at\": \"2026-07-22T08:00:07+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"{{protocol}}://{{domain}}/api/airtable/instances/7/mappings?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"Create Mapping","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/mappings","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","mappings"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]},"description":"Create a dataset mapping that syncs a SKU.io dataset to a table in an Airtable base.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nBody fields:\n- dataset (string, required): the SKU.io dataset to sync. One of products, inventory_levels, sales_orders, sales_order_lines, purchase_orders, purchase_order_lines, suppliers.\n- direction (string, optional, default outbound): outbound sends SKU.io data to Airtable; inbound reads changes back. Inbound is only supported for the products dataset.\n- base_id (string, required): the target Airtable base id.\n- table_id (string, required): the target table id within the base.\n- key_field_id (string, required): the Airtable field that stores the SKU.io id and is used as the merge key for upserts.\n- field_map (array, required, min 1): each entry maps a SKU.io source field to an Airtable target field. Each item has source (string) and target_field_id (string).\n- filters (object, optional): optional filter conditions restricting which records sync.\n- delete_mode (string, optional, default flag): how records removed in SKU.io are handled in Airtable. One of flag, delete, ignore.\n- schedule (string, optional, default hourly): automatic sync cadence. One of fifteen_minutes, hourly, daily, manual.\n- realtime_enabled (boolean, optional, default false): push changes as they happen.\n- apply_mode (string, optional): for inbound mappings, whether changes are queued for review or applied automatically. One of review, auto.\n- auto_create (boolean, optional, default false): when true, create missing Airtable records during sync instead of only updating existing rows.\n- is_enabled (boolean, optional, default true): whether the mapping is active.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"dataset\": \"products\",\n  \"direction\": \"outbound\",\n  \"base_id\": \"appXt3kQ9vB2mLpZ1\",\n  \"table_id\": \"tbl9aRk2Pq7WnE5cX\",\n  \"key_field_id\": \"fldSkuIoIdKey0001\",\n  \"field_map\": [\n    {\n      \"source\": \"name\",\n      \"target_field_id\": \"fldName0000000001\"\n    },\n    {\n      \"source\": \"price\",\n      \"target_field_id\": \"fldPrice000000001\"\n    },\n    {\n      \"source\": \"brand.name\",\n      \"target_field_id\": \"fldBrand000000001\"\n    }\n  ],\n  \"filters\": null,\n  \"delete_mode\": \"flag\",\n  \"schedule\": \"hourly\",\n  \"realtime_enabled\": true,\n  \"apply_mode\": null,\n  \"auto_create\": true,\n  \"is_enabled\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/mappings","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","mappings"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 42,\n  \"integration_instance_id\": 7,\n  \"dataset\": \"products\",\n  \"dataset_label\": \"Products\",\n  \"direction\": \"outbound\",\n  \"base_id\": \"appXt3kQ9vB2mLpZ1\",\n  \"table_id\": \"tbl9aRk2Pq7WnE5cX\",\n  \"key_field_id\": \"fldSkuIoIdKey0001\",\n  \"field_map\": [\n    {\n      \"source\": \"name\",\n      \"target_field_id\": \"fldName0000000001\"\n    },\n    {\n      \"source\": \"price\",\n      \"target_field_id\": \"fldPrice000000001\"\n    },\n    {\n      \"source\": \"brand.name\",\n      \"target_field_id\": \"fldBrand000000001\"\n    }\n  ],\n  \"filters\": null,\n  \"delete_mode\": \"flag\",\n  \"schedule\": \"hourly\",\n  \"realtime_enabled\": true,\n  \"apply_mode\": null,\n  \"auto_create\": true,\n  \"webhook_active\": false,\n  \"is_enabled\": true,\n  \"error_count\": 0,\n  \"last_synced_at\": \"2026-07-22T08:00:00+00:00\",\n  \"last_result\": {\n    \"created\": 4,\n    \"updated\": 120,\n    \"deleted\": 0,\n    \"skipped\": 2,\n    \"finished_at\": \"2026-07-22T08:00:07+00:00\"\n  },\n  \"created_at\": \"2026-07-10T12:00:00+00:00\",\n  \"updated_at\": \"2026-07-22T08:00:07+00:00\"\n}"}]},{"name":"Update Mapping","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/mappings/{mapping}","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","mappings",":mapping"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."},{"key":"mapping","value":"42","description":"Dataset mapping id (from List Mappings)."}]},"description":"Update an existing dataset mapping. Send only the fields you want to change; omitted fields keep their current value.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nBody fields (all optional): key_field_id (string), field_map (array of source / target_field_id entries), filters (object or null), delete_mode (flag, delete, ignore), schedule (fifteen_minutes, hourly, daily, manual), realtime_enabled (boolean), apply_mode (review, auto, or null), auto_create (boolean), is_enabled (boolean).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"schedule\": \"daily\",\n  \"realtime_enabled\": false,\n  \"auto_create\": false,\n  \"is_enabled\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/mappings/{mapping}","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","mappings",":mapping"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."},{"key":"mapping","value":"42","description":"Dataset mapping id (from List Mappings)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 42,\n  \"integration_instance_id\": 7,\n  \"dataset\": \"products\",\n  \"dataset_label\": \"Products\",\n  \"direction\": \"outbound\",\n  \"base_id\": \"appXt3kQ9vB2mLpZ1\",\n  \"table_id\": \"tbl9aRk2Pq7WnE5cX\",\n  \"key_field_id\": \"fldSkuIoIdKey0001\",\n  \"field_map\": [\n    {\n      \"source\": \"name\",\n      \"target_field_id\": \"fldName0000000001\"\n    },\n    {\n      \"source\": \"price\",\n      \"target_field_id\": \"fldPrice000000001\"\n    },\n    {\n      \"source\": \"brand.name\",\n      \"target_field_id\": \"fldBrand000000001\"\n    }\n  ],\n  \"filters\": null,\n  \"delete_mode\": \"flag\",\n  \"schedule\": \"daily\",\n  \"realtime_enabled\": false,\n  \"apply_mode\": null,\n  \"auto_create\": false,\n  \"webhook_active\": false,\n  \"is_enabled\": true,\n  \"error_count\": 0,\n  \"last_synced_at\": \"2026-07-22T08:00:00+00:00\",\n  \"last_result\": {\n    \"created\": 4,\n    \"updated\": 120,\n    \"deleted\": 0,\n    \"skipped\": 2,\n    \"finished_at\": \"2026-07-22T08:00:07+00:00\"\n  },\n  \"created_at\": \"2026-07-10T12:00:00+00:00\",\n  \"updated_at\": \"2026-07-22T08:00:07+00:00\"\n}"}]},{"name":"Delete Mapping","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/mappings/{mapping}","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","mappings",":mapping"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."},{"key":"mapping","value":"42","description":"Dataset mapping id (from List Mappings)."}]},"description":"Delete a dataset mapping. This stops future syncs for the mapping; records already written to Airtable are left in place.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"No Content","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/mappings/{mapping}","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","mappings",":mapping"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."},{"key":"mapping","value":"42","description":"Dataset mapping id (from List Mappings)."}]}},"status":"No Content","code":204,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{}"}]},{"name":"Sync Mapping","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/mappings/{mapping}/sync","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","mappings",":mapping","sync"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."},{"key":"mapping","value":"42","description":"Dataset mapping id (from List Mappings)."}]},"description":"Trigger a sync run for a mapping now. The sync runs in the background; the response returns a tracked_job_log_id you can use to follow its progress.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/mappings/{mapping}/sync","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","mappings",":mapping","sync"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."},{"key":"mapping","value":"42","description":"Dataset mapping id (from List Mappings)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 98765\n  }\n}"}]},{"name":"Provision Template Table","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/provision-template","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","provision-template"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]},"description":"Provision a ready-made Airtable base for the connection. This creates a pre-built base with tables and mappings for common datasets and runs an initial backfill in the background.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nBody fields:\n- workspace_id (string, required): the Airtable workspace to create the base in.\n- base_name (string, optional, default \"SKU.io Ops Hub\"): the name for the new base.\n- order_window_days (integer, optional, default 90, range 1–3650): how many days of recent orders to backfill.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"workspace_id\": \"wspYq2Zt6Bn8Kx3Rd\",\n  \"base_name\": \"SKU.io Ops Hub\",\n  \"order_window_days\": 90\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/provision-template","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","provision-template"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 98766\n  }\n}"}]},{"name":"List Inbound Changes","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/inbound-changes","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","inbound-changes"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]},"description":"List the queued inbound changes proposed from Airtable and awaiting review. Each change targets a SKU.io record (product, sales order, or supplier): link_type/link_id identify it, link_reference and link_route present it for navigation, and proposed lists each mapped field's old and new value. Apply or reject them from the review queue.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nSupports pagination (page, per_page — default 10), sort (id, status, created_at, updated_at; prefix - for DESC), and filter[status] (pending, applied, rejected, failed, unmatched).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/inbound-changes","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","inbound-changes"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 301,\n      \"mapping_id\": 42,\n      \"airtable_record_id\": \"recQw7Yh2Kp9Lm4Zx\",\n      \"link_type\": \"App\\\\Models\\\\Product\",\n      \"link_id\": 512,\n      \"link_type_label\": \"Product\",\n      \"link_reference\": \"ABC-123\",\n      \"link_route\": \"/products/512\",\n      \"proposed\": {\n        \"barcode\": {\n          \"old\": \"0000000000\",\n          \"new\": 123456789012\n        },\n        \"name\": {\n          \"old\": \"Blue Widget\",\n          \"new\": \"Blue Widget (2026)\"\n        }\n      },\n      \"status\": \"pending\",\n      \"error\": null,\n      \"created_at\": \"2026-07-22T07:45:00+00:00\",\n      \"updated_at\": \"2026-07-22T07:45:00+00:00\"\n    },\n    {\n      \"id\": 302,\n      \"mapping_id\": 43,\n      \"airtable_record_id\": \"recSalesOrderAB12\",\n      \"link_type\": \"App\\\\Models\\\\SalesOrder\",\n      \"link_id\": 88,\n      \"link_type_label\": \"Sales Order\",\n      \"link_reference\": \"SO-1042\",\n      \"link_route\": \"/orders/sales-orders/88\",\n      \"proposed\": {\n        \"customer_company\": {\n          \"old\": \"Acme LLC\",\n          \"new\": \"Acme Inc\"\n        }\n      },\n      \"status\": \"pending\",\n      \"error\": null,\n      \"created_at\": \"2026-07-22T07:50:00+00:00\",\n      \"updated_at\": \"2026-07-22T07:50:00+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"{{protocol}}://{{domain}}/api/airtable/instances/7/inbound-changes?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"to\": 2,\n  \"total\": 2\n}"}]},{"name":"Apply Inbound Changes","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/inbound-changes/apply","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","inbound-changes","apply"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]},"description":"Apply queued inbound changes back into SKU.io. Send \"all\": true to apply every pending change for the connection, or \"ids\": [..] to apply specific changes. Changes are applied in the background; the response returns a tracked_job_log_id to follow progress.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nBody fields:\n- all (boolean, optional): apply every pending change when true.\n- ids (array of integers, optional): the specific change ids to apply when all is not set.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"all\": false,\n  \"ids\": [\n    301,\n    302\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/inbound-changes/apply","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","inbound-changes","apply"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 98767\n  }\n}"}]},{"name":"Reject Inbound Changes","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/inbound-changes/reject","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","inbound-changes","reject"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]},"description":"Reject queued inbound changes so they are not applied. Send \"all\": true to reject every pending change for the connection, or \"ids\": [..] to reject specific changes.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nBody fields:\n- all (boolean, optional): reject every pending change when true.\n- ids (array of integers, optional): the specific change ids to reject when all is not set.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"all\": false,\n  \"ids\": [\n    303\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/airtable/instances/{instance}/inbound-changes/reject","host":["{{baseUrl}}"],"path":["api","airtable","instances",":instance","inbound-changes","reject"],"variable":[{"key":"instance","value":"7","description":"Airtable connection id (from List Airtable Connections)."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"rejected\": 1\n  }\n}"}]}]},{"name":"SPS Commerce","item":[{"name":"List SPS Commerce Integrations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/integrations","host":["{{baseUrl}}"],"path":["api","spscommerce","integrations"]},"description":"List SPS Commerce connections for this account. Credentials and tokens are never returned.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/spscommerce/integrations","host":["{{baseUrl}}"],"path":["api","spscommerce","integrations"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"data\": [\n    {\n      \"id\": 2,\n      \"name\": \"SPS Commerce\",\n      \"is_authenticated\": true,\n      \"is_auto_polling_enabled\": true,\n      \"sync_status\": \"Active\",\n      \"environment\": \"sandbox\",\n      \"receiver_id\": \"ACMEVENDOR\",\n      \"sender_id\": null,\n      \"send_invoices\": false,\n      \"send_inventory_advice\": false,\n      \"inventory_advice_warehouse_ids\": [\n        1\n      ],\n      \"inventory_advice_buffer\": 2,\n      \"pack_level_asn\": false,\n      \"gs1_company_prefix\": null,\n      \"sscc_extension_digit\": 0,\n      \"carton_label_ids\": {},\n      \"sales_channel_id\": 3,\n      \"created_at\": \"2026-10-04T18:22:05.000000Z\",\n      \"updated_at\": \"2026-10-04T20:31:40.000000Z\"\n    }\n  ]\n}"}]},{"name":"Initialize SPS Commerce OAuth Flow","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/spscommerce/integrations/initialize","host":["{{baseUrl}}"],"path":["api","spscommerce","integrations","initialize"]},"description":"Initialize the SPS Commerce OAuth 2.0 authorization flow.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nClient credentials (client_id, client_secret) are read from server config (SPS_COMMERCE_CLIENT_ID, SPS_COMMERCE_CLIENT_SECRET env vars).\n\n**Request Body:**\n- `name` - Integration name (optional, defaults to \"SPS Commerce\")\n- `integration_settings` - EDI configuration (optional):\n  - `sender_id` - Your EDI sender ID\n  - `receiver_id` - Your EDI receiver ID (trading partner)\n  - `protocol` - Communication protocol (\"api\")\n  - `sandbox` - Use sandbox environment (true/false)\n\n**Process:**\n1. Generates a unique state parameter for CSRF protection\n2. Creates SPS Commerce OAuth authorization URL\n3. Stores credentials and settings temporarily in cache for OAuth completion\n4. Sets collection variable 'sps-state' for use in complete endpoint\n\n**Response:**\n- `state` - UUID to use in complete endpoint (matches OAuth callback state parameter)\n- `authorization_url` - Visit this URL in browser to authorize SPS Commerce access\n\n**Next Steps:**\n1. Visit the authorization_url in browser\n2. Complete SPS Commerce authorization\n3. Use the Complete endpoint with the stored state and your auto-polling preference\n\n**Security:**\n- State parameter prevents CSRF attacks\n- OAuth data temporarily cached for 1 hour\n- OAuth follows SPS Commerce security best practices","body":{"mode":"raw","raw":"{\n  \"name\": \"SPS Commerce\",\n  \"integration_settings\": {\n    \"sender_id\": \"YOUR_SENDER_ID\",\n    \"receiver_id\": \"YOUR_RECEIVER_ID\",\n    \"protocol\": \"api\",\n    \"sandbox\": true\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/spscommerce/integrations/initialize","host":["{{baseUrl}}"],"path":["api","spscommerce","integrations","initialize"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"state\": \"6f9d2cd0-e24d-4c36-80ea-33c14692547a\",\n    \"authorization_url\": \"https://auth.spscommerce.com/authorize?client_id=YOUR_CLIENT_ID&audience=https%3A%2F%2Fspscommerce.com&redirect_uri=https%3A%2F%2Fyour-app.com%2Fspscommerce%2Fcallback&response_type=code&scope=offline_access&state=6f9d2cd0-e24d-4c36-80ea-33c14692547a\"\n  }\n}"}]},{"name":"Complete SPS Commerce Integration Setup","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/spscommerce/integrations/complete","host":["{{baseUrl}}"],"path":["api","spscommerce","integrations","complete"]},"description":"Finish connecting after the SPS Commerce consent screen redirected back: exchanges the authorization code, stores the token, creates the connection and its sales channel.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token and the integrations.update permission.\n\nBody: state (required, from Initialize), receiver_id (required, your SPS trading-partner ID), sender_id (optional), name (optional), is_auto_polling_enabled (optional, default false).\n\nReturns 422 when receiver_id is missing; 400 when the state was not issued to this account or has expired.","body":{"mode":"raw","raw":"{\n  \"state\": \"https://acme.sku.io~acme_8f1e2d3c-4b5a-4c6d-9e8f-0a1b2c3d4e5f\",\n  \"name\": \"SPS Commerce\",\n  \"receiver_id\": \"ACMEVENDOR\",\n  \"sender_id\": null,\n  \"is_auto_polling_enabled\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/spscommerce/integrations/complete","host":["{{baseUrl}}"],"path":["api","spscommerce","integrations","complete"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"data\": {\n    \"integration_instance_id\": 2,\n    \"name\": \"SPS Commerce\",\n    \"is_authenticated\": true,\n    \"is_auto_polling_enabled\": true,\n    \"created_at\": \"2026-10-04T18:22:05.000000Z\"\n  }\n}"}]},{"name":"Test SPS Commerce Connection","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/integrations/{id}/test-connection","host":["{{baseUrl}}"],"path":["api","spscommerce","integrations",":id","test-connection"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Test the connection to SPS Commerce Transaction API.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nThis endpoint will use the stored OAuth tokens to make a test API call to SPS Commerce and verify the connection is working.\n\n**No Request Body Required**\n\n**Response:**\n- `success` - Connection test result\n- `message` - Status message\n- `data.success` - Whether connection was successful\n- `data.message` - Detailed status message"}},{"name":"Update SPS Commerce Settings","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/spscommerce/integrations/{id}/settings","host":["{{baseUrl}}"],"path":["api","spscommerce","integrations",":id","settings"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Update a connection's name, trading-partner IDs, automatic polling, EDI invoicing, inventory advice and carton-level ship notices. Send only the fields you change.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token and the integrations.update permission.\n\nFields: name, receiver_id, sender_id (null to clear), is_auto_polling_enabled, send_invoices (when true, an 810 invoice is sent to the trading partner for each shipment after its 856 advance ship notice is sent, billing the shipped quantities at the purchase order price; defaults to false), send_inventory_advice (when true, each trading partner is sent a daily 846 inventory advice with the available quantity of every item it has ordered; defaults to false), pack_level_asn (when true, each 856 reports the shipment carton by carton with an SSCC-18 per carton; requires gs1_company_prefix), gs1_company_prefix (7–10 digit GS1 company prefix the SSCC-18 numbers are built from; required when pack_level_asn is true), sscc_extension_digit (0–9, default 0), carton_label_ids (object mapping each trading partner ID to the SPS label template used to render its carton labels).\n- inventory_advice_warehouse_ids (array of integers, optional): warehouses whose available stock the daily 846 counts; an empty array counts available-to-sell across every warehouse\n- inventory_advice_buffer (integer, optional, min 0): units held back from every item's 846 quantity","body":{"mode":"raw","raw":"{\n  \"name\": \"SPS Commerce\",\n  \"receiver_id\": \"ACMEVENDOR\",\n  \"sender_id\": null,\n  \"is_auto_polling_enabled\": true,\n  \"send_invoices\": true,\n  \"send_inventory_advice\": true,\n  \"pack_level_asn\": true,\n  \"gs1_company_prefix\": \"0614141\",\n  \"sscc_extension_digit\": 0,\n  \"carton_label_ids\": {\n    \"WALMARTDC\": \"walmart-dc-ucc128\"\n  },\n  \"inventory_advice_warehouse_ids\": [\n    1\n  ],\n  \"inventory_advice_buffer\": 2\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/spscommerce/integrations/{id}/settings","host":["{{baseUrl}}"],"path":["api","spscommerce","integrations",":id","settings"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"data\": {\n    \"id\": 2,\n    \"name\": \"SPS Commerce\",\n    \"is_authenticated\": true,\n    \"is_auto_polling_enabled\": true,\n    \"sync_status\": \"Active\",\n    \"environment\": \"sandbox\",\n    \"receiver_id\": \"ACMEVENDOR\",\n    \"sender_id\": null,\n    \"send_invoices\": false,\n    \"send_inventory_advice\": false,\n    \"inventory_advice_warehouse_ids\": [\n      1\n    ],\n    \"inventory_advice_buffer\": 2,\n    \"pack_level_asn\": false,\n    \"gs1_company_prefix\": null,\n    \"sscc_extension_digit\": 0,\n    \"carton_label_ids\": {},\n    \"sales_channel_id\": 3,\n    \"created_at\": \"2026-10-04T18:22:05.000000Z\",\n    \"updated_at\": \"2026-10-04T20:31:40.000000Z\"\n  }\n}"}]},{"name":"Delete SPS Commerce Integration","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/integrations/{id}","host":["{{baseUrl}}"],"path":["api","spscommerce","integrations",":id"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"Disconnect SPS Commerce: removes the connection and its EDI documents. Refused once the connection has created sales orders.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token and the integrations.delete permission."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/spscommerce/integrations/{id}","host":["{{baseUrl}}"],"path":["api","spscommerce","integrations",":id"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"message\": \"SPS Commerce integration instance deleted successfully\",\n  \"data\": {\n    \"deleted_instance_id\": 2,\n    \"deleted_instance_name\": \"SPS Commerce\"\n  }\n}"}]},{"name":"List Carton Label Templates","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/integrations/{id}/label-templates","host":["{{baseUrl}}"],"path":["api","spscommerce","integrations",":id","label-templates"],"variable":[{"key":"id","value":"","description":"The record ID."}]},"description":"List the carton label templates SPS Commerce can render for the account, and the trading partners (from received purchase orders) to choose a template for, with each partner's current choice. Choices are saved with carton_label_ids on Update SPS Commerce Settings.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/spscommerce/integrations/{id}/label-templates","host":["{{baseUrl}}"],"path":["api","spscommerce","integrations",":id","label-templates"],"variable":[{"key":"id","value":"","description":"The record ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"data\": {\n    \"templates\": [\n      {\n        \"id\": \"15aa5856\",\n        \"name\": \"Sample Label - Bulk Import\",\n        \"description\": \"SPS Commerce created sample Bulk Import shipping label.\"\n      }\n    ],\n    \"trading_partners\": [\n      {\n        \"id\": \"SPSALLTESTID\",\n        \"label_template_id\": \"15aa5856\"\n      }\n    ]\n  }\n}"}]},{"name":"Get Shipment Packing","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/fulfillments/{salesOrderFulfillment}/packing","host":["{{baseUrl}}"],"path":["api","spscommerce","fulfillments",":salesOrderFulfillment","packing"],"variable":[{"key":"salesOrderFulfillment","value":"","description":"The sales order fulfillment ID."}]},"description":"Get a shipment's carton packing: what shipped versus what is packed, the cartons with their SSCC-18 numbers and label status, the freight references, and the ship notice (856) status (not_shipped, waiting_for_packing, ready, sent, failed). A connection that sends carton-level ship notices holds a shipped order's 856 until its packing is finalized.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token. Available for shipments of SPS Commerce EDI orders; other shipments return 404.\n\ninvoice_status: off (invoices turned off for the connection), waiting (sent after the ship notice), sending, sent, failed. po_number: the SPS purchase order the shipment answers."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/spscommerce/fulfillments/{salesOrderFulfillment}/packing","host":["{{baseUrl}}"],"path":["api","spscommerce","fulfillments",":salesOrderFulfillment","packing"],"variable":[{"key":"salesOrderFulfillment","value":"","description":"The sales order fulfillment ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"data\": {\n    \"sales_order_fulfillment_id\": 2,\n    \"integration_instance_id\": 4,\n    \"po_number\": \"PO584615-1\",\n    \"pack_level_enabled\": true,\n    \"trading_partner_id\": \"SPSALLTESTID\",\n    \"label_template_id\": \"15aa5856\",\n    \"has_company_prefix\": true,\n    \"is_fully_packed\": true,\n    \"is_finalized\": true,\n    \"is_locked\": false,\n    \"ship_notice_status\": \"ready\",\n    \"invoice_status\": \"waiting\",\n    \"freight\": {\n      \"bill_of_lading_number\": \"BOL-0001842\",\n      \"carrier_pro_number\": \"PRO-5531907\",\n      \"carrier_scac\": \"ODFL\",\n      \"carrier_name\": \"Old Dominion\"\n    },\n    \"lines\": [\n      {\n        \"sales_order_fulfillment_line_id\": 31,\n        \"product_id\": 7,\n        \"sku\": \"SPSSupplierHD-20\",\n        \"name\": \"Sample Product HD-20\",\n        \"case_quantity\": 2,\n        \"shipped\": 5,\n        \"packed\": 5,\n        \"remaining\": 0,\n        \"unit_weight_lb\": 47.99\n      }\n    ],\n    \"cartons\": [\n      {\n        \"id\": 11,\n        \"carton_number\": 1,\n        \"sscc\": \"006141410000000012\",\n        \"weight\": 95.98,\n        \"weight_unit\": \"LB\",\n        \"length\": 16,\n        \"width\": 12,\n        \"height\": 10,\n        \"dimension_unit\": \"IN\",\n        \"tracking_number\": null,\n        \"has_label\": true,\n        \"label_fetched_at\": \"2026-10-08T23:08:12.000000Z\",\n        \"lines\": [\n          {\n            \"sales_order_fulfillment_line_id\": 31,\n            \"quantity\": 2\n          }\n        ]\n      }\n    ]\n  }\n}"}]},{"name":"Update Shipment Packing","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/spscommerce/fulfillments/{salesOrderFulfillment}/packing","host":["{{baseUrl}}"],"path":["api","spscommerce","fulfillments",":salesOrderFulfillment","packing"],"variable":[{"key":"salesOrderFulfillment","value":"","description":"The sales order fulfillment ID."}]},"description":"Replace the shipment's cartons. Partial packing may be saved; packing more of a line than shipped, or an empty carton, is refused with 422. Refused once packing is finalized (reopen first) or once the 856 has been sent.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nFields: cartons[] (required, may be empty) — each with lines[] (sales_order_fulfillment_line_id, quantity > 0), weight, weight_unit (LB|KG|OZ|G), length, width, height, dimension_unit (IN|CM), tracking_number.\n\nAuthentication: Requires Bearer token. Available for shipments of SPS Commerce EDI orders; other shipments return 404. Requires the sales_orders.update permission.","body":{"mode":"raw","raw":"{\n  \"cartons\": [\n    {\n      \"lines\": [\n        {\n          \"sales_order_fulfillment_line_id\": 31,\n          \"quantity\": 2\n        },\n        {\n          \"sales_order_fulfillment_line_id\": 32,\n          \"quantity\": 1\n        }\n      ],\n      \"weight\": 12.5,\n      \"weight_unit\": \"LB\",\n      \"length\": 16,\n      \"width\": 12,\n      \"height\": 10,\n      \"dimension_unit\": \"IN\",\n      \"tracking_number\": \"1Z999AA10123456784\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/spscommerce/fulfillments/{salesOrderFulfillment}/packing","host":["{{baseUrl}}"],"path":["api","spscommerce","fulfillments",":salesOrderFulfillment","packing"],"variable":[{"key":"salesOrderFulfillment","value":"","description":"The sales order fulfillment ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"data\": {\n    \"sales_order_fulfillment_id\": 2,\n    \"integration_instance_id\": 4,\n    \"po_number\": \"PO584615-1\",\n    \"pack_level_enabled\": true,\n    \"trading_partner_id\": \"SPSALLTESTID\",\n    \"label_template_id\": \"15aa5856\",\n    \"has_company_prefix\": true,\n    \"is_fully_packed\": true,\n    \"is_finalized\": true,\n    \"is_locked\": false,\n    \"ship_notice_status\": \"ready\",\n    \"invoice_status\": \"waiting\",\n    \"freight\": {\n      \"bill_of_lading_number\": \"BOL-0001842\",\n      \"carrier_pro_number\": \"PRO-5531907\",\n      \"carrier_scac\": \"ODFL\",\n      \"carrier_name\": \"Old Dominion\"\n    },\n    \"lines\": [\n      {\n        \"sales_order_fulfillment_line_id\": 31,\n        \"product_id\": 7,\n        \"sku\": \"SPSSupplierHD-20\",\n        \"name\": \"Sample Product HD-20\",\n        \"case_quantity\": 2,\n        \"shipped\": 5,\n        \"packed\": 5,\n        \"remaining\": 0,\n        \"unit_weight_lb\": 47.99\n      }\n    ],\n    \"cartons\": [\n      {\n        \"id\": 11,\n        \"carton_number\": 1,\n        \"sscc\": \"006141410000000012\",\n        \"weight\": 95.98,\n        \"weight_unit\": \"LB\",\n        \"length\": 16,\n        \"width\": 12,\n        \"height\": 10,\n        \"dimension_unit\": \"IN\",\n        \"tracking_number\": null,\n        \"has_label\": true,\n        \"label_fetched_at\": \"2026-10-08T23:08:12.000000Z\",\n        \"lines\": [\n          {\n            \"sales_order_fulfillment_line_id\": 31,\n            \"quantity\": 2\n          }\n        ]\n      }\n    ]\n  }\n}"}]},{"name":"Auto-Pack Shipment","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/spscommerce/fulfillments/{salesOrderFulfillment}/packing/auto-pack","host":["{{baseUrl}}"],"path":["api","spscommerce","fulfillments",":salesOrderFulfillment","packing","auto-pack"],"variable":[{"key":"salesOrderFulfillment","value":"","description":"The sales order fulfillment ID."}]},"description":"Build and save a packing plan automatically. mode=case_pack fills full cartons of each product's case quantity, puts each remainder in its own carton and products with no case quantity in one shared carton; mode=single_carton packs everything into one carton. Carton weights are estimated from product weights. Returns the new packing and any warnings.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token. Available for shipments of SPS Commerce EDI orders; other shipments return 404. Requires the sales_orders.update permission.","body":{"mode":"raw","raw":"{\n  \"mode\": \"case_pack\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/spscommerce/fulfillments/{salesOrderFulfillment}/packing/auto-pack","host":["{{baseUrl}}"],"path":["api","spscommerce","fulfillments",":salesOrderFulfillment","packing","auto-pack"],"variable":[{"key":"salesOrderFulfillment","value":"","description":"The sales order fulfillment ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"data\": {\n    \"sales_order_fulfillment_id\": 2,\n    \"integration_instance_id\": 4,\n    \"po_number\": \"PO584615-1\",\n    \"pack_level_enabled\": true,\n    \"trading_partner_id\": \"SPSALLTESTID\",\n    \"label_template_id\": \"15aa5856\",\n    \"has_company_prefix\": true,\n    \"is_fully_packed\": true,\n    \"is_finalized\": true,\n    \"is_locked\": false,\n    \"ship_notice_status\": \"ready\",\n    \"invoice_status\": \"waiting\",\n    \"freight\": {\n      \"bill_of_lading_number\": \"BOL-0001842\",\n      \"carrier_pro_number\": \"PRO-5531907\",\n      \"carrier_scac\": \"ODFL\",\n      \"carrier_name\": \"Old Dominion\"\n    },\n    \"lines\": [\n      {\n        \"sales_order_fulfillment_line_id\": 31,\n        \"product_id\": 7,\n        \"sku\": \"SPSSupplierHD-20\",\n        \"name\": \"Sample Product HD-20\",\n        \"case_quantity\": 2,\n        \"shipped\": 5,\n        \"packed\": 5,\n        \"remaining\": 0,\n        \"unit_weight_lb\": 47.99\n      }\n    ],\n    \"cartons\": [\n      {\n        \"id\": 11,\n        \"carton_number\": 1,\n        \"sscc\": \"006141410000000012\",\n        \"weight\": 95.98,\n        \"weight_unit\": \"LB\",\n        \"length\": 16,\n        \"width\": 12,\n        \"height\": 10,\n        \"dimension_unit\": \"IN\",\n        \"tracking_number\": null,\n        \"has_label\": true,\n        \"label_fetched_at\": \"2026-10-08T23:08:12.000000Z\",\n        \"lines\": [\n          {\n            \"sales_order_fulfillment_line_id\": 31,\n            \"quantity\": 2\n          }\n        ]\n      }\n    ]\n  },\n  \"warnings\": [\n    \"SPSSupplierHD-20: 5 does not divide into cases of 2, so the last 1 went into a partial carton.\"\n  ]\n}"}]},{"name":"Update Shipment Freight Details","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/spscommerce/fulfillments/{salesOrderFulfillment}/packing/freight","host":["{{baseUrl}}"],"path":["api","spscommerce","fulfillments",":salesOrderFulfillment","packing","freight"],"variable":[{"key":"salesOrderFulfillment","value":"","description":"The sales order fulfillment ID."}]},"description":"Save the shipment's freight references, which retail carton labels and the 856 header carry: bill_of_lading_number (max 30), carrier_pro_number (max 35), carrier_scac (2–4 letters, stored upper case), carrier_name (max 60). The carrier defaults to the shipment's shipping method when not set. Editable until the 856 is sent.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token. Available for shipments of SPS Commerce EDI orders; other shipments return 404. Requires the sales_orders.update permission.","body":{"mode":"raw","raw":"{\n  \"bill_of_lading_number\": \"BOL-0001842\",\n  \"carrier_pro_number\": \"PRO-5531907\",\n  \"carrier_scac\": \"ODFL\",\n  \"carrier_name\": \"Old Dominion\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/spscommerce/fulfillments/{salesOrderFulfillment}/packing/freight","host":["{{baseUrl}}"],"path":["api","spscommerce","fulfillments",":salesOrderFulfillment","packing","freight"],"variable":[{"key":"salesOrderFulfillment","value":"","description":"The sales order fulfillment ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"data\": {\n    \"sales_order_fulfillment_id\": 2,\n    \"integration_instance_id\": 4,\n    \"po_number\": \"PO584615-1\",\n    \"pack_level_enabled\": true,\n    \"trading_partner_id\": \"SPSALLTESTID\",\n    \"label_template_id\": \"15aa5856\",\n    \"has_company_prefix\": true,\n    \"is_fully_packed\": true,\n    \"is_finalized\": true,\n    \"is_locked\": false,\n    \"ship_notice_status\": \"ready\",\n    \"invoice_status\": \"waiting\",\n    \"freight\": {\n      \"bill_of_lading_number\": \"BOL-0001842\",\n      \"carrier_pro_number\": \"PRO-5531907\",\n      \"carrier_scac\": \"ODFL\",\n      \"carrier_name\": \"Old Dominion\"\n    },\n    \"lines\": [\n      {\n        \"sales_order_fulfillment_line_id\": 31,\n        \"product_id\": 7,\n        \"sku\": \"SPSSupplierHD-20\",\n        \"name\": \"Sample Product HD-20\",\n        \"case_quantity\": 2,\n        \"shipped\": 5,\n        \"packed\": 5,\n        \"remaining\": 0,\n        \"unit_weight_lb\": 47.99\n      }\n    ],\n    \"cartons\": [\n      {\n        \"id\": 11,\n        \"carton_number\": 1,\n        \"sscc\": \"006141410000000012\",\n        \"weight\": 95.98,\n        \"weight_unit\": \"LB\",\n        \"length\": 16,\n        \"width\": 12,\n        \"height\": 10,\n        \"dimension_unit\": \"IN\",\n        \"tracking_number\": null,\n        \"has_label\": true,\n        \"label_fetched_at\": \"2026-10-08T23:08:12.000000Z\",\n        \"lines\": [\n          {\n            \"sales_order_fulfillment_line_id\": 31,\n            \"quantity\": 2\n          }\n        ]\n      }\n    ]\n  }\n}"}]},{"name":"Finalize Shipment Packing","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/fulfillments/{salesOrderFulfillment}/packing/finalize","host":["{{baseUrl}}"],"path":["api","spscommerce","fulfillments",":salesOrderFulfillment","packing","finalize"],"variable":[{"key":"salesOrderFulfillment","value":"","description":"The sales order fulfillment ID."}]},"description":"Lock the packing in and issue every carton its SSCC-18 number, from the connection's GS1 company prefix. Requires every shipped unit to be packed. If the shipment has already shipped, its held 856 ship notice (and 810 invoice, when enabled) is sent.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token. Available for shipments of SPS Commerce EDI orders; other shipments return 404. Requires the sales_orders.update permission."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/spscommerce/fulfillments/{salesOrderFulfillment}/packing/finalize","host":["{{baseUrl}}"],"path":["api","spscommerce","fulfillments",":salesOrderFulfillment","packing","finalize"],"variable":[{"key":"salesOrderFulfillment","value":"","description":"The sales order fulfillment ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"data\": {\n    \"sales_order_fulfillment_id\": 2,\n    \"integration_instance_id\": 4,\n    \"po_number\": \"PO584615-1\",\n    \"pack_level_enabled\": true,\n    \"trading_partner_id\": \"SPSALLTESTID\",\n    \"label_template_id\": \"15aa5856\",\n    \"has_company_prefix\": true,\n    \"is_fully_packed\": true,\n    \"is_finalized\": true,\n    \"is_locked\": false,\n    \"ship_notice_status\": \"ready\",\n    \"invoice_status\": \"waiting\",\n    \"freight\": {\n      \"bill_of_lading_number\": \"BOL-0001842\",\n      \"carrier_pro_number\": \"PRO-5531907\",\n      \"carrier_scac\": \"ODFL\",\n      \"carrier_name\": \"Old Dominion\"\n    },\n    \"lines\": [\n      {\n        \"sales_order_fulfillment_line_id\": 31,\n        \"product_id\": 7,\n        \"sku\": \"SPSSupplierHD-20\",\n        \"name\": \"Sample Product HD-20\",\n        \"case_quantity\": 2,\n        \"shipped\": 5,\n        \"packed\": 5,\n        \"remaining\": 0,\n        \"unit_weight_lb\": 47.99\n      }\n    ],\n    \"cartons\": [\n      {\n        \"id\": 11,\n        \"carton_number\": 1,\n        \"sscc\": \"006141410000000012\",\n        \"weight\": 95.98,\n        \"weight_unit\": \"LB\",\n        \"length\": 16,\n        \"width\": 12,\n        \"height\": 10,\n        \"dimension_unit\": \"IN\",\n        \"tracking_number\": null,\n        \"has_label\": true,\n        \"label_fetched_at\": \"2026-10-08T23:08:12.000000Z\",\n        \"lines\": [\n          {\n            \"sales_order_fulfillment_line_id\": 31,\n            \"quantity\": 2\n          }\n        ]\n      }\n    ]\n  }\n}"}]},{"name":"Reopen Shipment Packing","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/fulfillments/{salesOrderFulfillment}/packing/reopen","host":["{{baseUrl}}"],"path":["api","spscommerce","fulfillments",":salesOrderFulfillment","packing","reopen"],"variable":[{"key":"salesOrderFulfillment","value":"","description":"The sales order fulfillment ID."}]},"description":"Reopen finalized packing for changes. The cartons' SSCC numbers and labels are voided; new numbers are issued on the next finalize, so printed labels must be replaced. Refused once the 856 has been sent.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token. Available for shipments of SPS Commerce EDI orders; other shipments return 404. Requires the sales_orders.update permission."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/spscommerce/fulfillments/{salesOrderFulfillment}/packing/reopen","host":["{{baseUrl}}"],"path":["api","spscommerce","fulfillments",":salesOrderFulfillment","packing","reopen"],"variable":[{"key":"salesOrderFulfillment","value":"","description":"The sales order fulfillment ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"data\": {\n    \"sales_order_fulfillment_id\": 2,\n    \"integration_instance_id\": 4,\n    \"po_number\": \"PO584615-1\",\n    \"pack_level_enabled\": true,\n    \"trading_partner_id\": \"SPSALLTESTID\",\n    \"label_template_id\": \"15aa5856\",\n    \"has_company_prefix\": true,\n    \"is_fully_packed\": true,\n    \"is_finalized\": false,\n    \"is_locked\": false,\n    \"ship_notice_status\": \"ready\",\n    \"invoice_status\": \"waiting\",\n    \"freight\": {\n      \"bill_of_lading_number\": \"BOL-0001842\",\n      \"carrier_pro_number\": \"PRO-5531907\",\n      \"carrier_scac\": \"ODFL\",\n      \"carrier_name\": \"Old Dominion\"\n    },\n    \"lines\": [\n      {\n        \"sales_order_fulfillment_line_id\": 31,\n        \"product_id\": 7,\n        \"sku\": \"SPSSupplierHD-20\",\n        \"name\": \"Sample Product HD-20\",\n        \"case_quantity\": 2,\n        \"shipped\": 5,\n        \"packed\": 5,\n        \"remaining\": 0,\n        \"unit_weight_lb\": 47.99\n      }\n    ],\n    \"cartons\": [\n      {\n        \"id\": 11,\n        \"carton_number\": 1,\n        \"sscc\": \"006141410000000012\",\n        \"weight\": 95.98,\n        \"weight_unit\": \"LB\",\n        \"length\": 16,\n        \"width\": 12,\n        \"height\": 10,\n        \"dimension_unit\": \"IN\",\n        \"tracking_number\": null,\n        \"has_label\": true,\n        \"label_fetched_at\": \"2026-10-08T23:08:12.000000Z\",\n        \"lines\": [\n          {\n            \"sales_order_fulfillment_line_id\": 31,\n            \"quantity\": 2\n          }\n        ]\n      }\n    ]\n  }\n}"}]},{"name":"Generate Carton Labels","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/fulfillments/{salesOrderFulfillment}/packing/labels","host":["{{baseUrl}}"],"path":["api","spscommerce","fulfillments",":salesOrderFulfillment","packing","labels"],"variable":[{"key":"salesOrderFulfillment","value":"","description":"The sales order fulfillment ID."}]},"description":"Render every finalized carton's retailer-formatted GS1-128 label through SPS Commerce, using the label template chosen for the trading partner, and store the ZPL per carton. When the retailer's template needs data the shipment lacks, returns 422 naming what to add (for example a bill of lading number or a shorter carrier name).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token. Available for shipments of SPS Commerce EDI orders; other shipments return 404. Requires the sales_orders.update permission."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/spscommerce/fulfillments/{salesOrderFulfillment}/packing/labels","host":["{{baseUrl}}"],"path":["api","spscommerce","fulfillments",":salesOrderFulfillment","packing","labels"],"variable":[{"key":"salesOrderFulfillment","value":"","description":"The sales order fulfillment ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"data\": {\n    \"sales_order_fulfillment_id\": 2,\n    \"integration_instance_id\": 4,\n    \"po_number\": \"PO584615-1\",\n    \"pack_level_enabled\": true,\n    \"trading_partner_id\": \"SPSALLTESTID\",\n    \"label_template_id\": \"15aa5856\",\n    \"has_company_prefix\": true,\n    \"is_fully_packed\": true,\n    \"is_finalized\": true,\n    \"is_locked\": false,\n    \"ship_notice_status\": \"ready\",\n    \"invoice_status\": \"waiting\",\n    \"freight\": {\n      \"bill_of_lading_number\": \"BOL-0001842\",\n      \"carrier_pro_number\": \"PRO-5531907\",\n      \"carrier_scac\": \"ODFL\",\n      \"carrier_name\": \"Old Dominion\"\n    },\n    \"lines\": [\n      {\n        \"sales_order_fulfillment_line_id\": 31,\n        \"product_id\": 7,\n        \"sku\": \"SPSSupplierHD-20\",\n        \"name\": \"Sample Product HD-20\",\n        \"case_quantity\": 2,\n        \"shipped\": 5,\n        \"packed\": 5,\n        \"remaining\": 0,\n        \"unit_weight_lb\": 47.99\n      }\n    ],\n    \"cartons\": [\n      {\n        \"id\": 11,\n        \"carton_number\": 1,\n        \"sscc\": \"006141410000000012\",\n        \"weight\": 95.98,\n        \"weight_unit\": \"LB\",\n        \"length\": 16,\n        \"width\": 12,\n        \"height\": 10,\n        \"dimension_unit\": \"IN\",\n        \"tracking_number\": null,\n        \"has_label\": true,\n        \"label_fetched_at\": \"2026-10-08T23:08:12.000000Z\",\n        \"lines\": [\n          {\n            \"sales_order_fulfillment_line_id\": 31,\n            \"quantity\": 2\n          }\n        ]\n      }\n    ]\n  },\n  \"labels_generated\": 5\n}"}]},{"name":"Download Carton Labels (ZPL)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/fulfillments/{salesOrderFulfillment}/packing/labels/zpl","host":["{{baseUrl}}"],"path":["api","spscommerce","fulfillments",":salesOrderFulfillment","packing","labels","zpl"],"variable":[{"key":"salesOrderFulfillment","value":"","description":"The sales order fulfillment ID."}]},"description":"Download every stored carton label as one ZPL file for a thermal printer (Content-Type application/zpl). Returns 422 until labels have been generated.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token. Available for shipments of SPS Commerce EDI orders; other shipments return 404."}},{"name":"Download Carton Labels (PDF)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/fulfillments/{salesOrderFulfillment}/packing/labels/pdf","host":["{{baseUrl}}"],"path":["api","spscommerce","fulfillments",":salesOrderFulfillment","packing","labels","pdf"],"variable":[{"key":"salesOrderFulfillment","value":"","description":"The sales order fulfillment ID."}]},"description":"Render every carton label as one PDF through SPS Commerce, for printing on an ordinary printer (Content-Type application/pdf).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token. Available for shipments of SPS Commerce EDI orders; other shipments return 404."}},{"name":"List EDI Documents","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/documents","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","documents"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Paginated list of every EDI document the connection received or sent, newest first. Each row names the purchase order it belongs to, its sales order and shipment (when it has one), the trading partner, its send status and the reason it failed.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFilters: filter[search], filter_groups (see query params).\nSorts: id, transaction_set, direction, status, control_number, partner_reference, created_at, sent_at, received_at (default -id).\nPagination: page, per_page (default 10, max 100)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/documents","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","documents"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 13,\n      \"transaction_set\": \"810\",\n      \"direction\": \"outbound\",\n      \"status\": \"failed\",\n      \"control_number\": \"IN2\",\n      \"po_number\": \"PO584615-1\",\n      \"trading_partner_id\": \"SPSALLTESTID\",\n      \"sales_order\": {\n        \"id\": 18,\n        \"sales_order_number\": \"PO584615-1\"\n      },\n      \"fulfillment\": {\n        \"id\": 2,\n        \"fulfillment_number\": \"PO584615-1.1\"\n      },\n      \"error\": \"Failed to send document. HTTP 400: Invoice rejected by trading partner\",\n      \"can_resend\": true,\n      \"received_at\": null,\n      \"sent_at\": null,\n      \"created_at\": \"2026-10-08T23:06:12.000000Z\"\n    },\n    {\n      \"id\": 12,\n      \"transaction_set\": \"856\",\n      \"direction\": \"outbound\",\n      \"status\": \"sent\",\n      \"control_number\": \"SH2\",\n      \"po_number\": \"PO584615-1\",\n      \"trading_partner_id\": \"SPSALLTESTID\",\n      \"sales_order\": {\n        \"id\": 18,\n        \"sales_order_number\": \"PO584615-1\"\n      },\n      \"fulfillment\": {\n        \"id\": 2,\n        \"fulfillment_number\": \"PO584615-1.1\"\n      },\n      \"error\": null,\n      \"can_resend\": false,\n      \"received_at\": null,\n      \"sent_at\": \"2026-10-08T23:06:14.000000Z\",\n      \"created_at\": \"2026-10-08T23:06:12.000000Z\"\n    }\n  ],\n  \"first_page_url\": \"https://sku8.test/api/spscommerce/4/documents?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://sku8.test/api/spscommerce/4/documents?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://sku8.test/api/spscommerce/4/documents\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 2,\n  \"total\": 2\n}"}]},{"name":"Download EDI Document XML","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/documents/{document}/xml","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","documents",":document","xml"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."},{"key":"document","value":"","description":"The document ID."}]},"description":"Download the document exactly as it was exchanged with SPS Commerce, as an XML file attachment.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nReturns 404 when the document belongs to another connection or has no content yet."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/documents/{document}/xml","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","documents",":document","xml"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."},{"key":"document","value":"","description":"The document ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Invoice>\n  <Header>\n    <InvoiceHeader>\n      <TradingPartnerId>SPSALLTESTID</TradingPartnerId>\n      <InvoiceNumber>IN2</InvoiceNumber>\n      <PurchaseOrderNumber>PO584615-1</PurchaseOrderNumber>\n    </InvoiceHeader>\n  </Header>\n</Invoice>"}]},{"name":"Resend EDI Document","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/documents/{document}/resend","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","documents",":document","resend"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."},{"key":"document","value":"","description":"The document ID."}]},"description":"Queue another attempt at an outbound document (acknowledgment, ship notice, invoice or inventory advice) that failed to send. The document goes back to ready and its previous error is cleared.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token and the integrations.update permission.\n\nReturns 422 for a received document, a document that did not fail, or one that was never generated; 404 when it belongs to another connection."},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/documents/{document}/resend","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","documents",":document","resend"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."},{"key":"document","value":"","description":"The document ID."}]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Resend queued\",\n  \"data\": {\n    \"id\": 13,\n    \"transaction_set\": \"810\",\n    \"direction\": \"outbound\",\n    \"status\": \"ready\",\n    \"control_number\": \"IN2\",\n    \"po_number\": \"PO584615-1\",\n    \"trading_partner_id\": \"SPSALLTESTID\",\n    \"sales_order\": {\n      \"id\": 18,\n      \"sales_order_number\": \"PO584615-1\"\n    },\n    \"fulfillment\": {\n      \"id\": 2,\n      \"fulfillment_number\": \"PO584615-1.1\"\n    },\n    \"error\": null,\n    \"can_resend\": false,\n    \"received_at\": null,\n    \"sent_at\": null,\n    \"created_at\": \"2026-10-08T23:06:12.000000Z\"\n  }\n}"}]},{"name":"List Item Mappings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/item-mappings","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","item-mappings"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Paginated item mappings of the connection.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFilters: filter[search], filter_groups.\nSorts: id, partner_sku, sku, partner_description, created_at (default -id).\nPagination: page, per_page (default 10, max 100)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/item-mappings","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","item-mappings"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 1,\n      \"partner_sku\": \"WM-778812\",\n      \"partner_description\": \"Sample Description3\",\n      \"upc\": 43000186706,\n      \"is_active\": true,\n      \"product\": {\n        \"id\": 9,\n        \"sku\": \"SPSSupplierHD-60\",\n        \"name\": \"Sample Product HD-60\"\n      },\n      \"created_at\": \"2026-10-09T00:15:37.000000Z\",\n      \"updated_at\": \"2026-10-09T00:15:37.000000Z\"\n    }\n  ],\n  \"first_page_url\": \"https://sku8.test/api/spscommerce/4/documents?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://sku8.test/api/spscommerce/4/documents?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://sku8.test/api/spscommerce/4/item-mappings\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"Create Item Mapping","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/item-mappings","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","item-mappings"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Match a trading partner item number to a product. Every failed purchase order waiting on that item number is retried straight away; retried_purchase_orders says how many.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token and the integrations.update permission.\n\nBody fields:\n- partner_sku (string, required, max 255): the trading partner's item number (buyer part number); unique per connection\n- product_id (integer, required): the product it becomes\n- partner_description (string, optional, max 255)\n- upc (string, optional, max 255)\n- is_active (boolean, optional, default true): an inactive mapping is ignored","body":{"mode":"raw","raw":"{\n  \"partner_sku\": \"WM-778812\",\n  \"product_id\": 9,\n  \"partner_description\": \"Sample Description3\",\n  \"upc\": 43000186706,\n  \"is_active\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/item-mappings","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","item-mappings"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"partner_sku\": \"WM-778812\",\n    \"partner_description\": \"Sample Description3\",\n    \"upc\": 43000186706,\n    \"is_active\": true,\n    \"product\": {\n      \"id\": 9,\n      \"sku\": \"SPSSupplierHD-60\",\n      \"name\": \"Sample Product HD-60\"\n    },\n    \"created_at\": \"2026-10-09T00:15:37.000000Z\",\n    \"updated_at\": \"2026-10-09T00:15:37.000000Z\"\n  },\n  \"retried_purchase_orders\": 1\n}"}]},{"name":"List Unmatched Items","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/item-mappings/unmatched","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","item-mappings","unmatched"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Item numbers on purchase orders that have not become sales orders because no product matches them, one row per item number, with how many purchase orders and units are waiting. Item numbers that already have an active mapping are left out.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/item-mappings/unmatched","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","item-mappings","unmatched"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"partner_sku\": \"WM-778812\",\n      \"vendor_sku\": \"NF-ROLLER-60\",\n      \"upc\": 43000186706,\n      \"description\": \"Sample Description3\",\n      \"purchase_orders\": 1,\n      \"units\": 3\n    }\n  ]\n}"}]},{"name":"Update Item Mapping","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/item-mappings/{mapping}","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","item-mappings",":mapping"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."},{"key":"mapping","value":"","description":"The mapping ID."}]},"description":"Change a mapping's item number, product, description or active flag. When the mapping is active, failed purchase orders waiting on its item number are retried.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token and the integrations.update permission.\n\nBody fields:\n- partner_sku (string, required, max 255): the trading partner's item number (buyer part number); unique per connection\n- product_id (integer, required): the product it becomes\n- partner_description (string, optional, max 255)\n- upc (string, optional, max 255)\n- is_active (boolean, optional, default true): an inactive mapping is ignored\n\nReturns 404 when the mapping belongs to another connection.","body":{"mode":"raw","raw":"{\n  \"partner_sku\": \"WM-778812\",\n  \"product_id\": 9,\n  \"partner_description\": \"Sample Description3\",\n  \"upc\": 43000186706,\n  \"is_active\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/item-mappings/{mapping}","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","item-mappings",":mapping"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."},{"key":"mapping","value":"","description":"The mapping ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"partner_sku\": \"WM-778812\",\n    \"partner_description\": \"Sample Description3\",\n    \"upc\": 43000186706,\n    \"is_active\": false,\n    \"product\": {\n      \"id\": 9,\n      \"sku\": \"SPSSupplierHD-60\",\n      \"name\": \"Sample Product HD-60\"\n    },\n    \"created_at\": \"2026-10-09T00:15:37.000000Z\",\n    \"updated_at\": \"2026-10-09T00:15:37.000000Z\"\n  },\n  \"retried_purchase_orders\": 0\n}"}]},{"name":"Delete Item Mapping","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/item-mappings/{mapping}","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","item-mappings",":mapping"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."},{"key":"mapping","value":"","description":"The mapping ID."}]},"description":"Delete a mapping. New purchase orders with that item number fail until it matches a product again; sales orders already created are unchanged.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token and the integrations.update permission.\n\nReturns 404 when the mapping belongs to another connection."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/item-mappings/{mapping}","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","item-mappings",":mapping"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."},{"key":"mapping","value":"","description":"The mapping ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Mapping deleted\"\n}"}]},{"name":"List Purchase Orders","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/purchase-orders","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","purchase-orders"],"variable":[{"key":"integrationInstance","value":"{{sps-instance-id}}","description":"SPS Commerce integration instance ID"}]},"description":"List EDI 850 purchase orders received for an SPS Commerce integration, paginated.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFilters: filter[search] (PO number or buyer name, partial), filter[processing_status] (exact), filter[sales_order_id] (exact).\nAllowed sorts: id, po_number, po_date, buyer_name, total_amount, processing_status, created_at (default -id).\nPagination: page, per_page (default 10)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/purchase-orders","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","purchase-orders"],"variable":[{"key":"integrationInstance","value":"{{sps-instance-id}}","description":"SPS Commerce integration instance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 57,\n      \"edi_document_id\": 311,\n      \"integration_instance_id\": 4,\n      \"sales_order_id\": 1042,\n      \"po_number\": \"PO-100234\",\n      \"po_date\": \"2026-09-28T00:00:00.000000Z\",\n      \"po_type\": \"SA\",\n      \"requested_ship_date\": \"2026-10-03T00:00:00.000000Z\",\n      \"requested_delivery_date\": \"2026-10-08T00:00:00.000000Z\",\n      \"buyer_name\": \"Northwind Retail\",\n      \"buyer_id\": \"NWR-0091\",\n      \"ship_to_name\": \"Northwind DC 3\",\n      \"ship_to_address\": {\n        \"line1\": \"4100 Commerce Way\",\n        \"city\": \"Reno\",\n        \"state\": \"NV\",\n        \"zip\": \"89502\",\n        \"country\": \"US\"\n      },\n      \"total_quantity\": \"48.0000\",\n      \"total_amount\": \"1176.00\",\n      \"currency_code\": \"USD\",\n      \"processing_status\": \"sales_order_created\",\n      \"processing_errors\": null,\n      \"created_at\": \"2026-09-28T14:05:11.000000Z\",\n      \"updated_at\": \"2026-09-28T14:05:19.000000Z\",\n      \"deleted_at\": null,\n      \"sales_order\": {\n        \"id\": 1,\n        \"sales_order_number\": \"PO-100234\"\n      }\n    }\n  ],\n  \"first_page_url\": \"https://sku4.test/api/spscommerce/4/purchase-orders?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://sku4.test/api/spscommerce/4/purchase-orders?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://sku4.test/api/spscommerce/4/purchase-orders\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"Delete Purchase Orders","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/purchase-orders","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","purchase-orders"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Permanently delete multiple purchase orders.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Request Body:**\n- `ids` - Array of purchase order IDs to delete"}},{"name":"Get Purchase Order","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/purchase-orders/{purchaseOrder}","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","purchase-orders",":purchaseOrder"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."},{"key":"purchaseOrder","value":"","description":"The purchase order ID."}]},"description":"Get detailed information for a specific purchase order including line items.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Response:**\n- Full purchase order details\n- Line items array with SKU, quantity, price\n- Trading partner information\n- Acknowledgment status\n- Linked sales order (if mapped)"}},{"name":"Retry Purchase Order Conversion","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/purchase-orders/{purchaseOrder}/retry","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","purchase-orders",":purchaseOrder","retry"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."},{"key":"purchaseOrder","value":"","description":"The purchase order ID."}]},"description":"Re-run conversion of a purchase order that failed (for example after mapping its SKUs). Queued; the existing purchase order is converted rather than duplicated.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token and the integrations.update permission.\n\nReturns 422 when the purchase order already has a sales order."},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/purchase-orders/{purchaseOrder}/retry","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","purchase-orders",":purchaseOrder","retry"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."},{"key":"purchaseOrder","value":"","description":"The purchase order ID."}]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Conversion queued\",\n  \"purchase_order_id\": 7\n}"}]},{"name":"Map Buyer SKU to Product","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/sku-mappings","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","sku-mappings"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Map a trading partner's buyer part number to one of your products. The mapping is remembered for future purchase orders on this connection, applied straight away to unmapped lines of purchase orders already received (those not yet converted to sales orders), and any failed purchase order left with no unmapped lines is queued to create its sales order.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nMapping a buyer part number again replaces its earlier product.\n\nAuthentication: Requires Bearer token and the integrations.update permission.\n\nBody:\n- partner_sku (string, required, max 255) — the buyer part number exactly as it appears on the purchase order line\n- product_id (integer, required) — id of an existing product\n\nResponse fields:\n- mapping_id — id of the saved mapping\n- purchase_orders_updated — number of purchase orders whose lines were mapped\n- retried_purchase_order_ids — purchase orders queued to create their sales order","body":{"mode":"raw","raw":"{\n  \"partner_sku\": \"SPSBuyerItem1\",\n  \"product_id\": 42\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/sku-mappings","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","sku-mappings"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"mapping_id\": 3,\n    \"partner_sku\": \"SPSBuyerItem1\",\n    \"product_id\": 42,\n    \"product_sku\": \"WIDGET-A\",\n    \"purchase_orders_updated\": 1,\n    \"retried_purchase_order_ids\": [\n      7\n    ]\n  }\n}"}]},{"name":"Poll for New Purchase Orders","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/purchase-orders/poll","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","purchase-orders","poll"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Manually trigger polling for new EDI 850 purchase orders from SPS Commerce.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**No Request Body Required**\n\n**Process:**\n1. Fetches transaction history from SPS Commerce API\n2. Downloads new 850 PO files\n3. Parses EDI documents\n4. Creates purchase order records in database\n5. Deletes processed files from SPS Commerce\n\n**Response:**\n- `success` - Operation result\n- `message` - Summary message\n- `new_orders_count` - Number of new orders imported\n- `processed_files` - List of processed files"}},{"name":"Send Acknowledgment (855)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/purchase-orders/acknowledge","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","purchase-orders","acknowledge"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Send EDI 855 PO Acknowledgment to SPS Commerce for one or more purchase orders.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Request Body:**\n- `purchase_order_ids` - Array of purchase order IDs to acknowledge\n\n**Process:**\n1. Generates EDI 855 XML for each purchase order\n2. Sends acknowledgment to SPS Commerce /in/ endpoint\n3. Updates acknowledgment status in database\n4. Records transmission log\n\n**Response:**\n- `success` - Operation result\n- `message` - Summary message\n- `acknowledged_count` - Number of orders acknowledged\n- `failed` - Any failed transmissions","body":{"mode":"raw","raw":"{\n  \"purchase_order_ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}}},{"name":"Download Raw XML","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/purchase-orders/{purchaseOrder}/xml","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","purchase-orders",":purchaseOrder","xml"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."},{"key":"purchaseOrder","value":"","description":"The purchase order ID."}]},"description":"Download the raw EDI 850 XML for a purchase order.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Response:**\n- Raw XML content with Content-Type: application/xml\n- Content-Disposition header for file download"}},{"name":"Export Purchase Orders","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/purchase-orders/export?format=csv","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","purchase-orders","export"],"query":[{"key":"format","value":"csv","description":"Export format: csv, xlsx","disabled":true}],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Export purchase orders to CSV or Excel.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Query Parameters:**\n- `format` - Export format: csv or xlsx\n- `status` - Filter by status (optional)\n- Other filters same as List endpoint\n\n**Response:**\n- CSV or Excel file download\n- Includes all purchase order fields"}},{"name":"Archive Purchase Orders","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/purchase-orders/archive","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","purchase-orders","archive"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Archive multiple purchase orders.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Request Body:**\n- `ids` - Array of purchase order IDs to archive","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}}},{"name":"Unarchive Purchase Orders","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/purchase-orders/unarchive","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","purchase-orders","unarchive"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Unarchive multiple purchase orders.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Request Body:**\n- `ids` - Array of purchase order IDs to unarchive","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}}},{"name":"Get Purchase Order Statistics","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/purchase-orders/metrics","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","purchase-orders","metrics"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Get statistics and metrics for purchase orders.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Response:**\n- `pending` - Count of pending POs\n- `unmapped` - Count of unmapped POs\n- `failed` - Count of failed POs\n- `success_rate` - Success rate percentage\n- `new_today` - Count of POs received today\n- `total_amount` - Total value of orders\n- `avg_line_items` - Average line items per order"}},{"name":"List Acknowledgments","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/acknowledgments?page=1&per_page=15","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","acknowledgments"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"15","description":"Results per page (default 10).","disabled":true}],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Get paginated list of EDI 855 PO Acknowledgments sent to trading partners.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**READ-ONLY** - Acknowledgments cannot be edited once sent.\n\n**Query Parameters:**\n- `page` - Page number (default: 1)\n- `per_page` - Items per page (default: 15)\n- `status` - Filter by: sent, failed\n- `date_from` - Filter by sent date from (YYYY-MM-DD)\n- `date_to` - Filter by sent date to (YYYY-MM-DD)\n\n**Response:** Paginated list with acknowledgments including:\n- `id`, `purchase_order_id`, `po_number`\n- `acknowledgment_code` (AD=Accepted, AC=Accepted with changes, etc.)\n- `sent_at`, `status`, `transmission_id`\n- Pagination metadata"}},{"name":"Get Acknowledgment","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/acknowledgments/{acknowledgment}","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","acknowledgments",":acknowledgment"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."},{"key":"acknowledgment","value":"","description":"The acknowledgment ID."}]},"description":"Get detailed information for a specific 855 acknowledgment.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**READ-ONLY** - View only, cannot be modified.\n\n**Response:**\n- Full acknowledgment details\n- Line items with status codes (IA=Accepted, IB=Backordered, IR=Rejected)\n- Transmission timestamp and status\n- Related purchase order information"}},{"name":"Download Acknowledgment XML","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/acknowledgments/{acknowledgment}/xml","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","acknowledgments",":acknowledgment","xml"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."},{"key":"acknowledgment","value":"","description":"The acknowledgment ID."}]},"description":"Download the raw EDI 855 XML that was sent to SPS Commerce.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Response:**\n- Raw XML content with Content-Type: application/xml\n- Content-Disposition header for file download"}},{"name":"Get Acknowledgment Statistics","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/acknowledgments/metrics","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","acknowledgments","metrics"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Get statistics for 855 acknowledgments.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Response:**\n- `total_sent` - Total acknowledgments sent\n- `failed` - Failed transmissions\n- `success_rate` - Success rate percentage\n- `sent_today` - Acknowledgments sent today\n- `avg_response_time` - Average time from PO to ACK"}},{"name":"List ASN","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/asn?page=1&per_page=15","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","asn"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"15","description":"Results per page (default 10).","disabled":true}],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Get paginated list of EDI 856 Advance Ship Notices sent to trading partners.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**READ-ONLY** - ASN documents cannot be edited once sent.\n\n**Query Parameters:**\n- `page` - Page number (default: 1)\n- `per_page` - Items per page (default: 15)\n- `status` - Filter by: sent, failed\n- `date_from` - Filter by ship date from (YYYY-MM-DD)\n- `date_to` - Filter by ship date to (YYYY-MM-DD)\n\n**Response:** Paginated list with ASN including:\n- `id`, `purchase_order_id`, `po_number`\n- `shipment_id`, `carrier`, `tracking_number`\n- `ship_date`, `sent_at`, `status`\n- Pagination metadata"}},{"name":"Send ASN","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/asn","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","asn"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Generate (if needed) and send the 856 Advance Ship Notice for a completed fulfillment of an order received through this SPS Commerce integration. Retries resume the existing ASN; an ASN already sent is not transmitted again.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token and the integrations.update permission.\n\nReturns 422 when the fulfillment is not a completed shipment of an order from this integration, or when no ASN can be generated.\nReturns 409 when another process is already sending this ASN.","body":{"mode":"raw","raw":"{\n  \"purchase_order_id\": 1,\n  \"shipment_data\": {\n    \"carrier\": \"UPS\",\n    \"tracking_number\": \"1Z999AA10123456784\",\n    \"ship_date\": \"2025-01-15\",\n    \"estimated_delivery\": \"2025-01-18\",\n    \"packages\": [\n      {\n        \"tracking_number\": \"1Z999AA10123456784\",\n        \"weight\": 10.5,\n        \"weight_unit\": \"LBS\",\n        \"dimensions\": {\n          \"length\": 12,\n          \"width\": 10,\n          \"height\": 8,\n          \"unit\": \"IN\"\n        }\n      }\n    ],\n    \"line_items\": [\n      {\n        \"line_number\": 1,\n        \"quantity_shipped\": 50,\n        \"upc\": 12345678905\n      }\n    ]\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/asn","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","asn"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"ASN sent successfully\",\n  \"fulfillment_id\": 8812\n}"}]},{"name":"Get ASN","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/asn/{advanceShipNotice}","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","asn",":advanceShipNotice"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."},{"key":"advanceShipNotice","value":"","description":"The advance ship notice ID."}]},"description":"Get detailed information for a specific 856 ASN.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**READ-ONLY** - View only, cannot be modified.\n\n**Response:**\n- Full ASN details\n- Shipment information (carrier, tracking, packages)\n- Line items shipped\n- Transmission timestamp and status\n- Related purchase order information"}},{"name":"Download ASN XML","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/asn/{advanceShipNotice}/xml","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","asn",":advanceShipNotice","xml"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."},{"key":"advanceShipNotice","value":"","description":"The advance ship notice ID."}]},"description":"Download the raw EDI 856 XML that was sent to SPS Commerce.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Response:**\n- Raw XML content with Content-Type: application/xml\n- Content-Disposition header for file download"}},{"name":"Get Ship Notice Statistics","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/spscommerce/{integrationInstance}/asn/metrics","host":["{{baseUrl}}"],"path":["api","spscommerce",":integrationInstance","asn","metrics"],"variable":[{"key":"integrationInstance","value":"","description":"The integration instance ID."}]},"description":"Get statistics for 856 ASN documents.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Response:**\n- `total_sent` - Total ASNs sent\n- `failed` - Failed transmissions\n- `success_rate` - Success rate percentage\n- `sent_today` - ASNs sent today\n- `avg_ship_time` - Average time from PO to shipment"}}]},{"name":"Products","item":[{"name":"Get Product","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{id}","host":["{{baseUrl}}"],"path":["api","products",":id"],"variable":[{"key":"id","value":"1","description":"Product ID"}]},"description":"Get a single product by ID with all related data.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 404 if product not found."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{id}","host":["{{baseUrl}}"],"path":["api","products",":id"],"variable":[{"key":"id","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"sku\": \"SKU-001\",\n    \"name\": \"Blue Widget\",\n    \"barcode\": 123456789012,\n    \"mpn\": \"BW-001\",\n    \"hts_code\": \"8471.30.0100\",\n    \"country_of_origin\": \"US\",\n    \"density_g_per_ml\": null,\n    \"density_source\": null,\n    \"density_basis\": null,\n    \"type\": \"standard\",\n    \"is_lot_tracked\": true,\n    \"lot_tracking_method\": \"fifo\",\n    \"shelf_life_days\": 180,\n    \"near_expiry_threshold_days\": 60,\n    \"min_remaining_shelf_life_days\": 105,\n    \"expiry_aware_availability\": true,\n    \"image_url\": \"https://cdn.example.com/products/blue-widget.jpg\",\n    \"has_inventory_movements\": true,\n    \"brand\": {\n      \"id\": 3,\n      \"name\": \"Acme Co\"\n    },\n    \"tags\": [\n      \"new\",\n      \"featured\"\n    ],\n    \"is_variation\": false,\n    \"weight\": \"0.5\",\n    \"weight_unit\": \"kg\",\n    \"length\": \"10.0\",\n    \"width\": \"5.0\",\n    \"height\": \"3.0\",\n    \"dimension_unit\": \"cm\",\n    \"case_quantity\": 12,\n    \"portal_visible\": true,\n    \"min_order_quantity\": null,\n    \"order_increment\": null,\n    \"allow_backorder\": null,\n    \"portal_lead_time_days\": null,\n    \"case_weight\": \"6.0\",\n    \"case_weight_unit\": \"kg\",\n    \"fba_prep_instructions\": \"Bubble wrap required\",\n    \"price\": {\n      \"Default\": {\n        \"price\": 29.99\n      }\n    },\n    \"average_cost\": {\n      \"value\": 12.5,\n      \"currency\": \"USD\"\n    },\n    \"default_supplier\": {\n      \"id\": 5,\n      \"name\": \"Main Supplier\"\n    },\n    \"default_supplier_sku\": \"MS-BW-001\",\n    \"default_supplier_leadtime\": 14,\n    \"default_supplier_moq\": 100,\n    \"default_supplier_price\": 10,\n    \"min_stock_level\": 50,\n    \"target_stock_days\": 30,\n    \"inventory_total\": 150,\n    \"inventory_available\": 120,\n    \"inventory_available_to_sell\": 115,\n    \"inventory_allocated\": 30,\n    \"inventory_on_hold\": 0,\n    \"inventory_incoming\": 200,\n    \"category_main\": {\n      \"id\": 2,\n      \"name\": \"Widgets\"\n    },\n    \"attributes\": {},\n    \"sales_nominal_code_id\": null,\n    \"cogs_nominal_code_id\": null,\n    \"default_tax_rate_id\": null,\n    \"is_taxable\": false,\n    \"archived_at\": null,\n    \"stock_uom_id\": 1,\n    \"stock_uom_code\": \"EACH\",\n    \"stock_uom_name\": \"Each\",\n    \"created_at\": \"2024-01-15T10:30:00.000000Z\",\n    \"updated_at\": \"2024-06-01T08:00:00.000000Z\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Update Product","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/{id}","host":["{{baseUrl}}"],"path":["api","products",":id"],"variable":[{"key":"id","value":"1","description":"Product ID"}]},"description":"Update an existing product by ID. All fields are optional on update.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nProduct type cannot be changed if inventory movements exist (exception: standard -> kit is allowed). Additionally, an inventory-bearing product (standard/kit/blemished/manufactured) cannot be converted to a non-inventory type (bundle/matrix) while it still carries any inventory ledger - a FIFO layer OR an inventory movement of any type (including stock_take, receipt, adjustment, transfer). This prevents orphaning stock layers and movements.\n\nFor update (PUT), additional field available:\n- initial_inventory.warehouses[].id (required with initial_inventory)\n- initial_inventory.warehouses[].quantity (required)\n- initial_inventory.warehouses[0].unit_cost (required)\n- images[].id — required only when images[].operation is delete\n- images[].operation — nullable|in:update_create,delete\n\n**Optional field:** `mpn` (string, max 255, nullable) — manufacturer part number.\n\n**Optional field:** `hts_code` (string, max 255, nullable) — Harmonized Tariff Schedule code.\n\n**Optional field:** `product_type_id` (integer, nullable) — the product's product type (Organization → Product Types); must be an active leaf type. It decides which channel category the product publishes into on each sales channel. Send null to clear it.\n\nReturns 422 if product type change is invalid.\n\n**Bundle / Kit components:** When the resulting product type is `bundle` or `kit`, `components[]` must be present and non-empty. This applies whether you are (a) changing the type from `standard` → `bundle|kit`, (b) updating an existing bundle/kit that currently has no components, or (c) sending an empty `components: []` payload (which would otherwise wipe components). Returns a 422 validation error on the `components` field in these cases. Updates that don't touch `components` on a bundle/kit which already has components are unaffected. To convert a `standard` product that has no components yet, supply `components[]` in the same request as the `type` change — the components are saved as part of the same update, satisfying the requirement atomically.\n\n**Note (pricing tiers):** When sending `suppliers[].pricing[]` entries with `operation: updateOrCreate` (or omitted), `price` is **required** and must be a numeric value `>= 0` and `< 100000`. To remove a tier, send `operation: \"delete\"`. Sending `price: null` will return a `422` validation error on `suppliers.{i}.pricing.{j}.price`.\n\n**Lot tracking fields** (optional):\n- `is_lot_tracked` (boolean, nullable) — enable batch/lot tracking for this product.\n- `lot_tracking_method` (string, nullable) — required when `is_lot_tracked` is true. Allowed values: `fifo` (First-In, First-Out), `fefo` (First-Expiry, First-Out).\n\n**Stock unit of measure** (optional):\n- `stock_uom_id` (integer, nullable) — id of a row in `units_of_measure`. When omitted, the product is auto-assigned `EACH` as its stock UoM on create. Pick `KG`, `L`, etc. for bulk weight or volume products so quantities, purchase orders, sales orders, and stock takes use the correct unit from day one.\n\nAlso accepts expiration_offset_months (integer 0-120, nullable) — the product's default FBA-inbound shelf-life estimate in months; blank uses the app default (10). Responses include it too.\n\n**Supplier links (`suppliers[]`):** Supplier changes apply to every product type (standard, kit, bundle, matrix, blemished, manufactured). Each entry accepts an optional `operation` field:\n- `updateOrCreate` (default when omitted) — link the supplier to the product, creating the link if it does not exist and updating `supplier_sku`, `leadtime`, `minimum_order_quantity`, `is_default`, and `pricing[]` if it does.\n- `delete` — unlink the supplier from the product. Only `supplier_id` is required; the supplier's pricing tiers and per-warehouse supplier inventory for this product are removed with it.\n\nWhen **no** entry carries an `operation`, `suppliers[]` is treated as the complete list and any supplier absent from it is unlinked. When **any** entry carries an `operation`, only the listed suppliers are touched and omitted suppliers are left alone — so an incremental edit should set `operation` on every entry it sends.\n\n`allow_negative_inventory` (boolean|null, optional): whether this product may ship when it would take a warehouse below zero on hand. Deliberately one-way — `false` pins the product to refusing, whatever the warehouse or company setting says, and is the way to protect an item that must never oversell. `true` grants nothing on its own: a product cannot unlock permission the warehouse or company has not already given. Omit or send `null` to inherit.\n\n**Serial format.** `serial_format_override`, when set, must include a sequence token, `{seq}` or `{seq:N}`, so every generated serial is different. A format without one fails with 422 on `serial_format_override`.\n\n**B2B Portal ordering rules** (optional; honoured by the buyer portal's catalog, cart and inventory feed):\n- `portal_visible` (boolean) — show the product on the portal. Default `true`.\n- `min_order_quantity` (integer >= 1, nullable) — product-level MOQ. `null` = no minimum.\n- `order_increment` (integer >= 1, nullable) — buyers order in multiples of this. `null` falls back to `case_quantity`, else any quantity.\n- `allow_backorder` (boolean, nullable) — per-product backorder override. `null` inherits the account's, then the portal's default.\n- `portal_lead_time_days` (integer >= 0, nullable) — lead time shown to buyers when the product is out of stock.","body":{"mode":"raw","raw":"{\n  \"sku\": \"SKU-001-UPDATED\",\n  \"name\": \"Updated Blue Widget\",\n  \"type\": \"standard\",\n  \"is_lot_tracked\": true,\n  \"lot_tracking_method\": \"fifo\",\n  \"shelf_life_days\": 180,\n  \"near_expiry_threshold_days\": 60,\n  \"min_remaining_shelf_life_days\": 105,\n  \"expiry_aware_availability\": true,\n  \"barcode\": \"1234567890124\",\n  \"mpn\": \"MPN-12345\",\n  \"hts_code\": \"8471.30.0100\",\n  \"country_of_origin\": \"US\",\n  \"brand_name\": \"Acme Co\",\n  \"product_type_id\": 7,\n  \"weight\": 0.6,\n  \"weight_unit\": \"kg\",\n  \"min_stock_level\": 60,\n  \"portal_visible\": true,\n  \"min_order_quantity\": 12,\n  \"order_increment\": 6,\n  \"allow_backorder\": null,\n  \"portal_lead_time_days\": 14,\n  \"target_stock_days\": 45,\n  \"stock_uom_id\": null,\n  \"pricing\": [\n    {\n      \"product_pricing_tier_name\": \"Default\",\n      \"price\": 34.99\n    }\n  ],\n  \"suppliers\": [\n    {\n      \"supplier_id\": 5,\n      \"operation\": \"updateOrCreate\",\n      \"is_default\": true,\n      \"supplier_sku\": \"MS-BW-001-V2\",\n      \"leadtime\": 10,\n      \"minimum_order_quantity\": 50\n    },\n    {\n      \"supplier_id\": 9,\n      \"operation\": \"delete\"\n    }\n  ],\n  \"tags\": [\n    \"updated\"\n  ],\n  \"initial_inventory\": {\n    \"warehouses\": [\n      {\n        \"id\": 1,\n        \"quantity\": 100,\n        \"unit_cost\": 12.5\n      }\n    ]\n  },\n  \"shared_children_attributes\": []\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/products/{id}","host":["{{baseUrl}}"],"path":["api","products",":id"],"variable":[{"key":"id","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"sku\": \"SKU-001-UPDATED\",\n    \"name\": \"Updated Blue Widget\",\n    \"type\": \"standard\",\n    \"is_lot_tracked\": true,\n    \"lot_tracking_method\": \"fifo\",\n    \"shelf_life_days\": 180,\n    \"near_expiry_threshold_days\": 60,\n    \"min_remaining_shelf_life_days\": 105,\n    \"expiry_aware_availability\": true,\n    \"updated_at\": \"2024-06-15T14:00:00.000000Z\"\n  },\n  \"message\": \"Product SKU-001-UPDATED updated successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Delete Product","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/products/{id}","host":["{{baseUrl}}"],"path":["api","products",":id"],"variable":[{"key":"id","value":"1","description":"Product ID"}]},"description":"Delete a product by ID. Deletion fails if the product (or, for a matrix parent, any of its variations) is still referenced by a RESTRICT foreign key: sales order lines, bundle sales order lines, purchase order lines, inventory movements, FIFO layers, inventory adjustments, assembly lines, warehouse transfer lines, return receipt lines, RMA lines, sales credit lines, inbound shipment lines, blemished products, or subscription editions.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 400 Bad Request with reason details if the product is in use."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/products/{id}","host":["{{baseUrl}}"],"path":["api","products",":id"],"variable":[{"key":"id","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Product SKU-001 deleted successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Product by SKU","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/by-sku?sku=SKU-001","host":["{{baseUrl}}"],"path":["api","products","by-sku"],"query":[{"key":"sku","value":"SKU-001","description":"Required. Exact SKU to look up.","disabled":true}]},"description":"Get a product by its exact SKU.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 422 if sku parameter is missing. Returns 404 (ModelNotFoundException) if SKU not found."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/by-sku?sku=SKU-001","host":["{{baseUrl}}"],"path":["api","products","by-sku"],"query":[{"key":"sku","value":"SKU-001","description":"Required. Exact SKU to look up.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"sku\": \"SKU-001\",\n    \"name\": \"Blue Widget\",\n    \"type\": \"standard\",\n    \"archived_at\": null\n  },\n  \"status\": \"success\"\n}"}]},{"name":"List Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products?page=1&limit=10&search=&sort=sku&archived=0&included[]=sku&excluded[]=&filter[type]=&filter[brand]=&filter[category]=&filter[tag]=&filter[supplier]=&supplier_id=","host":["{{baseUrl}}"],"path":["api","products"],"query":[{"key":"page","value":"1","description":"Page number (default 1)","disabled":true},{"key":"limit","value":"10","description":"Items per page (default 10)","disabled":true},{"key":"search","value":"","description":"Search by SKU, name, or barcode","disabled":true},{"key":"sort","value":"sku","description":"Sort field. Prefix with - for descending (e.g. -sku). Allowed: sku, name, barcode, type, created_at, updated_at, inventory_total, inventory_available, average_cost","disabled":true},{"key":"archived","value":"0","description":"0 = active only, 1 = archived only, 2 = all","disabled":true},{"key":"included[]","value":"sku","description":"Columns to include in response. Repeat for multiple.","disabled":true},{"key":"excluded[]","value":"","description":"Columns to exclude. Cannot be used with included[].","disabled":true},{"key":"filter[type]","value":"","description":"Filter by product type: standard, bundle, kit, matrix","disabled":true},{"key":"filter[brand]","value":"","description":"Filter by brand name","disabled":true},{"key":"filter[category]","value":"","description":"Filter by category","disabled":true},{"key":"filter[tag]","value":"","description":"Filter by tag","disabled":true},{"key":"filter[supplier]","value":"","description":"Filter by supplier name or ID","disabled":true},{"key":"supplier_id","value":"","description":"Filter variations shown in the response to those belonging to a specific supplier","disabled":true}]},"description":"List all products. Supports searching, sorting, filtering, pagination, and column selection.\n\nAuthentication: Requires Bearer token.\n\nFilter scopes available on the Product model:\n- filter[type]: standard | bundle | kit | matrix\n- filter[brand]: brand name string\n- filter[category]: category name or path\n- filter[tag]: tag name\n- filter[supplier]: supplier name or ID\n\nSort fields: sku, name, barcode, type, created_at, updated_at, inventory_total, inventory_available, average_cost\n\nInclusion columns: sku, id, name, barcode, type, image, image_url, brand, price, weight, weight_unit, length, width, height, dimension_unit, case_quantity, inventory_total, inventory_available, inventory_allocated, inventory_on_hold, inventory_in_transit, inventory_incoming, average_cost, default_supplier, default_supplier_sku, suppliers, tags, attributes, created_at, updated_at, archived_at\n\n**Stock UoM filters (added 2026-05-30):**\n- `filter[stock_uom_id]` — by uom_id (exact, comma-separated)\n- `filter[stock_uom_code]` — by UoM code (advanced text ops)\n- `filter[stock_uom_name]` — by UoM display name (advanced text ops)\n\nThe `hts_code` (Harmonized Tariff Schedule code) column can be returned via `included[]=hts_code`. Filtering (`filter[hts_code]`) and sorting by `hts_code` are available on the `/api/v2/products` endpoint. `country_of_origin` (ISO 3166-1 alpha-2, e.g. `US`) behaves the same way: `included[]=country_of_origin`, `filter[country_of_origin]`, and sorting."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products?page=1&limit=10&search=&sort=sku&archived=0&included[]=sku&excluded[]=&filter[type]=&filter[brand]=&filter[category]=&filter[tag]=&filter[supplier]=&supplier_id=","host":["{{baseUrl}}"],"path":["api","products"],"query":[{"key":"page","value":"1","description":"Page number (default 1)","disabled":true},{"key":"limit","value":"10","description":"Items per page (default 10)","disabled":true},{"key":"search","value":"","description":"Search by SKU, name, or barcode","disabled":true},{"key":"sort","value":"sku","description":"Sort field. Prefix with - for descending (e.g. -sku). Allowed: sku, name, barcode, type, created_at, updated_at, inventory_total, inventory_available, average_cost","disabled":true},{"key":"archived","value":"0","description":"0 = active only, 1 = archived only, 2 = all","disabled":true},{"key":"included[]","value":"sku","description":"Columns to include in response. Repeat for multiple.","disabled":true},{"key":"excluded[]","value":"","description":"Columns to exclude. Cannot be used with included[].","disabled":true},{"key":"filter[type]","value":"","description":"Filter by product type: standard, bundle, kit, matrix","disabled":true},{"key":"filter[brand]","value":"","description":"Filter by brand name","disabled":true},{"key":"filter[category]","value":"","description":"Filter by category","disabled":true},{"key":"filter[tag]","value":"","description":"Filter by tag","disabled":true},{"key":"filter[supplier]","value":"","description":"Filter by supplier name or ID","disabled":true},{"key":"supplier_id","value":"","description":"Filter variations shown in the response to those belonging to a specific supplier","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"sku\": \"SKU-001\",\n      \"name\": \"Blue Widget\",\n      \"barcode\": 123456789012,\n      \"hts_code\": \"8471.30.0100\",\n      \"country_of_origin\": \"US\",\n      \"density_g_per_ml\": null,\n      \"density_source\": null,\n      \"density_basis\": null,\n      \"type\": \"standard\",\n      \"image_url\": \"https://cdn.example.com/products/blue-widget.jpg\",\n      \"brand\": {\n        \"id\": 3,\n        \"name\": \"Acme Co\"\n      },\n      \"price\": {\n        \"Default\": {\n          \"price\": 29.99\n        }\n      },\n      \"weight\": \"0.5\",\n      \"weight_unit\": \"kg\",\n      \"inventory_total\": 150,\n      \"inventory_available\": 120,\n      \"inventory_available_to_sell\": 115,\n      \"inventory_allocated\": 30,\n      \"average_cost\": {\n        \"value\": 12.5,\n        \"currency\": \"USD\"\n      },\n      \"inventory_avg_cost\": {\n        \"value\": 11.85,\n        \"currency\": \"USD\"\n      },\n      \"archived_at\": null,\n      \"stock_uom_id\": 1,\n      \"stock_uom_code\": \"EACH\",\n      \"stock_uom_name\": \"Each\",\n      \"created_at\": \"2024-01-15T10:30:00.000000Z\",\n      \"updated_at\": \"2024-06-01T08:00:00.000000Z\"\n    }\n  ],\n  \"table_specifications\": {\n    \"columns\": [\n      {\n        \"data_name\": \"sku\",\n        \"label\": \"SKU\",\n        \"default_visible\": 1,\n        \"sortable\": true\n      }\n    ]\n  },\n  \"links\": {\n    \"first\": \"https://sku2.test/api/products?page=1\",\n    \"last\": \"https://sku2.test/api/products?page=10\"\n  },\n  \"meta\": {\n    \"current_page\": 1,\n    \"last_page\": 10,\n    \"per_page\": 10,\n    \"total\": 98\n  }\n}"}]},{"name":"Create Product","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products","host":["{{baseUrl}}"],"path":["api","products"]},"description":"Create a new product.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequired fields: sku (unique), type\nProduct types: standard, bundle, kit, matrix\nWeight units: kg, lb, oz, g\nDimension units: cm, in, mm, ft\n\nFor bundle/kit products, `components[]` is **required** and must contain at least one component (each entry has `id` and `quantity`). Saving a bundle or kit with an empty `components` array (or without providing one when creating, or when changing type to bundle/kit) returns a 422 validation error on the `components` field.\nFor matrix products (parent), include variations[] with sku and attributes.\n\n**Optional field:** `mpn` (string, max 255, nullable) — manufacturer part number.\n\n**Optional field:** `hts_code` (string, max 255, nullable) — Harmonized Tariff Schedule code.\n\n**Optional field:** `product_type_id` (integer, nullable) — the product's product type (Organization → Product Types); must be an active leaf type. It decides which channel category the product publishes into on each sales channel. Send null to clear it.\n\nReturns 422 validation error if SKU already exists or other validation fails.\n\n**Note (pricing tiers):** When sending `suppliers[].pricing[]` entries with `operation: updateOrCreate` (or omitted), `price` is **required** and must be a numeric value `>= 0` and `< 100000`. To remove a tier, send `operation: \"delete\"`. Sending `price: null` will return a `422` validation error on `suppliers.{i}.pricing.{j}.price`.\n\n**Lot tracking fields** (optional):\n- `is_lot_tracked` (boolean, nullable) — enable batch/lot tracking for this product.\n- `lot_tracking_method` (string, nullable) — required when `is_lot_tracked` is true. Allowed values: `fifo` (First-In, First-Out), `fefo` (First-Expiry, First-Out).\n- When `is_lot_tracked` is omitted (or null), the product follows the tenant default from the Serial & Lot Tracking settings (`lot_tracking_default_enabled` / `default_lot_tracking_method`). An explicit `true` or `false` always wins.\n\n**Stock unit of measure** (optional):\n- `stock_uom_id` (integer, nullable) — id of a row in `units_of_measure`. When omitted, the product is auto-assigned `EACH` as its stock UoM on create. Pick `KG`, `L`, etc. for bulk weight or volume products so quantities, purchase orders, sales orders, and stock takes use the correct unit from day one.\n\n**Requires permission:** `products.create`\n\nAlso accepts expiration_offset_months (integer 0-120, nullable) — the product's default FBA-inbound shelf-life estimate in months; blank uses the app default (10). Responses include it too.\n\n`allow_negative_inventory` (boolean|null, optional): whether this product may ship when it would take a warehouse below zero on hand. Deliberately one-way — `false` pins the product to refusing, whatever the warehouse or company setting says, and is the way to protect an item that must never oversell. `true` grants nothing on its own: a product cannot unlock permission the warehouse or company has not already given. Omit or send `null` to inherit.\n\n**Serial format.** `serial_format_override`, when set, must include a sequence token, `{seq}` or `{seq:N}`, so every generated serial is different. A format without one fails with 422 on `serial_format_override`.\n\n**B2B Portal ordering rules** (optional; honoured by the buyer portal's catalog, cart and inventory feed):\n- `portal_visible` (boolean) — show the product on the portal. Default `true`.\n- `min_order_quantity` (integer >= 1, nullable) — product-level MOQ. `null` = no minimum.\n- `order_increment` (integer >= 1, nullable) — buyers order in multiples of this. `null` falls back to `case_quantity`, else any quantity.\n- `allow_backorder` (boolean, nullable) — per-product backorder override. `null` inherits the account's, then the portal's default.\n- `portal_lead_time_days` (integer >= 0, nullable) — lead time shown to buyers when the product is out of stock.","body":{"mode":"raw","raw":"{\n  \"sku\": \"SKU-NEW-001\",\n  \"name\": \"New Product\",\n  \"type\": \"standard\",\n  \"is_lot_tracked\": true,\n  \"lot_tracking_method\": \"fifo\",\n  \"shelf_life_days\": 180,\n  \"near_expiry_threshold_days\": 60,\n  \"min_remaining_shelf_life_days\": 105,\n  \"expiry_aware_availability\": true,\n  \"barcode\": \"1234567890123\",\n  \"mpn\": \"MPN-12345\",\n  \"hts_code\": \"8471.30.0100\",\n  \"country_of_origin\": \"US\",\n  \"brand_name\": \"Acme Co\",\n  \"product_type_id\": 7,\n  \"weight\": 0.5,\n  \"weight_unit\": \"kg\",\n  \"length\": 10,\n  \"width\": 5,\n  \"height\": 3,\n  \"dimension_unit\": \"cm\",\n  \"case_quantity\": 12,\n  \"case_weight\": 6,\n  \"case_weight_unit\": \"kg\",\n  \"case_length\": 30,\n  \"case_width\": 20,\n  \"case_height\": 15,\n  \"case_dimension_unit\": \"cm\",\n  \"fba_prep_instructions\": \"Bubble wrap required\",\n  \"target_stock_days\": 30,\n  \"min_stock_level\": 50,\n  \"proforma_landed_cost_percentage\": 5,\n  \"proforma_shipping_cost\": 2.5,\n  \"stock_uom_id\": null,\n  \"sales_nominal_code_id\": null,\n  \"cogs_nominal_code_id\": null,\n  \"default_tax_rate_id\": null,\n  \"is_dropshippable\": false,\n  \"is_decoration_consumable\": false,\n  \"not_for_sale_on_channels\": false,\n  \"weight_source\": \"estimated\",\n  \"weight_measured_by\": null,\n  \"dimensions_source\": \"estimated\",\n  \"dimensions_measured_by\": null,\n  \"parent_id\": null,\n  \"tags\": [\n    \"new\",\n    \"featured\"\n  ],\n  \"tags_operation\": \"set\",\n  \"pricing\": [\n    {\n      \"product_pricing_tier_name\": \"Default\",\n      \"price\": 29.99\n    }\n  ],\n  \"suppliers\": [\n    {\n      \"supplier_id\": 5,\n      \"is_default\": true,\n      \"supplier_sku\": \"MS-BW-001\",\n      \"leadtime\": 14,\n      \"minimum_order_quantity\": 100,\n      \"pricing\": [\n        {\n          \"supplier_pricing_tier_name\": \"Default\",\n          \"price\": 10\n        }\n      ]\n    }\n  ],\n  \"categories\": [\n    {\n      \"category_id\": 2,\n      \"is_primary\": true\n    }\n  ],\n  \"attributes\": [\n    {\n      \"name\": \"Color\",\n      \"value\": \"Blue\"\n    }\n  ],\n  \"images\": [\n    {\n      \"url\": \"https://example.com/images/blue-widget.jpg\",\n      \"name\": \"Main Image\",\n      \"is_primary\": true,\n      \"download\": false\n    }\n  ],\n  \"components\": [],\n  \"variations\": []\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/products","host":["{{baseUrl}}"],"path":["api","products"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"sku\": \"SKU-NEW-001\",\n    \"name\": \"New Product\",\n    \"type\": \"standard\",\n    \"is_lot_tracked\": true,\n    \"lot_tracking_method\": \"fifo\",\n    \"shelf_life_days\": 180,\n    \"near_expiry_threshold_days\": 60,\n    \"min_remaining_shelf_life_days\": 105,\n    \"expiry_aware_availability\": true,\n    \"barcode\": \"1234567890123\",\n    \"hts_code\": \"8471.30.0100\",\n    \"country_of_origin\": \"US\",\n    \"density_g_per_ml\": null,\n    \"density_source\": null,\n    \"density_basis\": null,\n    \"archived_at\": null,\n    \"created_at\": \"2024-06-15T12:00:00.000000Z\",\n    \"updated_at\": \"2024-06-15T12:00:00.000000Z\"\n  },\n  \"message\": \"Product created successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Delete Products","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/products","host":["{{baseUrl}}"],"path":["api","products"]},"description":"Bulk delete multiple products. For large datasets, deletion is processed in the background via a queue.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody fields (one of `ids`, `filters`, or `apply_to_all` is required):\n- ids (array, required without `filters`/`apply_to_all`): specific product IDs to delete\n- filters (object, required without `ids`/`apply_to_all`): filter criteria to select products for deletion\n- apply_to_all (boolean, optional): Targets every product matching the active `filters` instead of an explicit `ids` list. An empty `filters` object is indistinguishable from an absent one, so this flag is what keeps an unfiltered \"select all\" valid — send `{\"apply_to_all\": true, \"filters\": {}}` to delete every product.\n\n**Requires permission:** `products.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/products","host":["{{baseUrl}}"],"path":["api","products"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Products deleted successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Duplicate Product","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/{product}/duplicate","host":["{{baseUrl}}"],"path":["api","products",":product","duplicate"],"variable":[{"key":"product","value":"42","description":"ID of the product to duplicate"}]},"description":"Duplicate an existing product into a brand-new one with its own SKU.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe new product copies the source's identity-adjacent configuration - name, brand, type, dimensions, unit cost, financial line type, flags, attributes, categories, pricing tiers, suppliers (and their pricing), units of measure, tags, images, and kit/bundle components - but starts with zero inventory: no stock movements, FIFO layers, inventory cache, or channel listings. The barcode and inventory/velocity-derived figures (average cost, quantities sold, ABC/XYZ classification) are reset.\n\nA new unique `sku` is required. When `name` is omitted it defaults to the source name suffixed with \" (copy)\".\n\nMatrix (variation) products and variation children cannot be duplicated and return 422.\n\nRequires a Bearer token with the `products.create` permission.","body":{"mode":"raw","raw":"{\n  \"sku\": \"SKU-NEW-001-COPY\",\n  \"name\": \"New Product (copy)\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/products/{product}/duplicate","host":["{{baseUrl}}"],"path":["api","products",":product","duplicate"],"variable":[{"key":"product","value":"42","description":"ID of the product to duplicate"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 108,\n    \"sku\": \"SKU-NEW-001-COPY\",\n    \"name\": \"New Product (copy)\",\n    \"type\": \"standard\",\n    \"barcode\": null,\n    \"brand_name\": \"Acme Co\",\n    \"weight\": 0.5,\n    \"weight_unit\": \"kg\",\n    \"average_cost\": null,\n    \"archived_at\": null,\n    \"created_at\": \"2024-06-15T12:00:00.000000Z\",\n    \"updated_at\": \"2024-06-15T12:00:00.000000Z\"\n  },\n  \"message\": \"The product 'SKU-NEW-001-COPY' was duplicated successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Product Constants","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/constants","host":["{{baseUrl}}"],"path":["api","products","constants"]},"description":"Returns enumeration constants used in product forms: product types, weight units, dimension units, attribute types, warehouse types, and inventory movement types.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/constants","host":["{{baseUrl}}"],"path":["api","products","constants"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product_types\": [\n      \"standard\",\n      \"bundle\",\n      \"kit\",\n      \"matrix\"\n    ],\n    \"weight_units\": [\n      \"kg\",\n      \"lb\",\n      \"oz\",\n      \"g\"\n    ],\n    \"dimension_units\": [\n      \"cm\",\n      \"in\",\n      \"mm\",\n      \"ft\"\n    ],\n    \"attribute_types\": [\n      \"text\",\n      \"number\",\n      \"date\",\n      \"boolean\"\n    ],\n    \"warehouse_types\": [\n      \"standard\",\n      \"amazon_fba\",\n      \"supplier\"\n    ],\n    \"inventory_movement_type\": [\n      \"purchase\",\n      \"sale\",\n      \"adjustment\",\n      \"transfer\"\n    ],\n    \"inventory_movement_status\": [\n      \"available\",\n      \"allocated\",\n      \"on_hold\",\n      \"in_transit\"\n    ]\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Get Create Product Form","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/create","host":["{{baseUrl}}"],"path":["api","products","create"]},"description":"Returns the data needed to pre-populate the create product form.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/create","host":["{{baseUrl}}"],"path":["api","products","create"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {},\n  \"status\": \"success\"\n}"}]},{"name":"Get Edit Product Form","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{product}/edit","host":["{{baseUrl}}"],"path":["api","products",":product","edit"],"variable":[{"key":"product","value":"1","description":"Product ID"}]},"description":"Returns the product data pre-populated for the edit form.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{product}/edit","host":["{{baseUrl}}"],"path":["api","products",":product","edit"],"variable":[{"key":"product","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"sku\": \"SKU-001\",\n    \"name\": \"Blue Widget\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Search Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/search?q=blue","host":["{{baseUrl}}"],"path":["api","products","search"],"query":[{"key":"q","value":"blue","description":"Required. Search term (min 2 chars). Searches SKU, name, and barcode with fuzzy matching.","disabled":true}]},"description":"Search products by SKU, name, or barcode. Results are ranked by a 9-tier relevance scheme so typing a full SKU surfaces the exact product first:\n  1. Exact SKU match\n  2. Exact barcode match\n  3. Exact name match\n  4. SKU starts with the query\n  5. Barcode starts with the query\n  6. Name starts with the query\n  7. SKU contains the query (anywhere)\n  8. Name contains the query (anywhere)\n  9. Anything else (barcode contains, etc.)\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nOnly active (non-archived) products are returned.\n\nAuthentication: Requires Bearer token.\n\nMinimum query length: 2 characters.\n\nOptional `with` parameter (allowlist): pass `with=inventory` to include warehouse-aggregate inventory totals (`inventory_available`, `inventory_allocated`, `inventory_on_hold`) in each result.\n\nOptional `simple_assembleable=1` filter: restricts results to products that can actually be assembled or disassembled via Manufacturing Simple Mode (have an active default BOM with component lines, or legacy kit components). Anything else would fail at submit, so the picker blocks it.\n\n**Stock UoM fields:** When the product's `stockProductUom.unitOfMeasure` relation is eager-loaded (this endpoint always loads it), each item additionally exposes `stock_uom_id`, `stock_uom_code` (e.g. `KG`), and `stock_uom_name`. The legacy nested `stock_uom` object is still emitted via `?with=stock_uom`.\n\nPass without_planning_policy=1 to restrict results to products with no MRP planning policy at all (neither an item-level default nor a per-warehouse override), for example when choosing a product to create a planning policy for."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/search?q=blue","host":["{{baseUrl}}"],"path":["api","products","search"],"query":[{"key":"q","value":"blue","description":"Required. Search term (min 2 chars). Searches SKU, name, and barcode with fuzzy matching.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"sku\": \"SKU-001\",\n      \"name\": \"Blue Widget\",\n      \"barcode\": 123456789012,\n      \"type\": \"standard\",\n      \"stock_uom_id\": 1,\n      \"stock_uom_code\": \"EACH\",\n      \"stock_uom_name\": \"Each\",\n      \"inventory_available\": 42,\n      \"inventory_allocated\": 8,\n      \"inventory_on_hold\": 2\n    },\n    {\n      \"id\": 2,\n      \"sku\": \"SKU-002\",\n      \"name\": \"Blue Widget Pro\",\n      \"barcode\": 123456789013,\n      \"type\": \"standard\",\n      \"stock_uom_id\": 1,\n      \"stock_uom_code\": \"EACH\",\n      \"stock_uom_name\": \"Each\",\n      \"inventory_available\": 15,\n      \"inventory_allocated\": 0,\n      \"inventory_on_hold\": 0\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Barcode Lookup","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/barcode-lookup?code=123456789012","host":["{{baseUrl}}"],"path":["api","products","barcode-lookup"],"query":[{"key":"code","value":"123456789012","description":"Required. Barcode or SKU to look up (exact match only, no fuzzy matching).","disabled":true}]},"description":"Exact-match barcode or SKU lookup for barcode scanning. Prioritises barcode match first, then falls back to SKU match. Only active products are returned. Returns null in data if no match found.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Including stock levels** — add `?with=inventory` to return the aggregate (all-warehouse) stock figures alongside each match, instead of making a second call per product. It costs one extra indexed query for the whole batch, so a batch of 500 with stock levels still costs about the same as a batch of 5.\n\n`inventory_available` is floored at zero and is what a picker can pull today. `inventory_available_to_sell` is the promiseable figure — it excludes stock in transit and subtracts backorders, and goes negative when commitments exceed stock on hand. A product that has never held stock has no inventory record and reads as zero across all six fields rather than null."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/barcode-lookup?code=123456789012","host":["{{baseUrl}}"],"path":["api","products","barcode-lookup"],"query":[{"key":"code","value":"123456789012","description":"Required. Barcode or SKU to look up (exact match only, no fuzzy matching).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"sku\": \"SKU-001\",\n    \"name\": \"Blue Widget\",\n    \"barcode\": 123456789012,\n    \"inventory_total\": 42,\n    \"inventory_available\": 30,\n    \"inventory_available_to_sell\": 26,\n    \"inventory_allocated\": 12,\n    \"inventory_on_hold\": 0,\n    \"inventory_in_transit\": 0\n  }\n}"}]},{"name":"Bulk Barcode Lookup","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/barcode-lookup/bulk?codes=0123456789012,0123456789013,SKU-001","host":["{{baseUrl}}"],"path":["api","products","barcode-lookup","bulk"],"query":[{"key":"codes","value":"0123456789012,0123456789013,SKU-001","description":"Required. Barcodes or SKUs to look up, comma-separated (or a JSON array). Max 500. Exact match only. On POST, send as a JSON array in the body instead.","disabled":true}]},"description":"Exact-match barcode or SKU lookup for a batch of codes in one request.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nSame resolution as Barcode Lookup — barcode is matched first, SKU is the fallback, and only active products are returned — but resolving many codes costs two indexed queries instead of one request per code. Intended for integrations that walk a catalog.\n\nAccepts GET or POST. Use GET with a comma-separated `codes` value for short lists; use POST with a JSON array once the list is too long for a URL. Up to 500 codes per request.\n\nThe response is an object keyed by the code you submitted, so each result can be matched back to its input — with the SKU fallback in play, a flat list would not tell you which code produced which product. A code that matched nothing is returned with an explicit `null` rather than being left out.\n\nBarcodes are not guaranteed unique. When more than one active product carries a code, the entry is marked `\"ambiguous\": true` and the product returned is the lowest-numbered match — stable across calls, but worth surfacing to a human rather than trusting blindly.\n\nAuthentication: Requires Bearer token.\n\n**Including stock levels** — add `?with=inventory` to return the aggregate (all-warehouse) stock figures alongside each match, instead of making a second call per product. It costs one extra indexed query for the whole batch, so a batch of 500 with stock levels still costs about the same as a batch of 5.\n\n`inventory_available` is floored at zero and is what a picker can pull today. `inventory_available_to_sell` is the promiseable figure — it excludes stock in transit and subtracts backorders, and goes negative when commitments exceed stock on hand. A product that has never held stock has no inventory record and reads as zero across all six fields rather than null.","body":{"mode":"raw","raw":"{\n  \"codes\": [\n    123456789012,\n    123456789013,\n    \"SKU-001\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/products/barcode-lookup/bulk?codes=0123456789012,0123456789013,SKU-001","host":["{{baseUrl}}"],"path":["api","products","barcode-lookup","bulk"],"query":[{"key":"codes","value":"0123456789012,0123456789013,SKU-001","description":"Required. Barcodes or SKUs to look up, comma-separated (or a JSON array). Max 500. Exact match only. On POST, send as a JSON array in the body instead.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"123456789012\": {\n      \"id\": 1,\n      \"sku\": \"SKU-001\",\n      \"name\": \"Blue Widget\",\n      \"barcode\": 123456789012,\n      \"ambiguous\": false\n    },\n    \"123456789013\": {\n      \"id\": 42,\n      \"sku\": \"SKU-042\",\n      \"name\": \"Red Widget\",\n      \"barcode\": 123456789013,\n      \"ambiguous\": true\n    },\n    \"SKU-001\": {\n      \"id\": 1,\n      \"sku\": \"SKU-001\",\n      \"name\": \"Blue Widget\",\n      \"barcode\": 123456789012,\n      \"ambiguous\": false\n    },\n    \"9999999999999\": null\n  }\n}"}]},{"name":"Check Product Deletability","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/is-deletable","host":["{{baseUrl}}"],"path":["api","products","is-deletable"]},"description":"Check whether one or more products can be safely deleted. Returns deletability status with reasons and usage record samples for each product.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- ids (required): array of product IDs (integers, must exist in DB)\n\nFor each product, returns:\n- deletable: true if safe to delete\n- reason: human-readable reason if not deletable\n- usages: array of blocking usage types, each with: relation, label, count, message, and sample records\n\nUsage relations include: salesOrderLines, bundleSalesOrderLines, purchaseOrderLines, inventoryMovements, fifoLayers, inventoryAdjustments, inventoryAssemblyLines, warehouseTransferLines, returnReceiptLines, rmaLines, salesCreditLines, blemishedProducts, inboundShipmentLines, and subscriptionEditions (each is a RESTRICT foreign key, so any referencing row blocks deletion; product variations are checked too, so a matrix parent is reported not deletable when any of its variants is referenced). bundleSalesOrderLines appears when the product was sold as a bundle parent (referenced via sales_order_lines.bundle_id, a RESTRICT foreign key); such bundles are reported as not deletable so they can be archived instead.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/products/is-deletable","host":["{{baseUrl}}"],"path":["api","products","is-deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Blue Widget\",\n      \"sku\": \"SKU-001\",\n      \"deletable\": false,\n      \"reason\": \"There are 5 sales order lines associated with this product(SKU-001).\",\n      \"usages\": [\n        {\n          \"relation\": \"salesOrderLines\",\n          \"label\": \"Sales Order Lines\",\n          \"count\": 5,\n          \"message\": \"There are 5 sales order lines associated with this product(SKU-001).\",\n          \"records\": [\n            {\n              \"label\": \"SO-1001\",\n              \"detail\": \"Qty: 2\"\n            },\n            {\n              \"label\": \"SO-1002\",\n              \"detail\": \"Qty: 1\"\n            }\n          ]\n        }\n      ]\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Starter Bundle Kit\",\n      \"sku\": \"BNDL-100\",\n      \"deletable\": false,\n      \"reason\": \"There are 4 bundle sales order lines associated with this product(BNDL-100).\",\n      \"usages\": [\n        {\n          \"relation\": \"bundleSalesOrderLines\",\n          \"label\": \"Bundle Sales Order Lines\",\n          \"count\": 4,\n          \"message\": \"There are 4 bundle sales order lines associated with this product(BNDL-100).\",\n          \"records\": [\n            {\n              \"label\": \"SO-2001\",\n              \"detail\": \"Qty: 1\"\n            },\n            {\n              \"label\": \"SO-2002\",\n              \"detail\": \"Qty: 3\"\n            }\n          ]\n        }\n      ]\n    },\n    {\n      \"id\": 3,\n      \"name\": \"Red Widget\",\n      \"sku\": \"SKU-002\",\n      \"deletable\": true,\n      \"reason\": null,\n      \"usages\": []\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Archive Product","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/products/{id}/archive","host":["{{baseUrl}}"],"path":["api","products",":id","archive"],"variable":[{"key":"id","value":"1","description":"Product ID"}]},"description":"Archive a product by ID. Sets archived_at timestamp.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns a warning if the product is already archived.\n\n**Requires permission:** `products.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/products/{id}/archive","host":["{{baseUrl}}"],"path":["api","products",":id","archive"],"variable":[{"key":"id","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Product SKU-001 archived successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Unarchive Product","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/products/{id}/unarchived","host":["{{baseUrl}}"],"path":["api","products",":id","unarchived"],"variable":[{"key":"id","value":"1","description":"Product ID"}]},"description":"Unarchive a product by ID. Clears the archived_at timestamp.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `products.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/products/{id}/unarchived","host":["{{baseUrl}}"],"path":["api","products",":id","unarchived"],"variable":[{"key":"id","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Product SKU-001 unarchived successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Archive Products","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/archive","host":["{{baseUrl}}"],"path":["api","products","archive"]},"description":"Bulk archive multiple products.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody fields:\n- ids (array): specific product IDs to archive\n- apply_to_all (boolean): if true, applies to all products matching the current filters instead of ids\n- filters (object): filter criteria used when apply_to_all is true\n\n**Requires permission:** `products.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/products/archive","host":["{{baseUrl}}"],"path":["api","products","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Products archived successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Unarchive Products","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/unarchive","host":["{{baseUrl}}"],"path":["api","products","unarchive"]},"description":"Bulk unarchive multiple products.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody fields:\n- ids (array): specific product IDs to unarchive\n- apply_to_all (boolean): if true, applies to all matching products\n- filters (object): filter criteria used when apply_to_all is true\n\n**Requires permission:** `products.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/products/unarchive","host":["{{baseUrl}}"],"path":["api","products","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Products unarchived successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Product Inventory","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{productId}/inventory?no_fba=false&no_suppliers=false&legacy_mode=false&include_mcf=false","host":["{{baseUrl}}"],"path":["api","products",":productId","inventory"],"query":[{"key":"no_fba","value":"false","description":"Exclude FBA warehouses from response (boolean)","disabled":true},{"key":"no_suppliers","value":"false","description":"Exclude supplier warehouses from response (boolean)","disabled":true},{"key":"legacy_mode","value":"false","description":"Use legacy inventory mode without Pan-EU FBA rollup (boolean)","disabled":true},{"key":"include_mcf","value":"false","description":"Include MCF inventory (boolean)","disabled":true}],"variable":[{"key":"productId","value":"1","description":"Product ID"}]},"description":"Get detailed inventory breakdown for a product across all warehouses. Includes FBA real-time data, Pan-EU rollup, supplier drop-ship inventory, and purchase order inbound quantities.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\nEach warehouse row and the totals include `non_sellable` — physically-present stock in non-sellable bins (excluded from `available` but included in on-hand)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{productId}/inventory?no_fba=false&no_suppliers=false&legacy_mode=false&include_mcf=false","host":["{{baseUrl}}"],"path":["api","products",":productId","inventory"],"query":[{"key":"no_fba","value":"false","description":"Exclude FBA warehouses from response (boolean)","disabled":true},{"key":"no_suppliers","value":"false","description":"Exclude supplier warehouses from response (boolean)","disabled":true},{"key":"legacy_mode","value":"false","description":"Use legacy inventory mode without Pan-EU FBA rollup (boolean)","disabled":true},{"key":"include_mcf","value":"false","description":"Include MCF inventory (boolean)","disabled":true}],"variable":[{"key":"productId","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total\": {\n      \"inbound\": false,\n      \"stock\": false,\n      \"allocated\": false,\n      \"backorder_planned\": false,\n      \"backorder_awaiting_receipt\": false,\n      \"backorder\": false,\n      \"on_hold\": false,\n      \"available\": false,\n      \"available_to_sell\": false,\n      \"in_transit\": false,\n      \"backordered\": false,\n      \"consigned\": false,\n      \"non_sellable\": 15\n    },\n    \"unit_cost\": 12.5,\n    \"warehouses\": {\n      \"Main Warehouse\": {\n        \"inbound\": false,\n        \"stock\": false,\n        \"allocated\": false,\n        \"backorder_planned\": false,\n        \"backorder_awaiting_receipt\": false,\n        \"backorder\": false,\n        \"on_hold\": false,\n        \"available\": false,\n        \"available_to_sell\": false,\n        \"in_transit\": false,\n        \"warehouse_id\": false,\n        \"warehouse_name\": false,\n        \"warehouse_type\": false,\n        \"initial_inventory\": false,\n        \"supplier\": false,\n        \"listing_count\": false,\n        \"country_code\": false,\n        \"consigned\": false,\n        \"non_sellable\": 15\n      }\n    },\n    \"bundle_data\": [],\n    \"cache_updated_at\": \"2024-06-15T12:00:00.000000Z\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Get Available Lots (FEFO)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{productId}/available-lots?warehouse_id=1","host":["{{baseUrl}}"],"path":["api","products",":productId","available-lots"],"query":[{"key":"warehouse_id","value":"1","description":"REQUIRED. Warehouse to list available lots for (integer, must exist in warehouses). The endpoint returns 422 if omitted.","disabled":true}],"variable":[{"key":"productId","value":"1","description":"Product ID"}]},"description":"Active FIFO layers available to CONSUME for a product in a given warehouse — only layers with remaining (unfulfilled) quantity are returned.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nLayers are ordered by the product's removal strategy: FEFO products list soonest-expiry first with undated lots last; FIFO products list by layer date. This drives the lot-override picker (LotConsumptionPanel) and its FEFO pre-fill on negative inventory events (fulfillment, transfer ship, decrease adjustment).\n\nAuthentication: Requires Bearer token.\n\nPath params:\n- productId: Product ID\n\nQuery params:\n- warehouse_id (REQUIRED, integer, exists:warehouses,id): warehouse to scope available lots to. Returns 422 if missing.\n\nResponse (wrapped in {data}) — array of lots:\n- fifo_layer_id (int): FIFO layer ID to reference in a lot_allocation override\n- batch_number (string|null): lot/batch number\n- expiry_date (date|null, YYYY-MM-DD): lot expiry date\n- manufacture_date (date|null, YYYY-MM-DD): lot manufacture date\n- available_quantity (float): remaining quantity available to consume from this layer\n- fifo_layer_date (date|null, YYYY-MM-DD): the layer's FIFO date\n- days_to_expiry (int|null): whole days until expiry (negative if already expired); null when undated\n- is_expired (bool): whether the lot's expiry date is before today\n- avg_cost (float): average unit cost of the layer"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{productId}/available-lots?warehouse_id=1","host":["{{baseUrl}}"],"path":["api","products",":productId","available-lots"],"query":[{"key":"warehouse_id","value":"1","description":"REQUIRED. Warehouse to list available lots for (integer, must exist in warehouses). The endpoint returns 422 if omitted.","disabled":true}],"variable":[{"key":"productId","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"fifo_layer_id\": 5012,\n      \"batch_number\": \"LOT-A\",\n      \"expiry_date\": \"2030-01-31\",\n      \"manufacture_date\": \"2026-01-01\",\n      \"available_quantity\": 40,\n      \"fifo_layer_date\": \"2026-01-10\",\n      \"days_to_expiry\": 1321,\n      \"is_expired\": false,\n      \"avg_cost\": 12.5\n    },\n    {\n      \"fifo_layer_id\": 5040,\n      \"batch_number\": null,\n      \"expiry_date\": null,\n      \"manufacture_date\": null,\n      \"available_quantity\": 15,\n      \"fifo_layer_date\": \"2026-02-02\",\n      \"days_to_expiry\": null,\n      \"is_expired\": false,\n      \"avg_cost\": 11.75\n    }\n  ]\n}"}]},{"name":"Get FBA Inventory","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{warehouseId}/{productId}/inventory-fba","host":["{{baseUrl}}"],"path":["api","products",":warehouseId",":productId","inventory-fba"],"variable":[{"key":"warehouseId","value":"2","description":"Warehouse ID (must be Amazon FBA type)"},{"key":"productId","value":"1","description":"Product ID"}]},"description":"Returns the Amazon FBA inventory listings for a product at a specific warehouse. Each row is an FBA-enabled product listing on the warehouse's sales channel, with its real-time FBA inventory snapshot (available, reserved, in transit, total, inbound) and Amazon **condition** (e.g. `NewItem`, `UsedLikeNew`). Rows are sorted by available quantity descending. Listings are scoped to the warehouse's marketplace (the warehouse `country_code`): a US FBA warehouse returns only US listings, never another marketplace's listing (e.g. a Canadian one), mirroring how Amazon MCF fulfils from the destination's marketplace.\n\nNote: Amazon MCF only fulfills New-condition inventory — listings with a non-New `condition` cannot be fulfilled via createFulfillmentOrder even when `available` is greater than 0.\n\nAuthentication: Requires Bearer token. Rows come from two sources: (1) Amazon listings mapped to this product via product_listings, and (2) the FNSKU mapper — the seller SKU (MSKU) behind each SELLABLE FNSKU mapped to this product in the warehouse marketplace, with its FBA report stock. Source 2 lets a product whose marketplace listing is mapped to a different SKU.io product (a per-market \"-CA-O\" shadow listing) still surface its shippable seller SKU, so Amazon MCF submission resolves correctly. Mirrors ResolveFbaSellerSku.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{warehouseId}/{productId}/inventory-fba","host":["{{baseUrl}}"],"path":["api","products",":warehouseId",":productId","inventory-fba"],"variable":[{"key":"warehouseId","value":"2","description":"Warehouse ID (must be Amazon FBA type)"},{"key":"productId","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"integration_instance_id\": 2,\n      \"product_listing_id\": 9380,\n      \"seller_sku\": \"BTS-AA-FBA-USED\",\n      \"asin\": \"B07XYZ1234\",\n      \"condition\": \"UsedLikeNew\",\n      \"available\": 1,\n      \"reserved\": 0,\n      \"in_transit\": 0,\n      \"total\": 1,\n      \"inbound\": 0\n    },\n    {\n      \"integration_instance_id\": 2,\n      \"product_listing_id\": 9381,\n      \"seller_sku\": \"2291988-FBA-1\",\n      \"asin\": \"B07ABC5678\",\n      \"condition\": \"NewItem\",\n      \"available\": 13,\n      \"reserved\": 2,\n      \"in_transit\": 0,\n      \"total\": 15,\n      \"inbound\": 5\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Refresh Inventory Cache","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/products/{productId}/inventory/refresh","host":["{{baseUrl}}"],"path":["api","products",":productId","inventory","refresh"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]},"description":"Synchronously refreshes the inventory cache for a specific product. This forces a recalculation of inventory totals and average cost.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 500 if the refresh fails."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/products/{productId}/inventory/refresh","host":["{{baseUrl}}"],"path":["api","products",":productId","inventory","refresh"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Inventory cache refreshed successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Sales Channel Inventory","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{product}/get-sales-channel-inventory","host":["{{baseUrl}}"],"path":["api","products",":product","get-sales-channel-inventory"],"variable":[{"key":"product","value":"1","description":"Product ID"}]},"description":"Get inventory broken down by sales channel for a specific product.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{product}/get-sales-channel-inventory","host":["{{baseUrl}}"],"path":["api","products",":product","get-sales-channel-inventory"],"variable":[{"key":"product","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"sales_channel\": \"Shopify Store\",\n      \"available\": 80,\n      \"allocated\": 20\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Get Last Purchase Price","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{product}/last-purchase-price","host":["{{baseUrl}}"],"path":["api","products",":product","last-purchase-price"],"variable":[{"key":"product","value":"1","description":"Product ID"}]},"description":"Get the most recent purchase price for a product from non-draft purchase orders.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns null in data if no purchase order lines exist with a positive amount."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{product}/last-purchase-price","host":["{{baseUrl}}"],"path":["api","products",":product","last-purchase-price"],"variable":[{"key":"product","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"amount\": 10,\n    \"purchase_order_id\": 55,\n    \"purchase_order_date\": \"2024-05-15\"\n  }\n}"}]},{"name":"Get Product Inventory Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{productId}/inventory-movements?page=1&limit=10&sort=-created_at&included[]=&excluded[]=","host":["{{baseUrl}}"],"path":["api","products",":productId","inventory-movements"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"limit","value":"10","description":"Items per page","disabled":true},{"key":"sort","value":"-created_at","description":"Sort field. Prefix - for descending.","disabled":true},{"key":"included[]","value":"","description":"Columns to include","disabled":true},{"key":"excluded[]","value":"","description":"Columns to exclude","disabled":true}],"variable":[{"key":"productId","value":"1","description":"Product ID"}]},"description":"Get paginated inventory movements for a specific product.\n\nAuthentication: Requires Bearer token.\n\nResponse includes movements with type, quantity, warehouse, FIFO layer, and reference data."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{productId}/inventory-movements?page=1&limit=10&sort=-created_at&included[]=&excluded[]=","host":["{{baseUrl}}"],"path":["api","products",":productId","inventory-movements"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"limit","value":"10","description":"Items per page","disabled":true},{"key":"sort","value":"-created_at","description":"Sort field. Prefix - for descending.","disabled":true},{"key":"included[]","value":"","description":"Columns to include","disabled":true},{"key":"excluded[]","value":"","description":"Columns to exclude","disabled":true}],"variable":[{"key":"productId","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 101,\n      \"inventory_movement_date\": \"2024-06-01\",\n      \"product_id\": 1,\n      \"sku\": \"SKU-001\",\n      \"type\": {\n        \"name\": \"purchase\",\n        \"link_type\": \"App\\\\Models\\\\PurchaseOrderLine\",\n        \"link_id\": 55,\n        \"parent_link_id\": 10,\n        \"detail_link_id\": null,\n        \"manufacturing_order\": null\n      },\n      \"inventory_status\": \"available\",\n      \"quantity\": 100,\n      \"warehouse\": {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      },\n      \"layer\": {\n        \"fifo_layer_id\": 3,\n        \"name\": \"Layer 2024-06-01\"\n      },\n      \"reference\": \"PO-1001\",\n      \"created_at\": \"2024-06-01T10:00:00.000000Z\"\n    }\n  ],\n  \"table_specifications\": {},\n  \"meta\": {\n    \"current_page\": 1,\n    \"last_page\": 5,\n    \"per_page\": 10,\n    \"total\": 48\n  }\n}"}]},{"name":"Get Product FIFO Layers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{productId}/fifo-layers","host":["{{baseUrl}}"],"path":["api","products",":productId","fifo-layers"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]},"description":"Get FIFO cost layers for a specific product.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{productId}/fifo-layers","host":["{{baseUrl}}"],"path":["api","products",":productId","fifo-layers"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3,\n      \"product_id\": 1,\n      \"quantity\": 100,\n      \"unit_cost\": 12.5,\n      \"created_at\": \"2024-06-01T10:00:00.000000Z\"\n    }\n  ]\n}"}]},{"name":"List Consignment Inventory","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/consignment/inventory","host":["{{baseUrl}}"],"path":["api","consignment","inventory"]},"description":"Consigned (supplier-owned) stock on hand that hasn't sold yet, one row per cost layer, with unit cost, total value, and days-on-consignment aging, plus the supplier and purchase order it came from. Drives the return-unsold workflow.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/consignment/inventory","host":["{{baseUrl}}"],"path":["api","consignment","inventory"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 40122,\n      \"sku\": \"WIDGET-1\",\n      \"product_id\": 881,\n      \"product_name\": \"Blue Widget\",\n      \"supplier_id\": 44,\n      \"supplier_name\": \"Acme Wholesale\",\n      \"warehouse_id\": 12,\n      \"warehouse_name\": \"Acme Consignment\",\n      \"purchase_order_id\": 10432,\n      \"purchase_order_number\": \"PO-10432\",\n      \"on_hand_quantity\": 40,\n      \"unit_cost\": 4,\n      \"total_value\": 160,\n      \"days_on_consignment\": 52\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Get Consignment Valuation Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/consignment/valuation-summary","host":["{{baseUrl}}"],"path":["api","consignment","valuation-summary"]},"description":"Off-balance-sheet valuation memo for consigned (supplier-owned) stock on hand. It reports the total value of consigned inventory you hold and the equal received-not-billed obligation that would arise if it were grossed up onto the balance sheet, broken down by supplier. Because the asset and obligation always offset, the net balance-sheet impact is zero. This is a disclosure only — it is never posted to the general ledger and is not synced to any accounting integration.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/consignment/valuation-summary","host":["{{baseUrl}}"],"path":["api","consignment","valuation-summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"consigned_inventory_value\": 12480,\n    \"received_not_billed_obligation\": 12480,\n    \"net_balance_sheet_impact\": 0,\n    \"total_on_hand_quantity\": 3120,\n    \"supplier_count\": 2,\n    \"suppliers\": [\n      {\n        \"supplier_id\": 44,\n        \"supplier_name\": \"Acme Wholesale\",\n        \"on_hand_quantity\": 2000,\n        \"total_value\": 8000\n      },\n      {\n        \"supplier_id\": 51,\n        \"supplier_name\": \"Globex Supply Co.\",\n        \"on_hand_quantity\": 1120,\n        \"total_value\": 4480\n      }\n    ]\n  }\n}"}]},{"name":"Get Product Attributes","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{productId}/attributes","host":["{{baseUrl}}"],"path":["api","products",":productId","attributes"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]},"description":"Get all available attributes for a product, including direct attributes and those inherited from category attribute groups.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{productId}/attributes","host":["{{baseUrl}}"],"path":["api","products",":productId","attributes"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"Color\": {\n      \"id\": 1,\n      \"attribute_id\": 5,\n      \"name\": \"Color\",\n      \"value\": \"Blue\",\n      \"type\": \"text\",\n      \"attribute_group\": {\n        \"id\": 2,\n        \"name\": \"Product Details\"\n      }\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Update Product Attributes","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/{productId}/attributes","host":["{{baseUrl}}"],"path":["api","products",":productId","attributes"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]},"description":"Add or update attributes on a product. Syncs without detaching other existing attributes.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields in attributes[]:\n- id (required without name): attribute ID\n- name (required without id): attribute name (max 255)\n- value (nullable): attribute value\n\n**Option-list enforcement:** when an attribute is option-typed and does not allow custom values, each submitted value must be one of its configured options; anything else is rejected with a 422 naming the attribute and the offending value. Matching is case-insensitive and accepted values are stored with the configured option casing (submitting `7 MIL` stores `7 Mil`). Multi-select values may be separated by newlines or commas and are stored newline-joined. Attributes that allow custom values, and attributes without an option list, accept any value.\n\n**Requires permission:** `products.manage_variants`","body":{"mode":"raw","raw":"{\n  \"attributes\": [\n    {\n      \"id\": 5,\n      \"value\": \"Red\"\n    },\n    {\n      \"name\": \"Material\",\n      \"value\": \"Plastic\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/products/{productId}/attributes","host":["{{baseUrl}}"],"path":["api","products",":productId","attributes"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Product attributes updated successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Delete Product Attributes","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/products/{productId}/attributes","host":["{{baseUrl}}"],"path":["api","products",":productId","attributes"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]},"description":"Remove specific attributes from a product by attribute IDs.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody fields:\n- attributes (required, array, min 1): array of attribute IDs to remove (must exist in attributes table)\n\n**Requires permission:** `products.manage_variants`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/products/{productId}/attributes","host":["{{baseUrl}}"],"path":["api","products",":productId","attributes"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Product attributes 5,7,12 deleted successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Product Attributes Grouped","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{productId}/attributes-grouped","host":["{{baseUrl}}"],"path":["api","products",":productId","attributes-grouped"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]},"description":"Get product attributes organised into grouped (by attribute group) and direct (ungrouped) sections.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{productId}/attributes-grouped","host":["{{baseUrl}}"],"path":["api","products",":productId","attributes-grouped"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"direct\": [\n      {\n        \"id\": 1,\n        \"name\": \"Description\",\n        \"value\": \"A blue widget\"\n      }\n    ],\n    \"groups\": {\n      \"2\": {\n        \"id\": 2,\n        \"name\": \"Product Details\",\n        \"attributes\": [\n          {\n            \"id\": 2,\n            \"name\": \"Color\",\n            \"value\": \"Blue\"\n          }\n        ]\n      }\n    }\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Assign Attribute Groups","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/{productId}/assign-attribute-groups","host":["{{baseUrl}}"],"path":["api","products",":productId","assign-attribute-groups"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]},"description":"Sync attribute groups assigned to a product. This replaces all current attribute group assignments.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody fields:\n- attribute_groups_ids (required, array): array of attribute group IDs (must exist in attribute_groups table)","body":{"mode":"raw","raw":"{\n  \"attribute_groups_ids\": [\n    2,\n    5\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/products/{productId}/assign-attribute-groups","host":["{{baseUrl}}"],"path":["api","products",":productId","assign-attribute-groups"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Attribute groups assigned successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Get All Attributes for Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/attributes?product_ids=[1,2,3]","host":["{{baseUrl}}"],"path":["api","products","attributes"],"query":[{"key":"product_ids","value":"[1,2,3]","description":"Required. JSON-encoded array of product IDs","disabled":true}]},"description":"Get unique variant attributes across multiple products. Used by mapping modals.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns a deduplicated list of variant attributes across all provided product IDs."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/attributes?product_ids=[1,2,3]","host":["{{baseUrl}}"],"path":["api","products","attributes"],"query":[{"key":"product_ids","value":"[1,2,3]","description":"Required. JSON-encoded array of product IDs","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"attribute_id\": 5,\n      \"name\": \"Color\",\n      \"value\": \"Blue\"\n    },\n    {\n      \"id\": 2,\n      \"attribute_id\": 6,\n      \"name\": \"Size\",\n      \"value\": \"M\"\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Get Product Suppliers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{productId}/suppliers","host":["{{baseUrl}}"],"path":["api","products",":productId","suppliers"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]},"description":"Get all supplier relationships for a product, including pricing tiers per supplier.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{productId}/suppliers","host":["{{baseUrl}}"],"path":["api","products",":productId","suppliers"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 10,\n      \"product_id\": 1,\n      \"supplier_id\": 5,\n      \"supplier_sku\": \"MS-BW-001\",\n      \"leadtime\": 14,\n      \"minimum_order_quantity\": 100,\n      \"is_default\": true,\n      \"supplier\": {\n        \"id\": 5,\n        \"name\": \"Main Supplier\"\n      },\n      \"supplier_pricing_tiers\": [\n        {\n          \"name\": \"Default\",\n          \"price\": 10,\n          \"currency_code\": \"USD\"\n        }\n      ]\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Set Default Supplier","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/products/{productId}/set-default-supplier/{supplierId}","host":["{{baseUrl}}"],"path":["api","products",":productId","set-default-supplier",":supplierId"],"variable":[{"key":"productId","value":"1","description":"Product ID"},{"key":"supplierId","value":"5","description":"Supplier ID"}]},"description":"Mark a supplier as the default supplier for a product. Clears the is_default flag from all other suppliers for this product.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBoth product_id and supplier_id must exist in the database."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/products/{productId}/set-default-supplier/{supplierId}","host":["{{baseUrl}}"],"path":["api","products",":productId","set-default-supplier",":supplierId"],"variable":[{"key":"productId","value":"1","description":"Product ID"},{"key":"supplierId","value":"5","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Supplier 5 set as default for product 1\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Product Components","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{productId}/components","host":["{{baseUrl}}"],"path":["api","products",":productId","components"],"variable":[{"key":"productId","value":"1","description":"Product ID (must be bundle or kit type)"}]},"description":"Get all component products for a bundle or kit product.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{productId}/components","host":["{{baseUrl}}"],"path":["api","products",":productId","components"],"variable":[{"key":"productId","value":"1","description":"Product ID (must be bundle or kit type)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"[\n  {\n    \"id\": 3,\n    \"sku\": \"COMP-001\",\n    \"name\": \"Component A\",\n    \"quantity\": 2\n  }\n]"}]},{"name":"Get Product Bundles","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{productId}/bundles","host":["{{baseUrl}}"],"path":["api","products",":productId","bundles"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]},"description":"Get all bundles that this product is a component of.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{productId}/bundles","host":["{{baseUrl}}"],"path":["api","products",":productId","bundles"],"variable":[{"key":"productId","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"[\n  {\n    \"id\": 8,\n    \"sku\": \"BUNDLE-001\",\n    \"name\": \"Starter Bundle\",\n    \"type\": \"bundle\"\n  }\n]"}]},{"name":"Get Potential Bundles","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{productId}/potential-bundles","host":["{{baseUrl}}"],"path":["api","products",":productId","potential-bundles"],"variable":[{"key":"productId","value":"1","description":"Product ID (must be kit type)"}]},"description":"Calculate potential bundle quantities for a kit product based on current stock of its components per warehouse and inventory status.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nOnly valid for kit-type products. Throws 500 if product is not a kit."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{productId}/potential-bundles","host":["{{baseUrl}}"],"path":["api","products",":productId","potential-bundles"],"variable":[{"key":"productId","value":"1","description":"Product ID (must be kit type)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3,\n      \"sku\": \"COMP-001\",\n      \"name\": \"Component A\",\n      \"bundle_data\": [\n        {\n          \"warehouse_id\": 1,\n          \"inventory_available\": {\n            \"quantity\": 200,\n            \"max_bundle_quantity\": 100\n          },\n          \"inventory_allocated\": {\n            \"quantity\": 30,\n            \"max_bundle_quantity\": 15\n          },\n          \"inventory_on_hold\": {\n            \"quantity\": 10,\n            \"max_bundle_quantity\": 5\n          },\n          \"inventory_in_transit\": {\n            \"quantity\": 0,\n            \"max_bundle_quantity\": 0\n          }\n        }\n      ]\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Get Product Drop-ship Status","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{product}/dropship-status","host":["{{baseUrl}}"],"path":["api","products",":product","dropship-status"],"variable":[{"key":"product","value":"123","description":"Product ID"}]},"description":"Return the drop-ship eligibility checklist for a product — whether a sales order line for this product will auto-route to a supplier's drop-ship warehouse, and which gate conditions are currently met or failing.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nOptionally scope the check to a specific supplier with the `supplier_id` query parameter; omit it to evaluate against the product's default supplier.\n\nA line auto-routes to a supplier's drop-ship warehouse when the product is drop-shippable, drop-shipping is enabled, the supplier has a drop-ship warehouse ranked in the warehouse priority list, and its live stock covers the line — there is no per-supplier-product opt-in. The `gates` object reports each eligibility condition, and `missing` lists the labels of the gates that are not yet satisfied.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{product}/dropship-status","host":["{{baseUrl}}"],"path":["api","products",":product","dropship-status"],"variable":[{"key":"product","value":"123","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"product_id\": 123,\n  \"supplier_id\": 45,\n  \"supplier_name\": \"Acme Supply\",\n  \"eligible\": false,\n  \"auto_routing_enabled\": true,\n  \"gates\": {\n    \"supplier_product_link\": true,\n    \"product_is_dropshippable\": true,\n    \"supplier_warehouse_enabled\": true,\n    \"supplier_feed_healthy\": false\n  },\n  \"missing\": [\n    \"Supplier feed healthy\"\n  ]\n}"}]},{"name":"Get Product 3PL Costs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{product}/three-pl-costs","host":["{{baseUrl}}"],"path":["api","products",":product","three-pl-costs"],"variable":[{"key":"product","value":"123","description":"Product ID"}]},"description":"Get a product's 3PL cost over the last N days, from 3PL billing feeds (ShipBob today): fulfillment fees and 3PL shipment freight allocated onto the product's sales order lines (orders dated in the window), grouped by financial line type; storage fees allocated to the product (prorated to the window), grouped by cost entry type; units sold; total; cost per unit; and the same split per integration instance.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nQuery: days (integer, 1–365, default 30)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{product}/three-pl-costs","host":["{{baseUrl}}"],"path":["api","products",":product","three-pl-costs"],"variable":[{"key":"product","value":"123","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"Success\",\n  \"data\": {\n    \"product_id\": 88,\n    \"currency_code\": \"USD\",\n    \"days\": 30,\n    \"from\": \"2026-09-09\",\n    \"to\": \"2026-10-08\",\n    \"units_sold\": 412,\n    \"fulfillment_total\": 231.4,\n    \"storage_total\": 12.8,\n    \"total\": 244.2,\n    \"cost_per_unit\": 0.5927,\n    \"fulfillment\": [\n      {\n        \"financial_line_type_id\": 12,\n        \"name\": \"Shipping Cost\",\n        \"amount\": 180.1\n      },\n      {\n        \"financial_line_type_id\": 31,\n        \"name\": \"3PL Fulfillment Fee\",\n        \"amount\": 38.2\n      },\n      {\n        \"financial_line_type_id\": 32,\n        \"name\": \"3PL Shipping Surcharge\",\n        \"amount\": 13.1\n      }\n    ],\n    \"storage\": [\n      {\n        \"name\": \"ShipBob Storage Fees\",\n        \"amount\": 12.8\n      }\n    ],\n    \"by_instance\": [\n      {\n        \"integration_instance_id\": 4,\n        \"name\": \"Mello ShipBob\",\n        \"fulfillment\": 231.4,\n        \"storage\": 12.8,\n        \"total\": 244.2\n      }\n    ]\n  }\n}"}]},{"name":"Get Product Variations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{product}/variations","host":["{{baseUrl}}"],"path":["api","products",":product","variations"],"variable":[{"key":"product","value":"1","description":"Product ID (must be matrix type)"}]},"description":"Get all variation products for a matrix (parent) product.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 400 if the product is not a matrix type."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{product}/variations","host":["{{baseUrl}}"],"path":["api","products",":product","variations"],"variable":[{"key":"product","value":"1","description":"Product ID (must be matrix type)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5,\n      \"sku\": \"SKU-001-RED-S\",\n      \"name\": \"Blue Widget - Red - Small\",\n      \"type\": \"standard\",\n      \"is_variation\": true\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Get Variation Attributes","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{product}/variations/attributes","host":["{{baseUrl}}"],"path":["api","products",":product","variations","attributes"],"variable":[{"key":"product","value":"1","description":"Product ID (must be matrix type)"}]},"description":"Get unique variant attributes used across all variations of a matrix product.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 400 if product is not a matrix type."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{product}/variations/attributes","host":["{{baseUrl}}"],"path":["api","products",":product","variations","attributes"],"variable":[{"key":"product","value":"1","description":"Product ID (must be matrix type)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"attribute_id\": 5,\n      \"name\": \"Color\"\n    },\n    {\n      \"id\": 2,\n      \"attribute_id\": 6,\n      \"name\": \"Size\"\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"List Product Images","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{product}/images","host":["{{baseUrl}}"],"path":["api","products",":product","images"],"variable":[{"key":"product","value":"1","description":"Product ID"}]},"description":"Get all images for a product.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{product}/images","host":["{{baseUrl}}"],"path":["api","products",":product","images"],"variable":[{"key":"product","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"url\": \"https://cdn.example.com/products/blue-widget.jpg\",\n      \"name\": \"Main Image\",\n      \"sort_order\": 1,\n      \"is_primary\": true,\n      \"resolvable\": true\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Add Product Image","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/{product}/images","host":["{{baseUrl}}"],"path":["api","products",":product","images"],"variable":[{"key":"product","value":"1","description":"Product ID"}]},"description":"Add a new image to a product. The `image` field accepts EITHER an uploaded file (multipart/form-data) OR a URL / base64 string (application/json).\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Mode 1 — File upload (multipart/form-data):**\n- image (required, file): image file uploaded from the device. Allowed types: jpg, jpeg, png, gif, webp. Max size: 10 MB (10240 KB).\n\n**Mode 2 — URL or base64 string (application/json):**\n- image (required, string): image URL or base64 encoded data URI.\n\n**Common optional fields (both modes):**\n- name (optional, max 255): image display name\n- sort_order (optional, integer): display order\n- is_primary (optional, boolean): set as the primary product image\n- download (optional, boolean): URL mode only — if true, downloads and stores the image from the URL\n- swatch_variant_attribute (optional, max 255): marks this image as the variant swatch for the named variant attribute (e.g. \"color\"). Sales channels that support swatches render it as the variant's swatch image. Omit or send null for a normal gallery image.","body":{"mode":"raw","raw":"{\n  \"image\": \"https://example.com/images/widget-red.jpg\",\n  \"name\": \"Red Variant Image\",\n  \"sort_order\": 2,\n  \"is_primary\": false,\n  \"swatch_variant_attribute\": \"color\",\n  \"download\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/products/{product}/images","host":["{{baseUrl}}"],"path":["api","products",":product","images"],"variable":[{"key":"product","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"url\": \"https://cdn.example.com/products/widget-red.jpg\",\n    \"name\": \"Red Variant Image\",\n    \"sort_order\": 2,\n    \"is_primary\": false,\n    \"resolvable\": true\n  },\n  \"message\": \"Product image created successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Update/Create Product Images","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/{product}/bulk-images","host":["{{baseUrl}}"],"path":["api","products",":product","bulk-images"],"variable":[{"key":"product","value":"1","description":"Product ID"}]},"description":"Bulk update or create multiple images for a product at once.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBy default this endpoint SYNCS: any image already on the product that is not in the request body is deleted. To ADD images without touching the existing gallery, send `operation: \"updateOrCreate\"` on each row; send `operation: \"delete\"` with an `id` to remove a single image.\n\nFields per image:\n- `id` (integer, optional) - an existing image on this product; required for `delete`\n- `url` (string, required unless `id` is given) - the image URL\n- `name` (string, optional, max 255)\n- `sort_order` (integer, optional) - gallery position\n- `download` (boolean, optional) - fetch and re-host the image instead of hot-linking it\n- `is_primary` (boolean, optional) - the product's main image\n- `operation` (string, optional) - `updateOrCreate` or `delete`. Omit for the sync behaviour above.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"images\": [\n    {\n      \"url\": \"https://example.com/images/main.jpg\",\n      \"name\": \"Main Image\",\n      \"sort_order\": 0,\n      \"is_primary\": true,\n      \"download\": false,\n      \"operation\": \"updateOrCreate\"\n    },\n    {\n      \"url\": \"https://example.com/images/angle2.jpg\",\n      \"name\": \"Side View\",\n      \"sort_order\": 1,\n      \"is_primary\": false,\n      \"download\": false,\n      \"operation\": \"updateOrCreate\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/products/{product}/bulk-images","host":["{{baseUrl}}"],"path":["api","products",":product","bulk-images"],"variable":[{"key":"product","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"url\": \"https://cdn.example.com/products/main.jpg\",\n      \"is_primary\": true\n    },\n    {\n      \"id\": 2,\n      \"url\": \"https://cdn.example.com/products/angle2.jpg\",\n      \"is_primary\": false\n    }\n  ],\n  \"message\": \"Product images updated\",\n  \"status\": \"success\"\n}"}]},{"name":"List Broken Product Images","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/product-images/broken","host":["{{baseUrl}}"],"path":["api","product-images","broken"]},"description":"List product images the on-demand image health check confirmed broken — rows where `image_checked_at` is set AND `resolvable` is false. Newest-checked first, with primary images surfaced to the top. Each row is a single image with its parent product for cross-linking.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nQuery parameters:\n- `page` — page number (default 1)\n- `per_page` — items per page (default 25)\n\nBacks the Broken Product Images report, where each image is re-loaded live in the browser; images that load are self-healed via `POST /api/product-images/mark-resolvable`, and genuinely dead ones are removed via `POST /api/product-images/bulk-delete`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/product-images/broken","host":["{{baseUrl}}"],"path":["api","product-images","broken"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 4821,\n      \"url\": \"https://images.example.com/widgets/blue-widget.jpg\",\n      \"is_primary\": true,\n      \"is_external\": true,\n      \"image_checked_at\": \"2026-06-09T18:30:00+00:00\",\n      \"product\": {\n        \"id\": 1,\n        \"sku\": \"SKU-001\",\n        \"name\": \"Blue Widget\",\n        \"reference\": \"SKU-001\"\n      },\n      \"product_link\": \"/products/1\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 25,\n  \"total\": 56,\n  \"from\": 1,\n  \"to\": 25,\n  \"path\": \"{{protocol}}{{domain}}/api/product-images/broken\"\n}"}]},{"name":"Bulk Delete Product Images","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-images/bulk-delete","host":["{{baseUrl}}"],"path":["api","product-images","bulk-delete"]},"description":"Delete the selected product images. Deletion runs through the model so its side effects fire: if a deleted image was a product's primary image, that product is left without a primary image, and affected packing slips are re-queued.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- `image_ids` (`array<int>`, required) — 1–500 existing product_image ids.\n\nReturns 422 if `image_ids` is empty, exceeds 500, or contains an id that does not exist.","body":{"mode":"raw","raw":"{\n  \"image_ids\": [\n    4821,\n    4822,\n    4830\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-images/bulk-delete","host":["{{baseUrl}}"],"path":["api","product-images","bulk-delete"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"deleted\": 3\n  },\n  \"message\": \"Deleted 3 images.\"\n}"}]},{"name":"Mark Product Images Resolvable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-images/mark-resolvable","host":["{{baseUrl}}"],"path":["api","product-images","mark-resolvable"]},"description":"Clear the broken flag on the selected images — the report's in-browser load proved they resolve, so the server's false positive is healed. Uses a mass update, so ProductImage save side effects (primary reset, packing-slip re-queue) do NOT fire and `image_checked_at` is preserved.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- `image_ids` (`array<int>`, required) — 1–500 existing product_image ids.\n\nReturns 422 if `image_ids` is empty, exceeds 500, or contains an id that does not exist.","body":{"mode":"raw","raw":"{\n  \"image_ids\": [\n    4821,\n    4822,\n    4830\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-images/mark-resolvable","host":["{{baseUrl}}"],"path":["api","product-images","mark-resolvable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"updated\": 3\n  },\n  \"message\": \"Marked 3 images as OK.\"\n}"}]},{"name":"Update Product Image","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-images/{productImage}","host":["{{baseUrl}}"],"path":["api","product-images",":productImage"],"variable":[{"key":"productImage","value":"315","description":"Product image ID"}]},"description":"Updates a product image's metadata and optionally replaces the image itself.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody fields (all optional on update):\n- image (string) — a new image as an external URL or a base64 data URI (data:image/...;base64,...). Omit to keep the current image and only update metadata.\n- name (string, max 255) — display name for the image\n- sort_order (integer) — position within the product's image list\n- is_primary (boolean) — mark as the product's primary image\n- download (boolean) — when true and image is an external URL, the file is downloaded and stored on SKU.io storage instead of hot-linking the URL\n\nIf an external image URL cannot be resolved, the update still succeeds but the response includes a warning and the image is stored with resolvable: false.\n\nAuthentication: Requires Bearer token.\n- swatch_variant_attribute (optional, max 255): marks this image as the variant swatch for the named variant attribute (e.g. \"color\"). Sales channels that support swatches render it as the variant's swatch image. Omit or send null for a normal gallery image.","body":{"mode":"raw","raw":"{\n  \"image\": \"https://cdn.example.com/images/widget-blue-front.jpg\",\n  \"name\": \"Front view\",\n  \"sort_order\": 1,\n  \"is_primary\": true,\n  \"swatch_variant_attribute\": \"color\",\n  \"download\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/product-images/{productImage}","host":["{{baseUrl}}"],"path":["api","product-images",":productImage"],"variable":[{"key":"productImage","value":"315","description":"Product image ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"The product image '315' updated successfully\",\n  \"data\": {\n    \"id\": 315,\n    \"product_id\": 42,\n    \"url\": \"https://example.sku.io/storage/images/widget-blue-front.jpg\",\n    \"name\": \"Front view\",\n    \"sort_order\": 1,\n    \"is_primary\": true,\n    \"resolvable\": true,\n    \"image_checked_at\": \"2026-07-01T08:30:00.000000Z\",\n    \"created_at\": \"2025-11-02T09:14:33.000000Z\",\n    \"updated_at\": \"2026-07-06T10:05:12.000000Z\"\n  }\n}"}]},{"name":"Delete Product Image","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/product-images/{productImage}","host":["{{baseUrl}}"],"path":["api","product-images",":productImage"],"variable":[{"key":"productImage","value":"315","description":"Product image ID"}]},"description":"Deletes a product image. Destructive: if the image file is hosted on SKU.io storage, the stored file (and its thumbnail) is removed from the server as well; externally hosted URLs are simply detached from the product.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/product-images/{productImage}","host":["{{baseUrl}}"],"path":["api","product-images",":productImage"],"variable":[{"key":"productImage","value":"315","description":"Product image ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"The product image 315 deleted successfully\"\n}"}]},{"name":"Upload Image","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/store-image","host":["{{baseUrl}}"],"path":["api","store-image"]},"description":"Uploads an image to SKU.io storage and returns its hosted URL. Use the returned URL anywhere an image URL is accepted — for example when creating or updating product images, or for store branding assets.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe image field accepts three input forms:\n- a multipart file upload (shown here)\n- an external image URL (the file is downloaded and stored)\n- a base64 data URI string (data:image/png;base64,...)\n\nReturns 400 when the image cannot be resolved (e.g. an unreachable URL or invalid data).\n\nAuthentication: Requires Bearer token.","body":{"mode":"formdata","formdata":[{"key":"image","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/store-image","host":["{{baseUrl}}"],"path":["api","store-image"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"The image uploaded successfully on the server\",\n  \"data\": {\n    \"url\": \"https://example.sku.io/storage/tenant-assets/images/9f2c1a7b3d.jpg\"\n  }\n}"}]},{"name":"Delete Uploaded Image","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/delete-image","host":["{{baseUrl}}"],"path":["api","delete-image"]},"description":"Removes a previously uploaded image (and its thumbnail) from SKU.io storage.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody fields:\n- url (required, string) — the hosted URL (or storage path) returned by Upload Image\n\nDestructive: the stored file is permanently deleted. Returns 400 when the file cannot be found at the given path.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/delete-image","host":["{{baseUrl}}"],"path":["api","delete-image"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"The image removed successfully from the server\"\n}"}]},{"name":"Create Blemished Product","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/blemished","host":["{{baseUrl}}"],"path":["api","products","blemished"]},"description":"Create a blemished (damaged/used) product derived from an existing standard product.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody fields:\n- blemished_sku (required, unique): SKU for the new blemished product\n- derived_from_product_id (required): ID of the original standard product\n- product_condition_id (optional): ID of a product condition grade (from List Product Conditions)\n- condition_notes (optional): free-text note describing the blemish\n- reference (optional): reference number or note\n- adjust_inventory (optional, boolean, default false): create an inventory adjustment\n- adjust_inventory_for_original_product (optional, boolean): also adjust the original product\n- adjustment_type (optional, default 'increase'): increase or decrease\n- adjustment_data (optional): adjustment details when adjust_inventory is true","body":{"mode":"raw","raw":"{\n  \"blemished_sku\": \"SKU-001-B\",\n  \"derived_from_product_id\": 1,\n  \"product_condition_id\": 3,\n  \"condition_notes\": \"Minor scratches on casing\",\n  \"reference\": \"REF-2024-001\",\n  \"adjust_inventory\": true,\n  \"adjust_inventory_for_original_product\": false,\n  \"adjustment_type\": \"increase\",\n  \"adjustment_data\": {\n    \"warehouse_id\": 1,\n    \"quantity\": 10\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/products/blemished","host":["{{baseUrl}}"],"path":["api","products","blemished"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 50,\n    \"sku\": \"SKU-001-B\",\n    \"name\": \"Blue Widget (Blemished)\",\n    \"type\": \"standard\",\n    \"blemished\": {\n      \"derived_product\": {\n        \"id\": 1,\n        \"name\": \"Blue Widget\",\n        \"sku\": \"SKU-001\"\n      },\n      \"condition\": \"Grade B - Minor scratches\",\n      \"reference\": \"REF-2024-001\"\n    }\n  },\n  \"message\": \"Product created successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Generate Blemished SKU Pattern","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/generate-sku-pattern/{product}","host":["{{baseUrl}}"],"path":["api","products","generate-sku-pattern",":product"],"variable":[{"key":"product","value":"1","description":"Product ID to generate blemished SKU pattern for"}]},"description":"Generate a blemished SKU based on the tenant's blemished SKU pattern settings and the original product's SKU.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/generate-sku-pattern/{product}","host":["{{baseUrl}}"],"path":["api","products","generate-sku-pattern",":product"],"variable":[{"key":"product","value":"1","description":"Product ID to generate blemished SKU pattern for"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"sku\": \"SKU-001-BLEM\",\n    \"pattern\": \"{sku}-BLEM\",\n    \"pattern_enabled\": true\n  },\n  \"status\": \"success\"\n}"}]},{"name":"List Product Conditions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/conditions","host":["{{baseUrl}}"],"path":["api","products","conditions"]},"description":"List the user-configurable product condition grades (e.g. 'Like New', 'Good', 'For Parts'), ordered by sort order then name. These grades can be assigned to a blemished product and mapped to a channel's condition field.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/conditions","host":["{{baseUrl}}"],"path":["api","products","conditions"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Like New\",\n      \"sort_order\": 1\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Good\",\n      \"sort_order\": 2\n    },\n    {\n      \"id\": 3,\n      \"name\": \"For Parts\",\n      \"sort_order\": 3\n    }\n  ]\n}"}]},{"name":"Create Product Condition","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/conditions","host":["{{baseUrl}}"],"path":["api","products","conditions"]},"description":"Create a product condition grade on the fly (the add-on-the-fly combo box seam). Idempotent by name: submitting an existing name returns that grade (HTTP 200) instead of creating a duplicate.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequires the products.update permission.\n\nAuthentication: Requires Bearer token.\n\nBody fields:\n- name (required, string, max 255): the grade name","body":{"mode":"raw","raw":"{\n  \"name\": \"Refurbished\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/products/conditions","host":["{{baseUrl}}"],"path":["api","products","conditions"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 4,\n    \"name\": \"Refurbished\",\n    \"sort_order\": null\n  }\n}"}]},{"name":"Import Products","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/import","host":["{{baseUrl}}"],"path":["api","products","import"]},"description":"Execute the product import — creates products from the validated data. Should be called after validate.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody fields:\n- products (required, array, min 1): array of validated product objects to import\n\nReturns 422 if the import fails.\n\n**Requires permission:** `products.import`\n\nRows may include `product_type` — the product type's full path (e.g. `Diapering > Cloth diapers`), or just its leaf name when that name is unique among leaf types. A value that matches no type fails the row's validation. The import template includes this column.","body":{"mode":"raw","raw":"{\n  \"products\": [\n    {\n      \"sku\": \"SKU-NEW-001\",\n      \"name\": \"New Product\",\n      \"type\": \"standard\",\n      \"product_type\": \"Diapering > Cloth diapers\",\n      \"barcode\": \"1234567890123\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/products/import","host":["{{baseUrl}}"],"path":["api","products","import"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"imported\": 1,\n  \"failed\": 0,\n  \"errors\": []\n}"}]},{"name":"Preview Import (Legacy CSV)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/import/preview","host":["{{baseUrl}}"],"path":["api","products","import","preview"]},"description":"Legacy endpoint for CSV import preview (delegates to ImportsData trait).\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `products.import`","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/products/import/preview","host":["{{baseUrl}}"],"path":["api","products","import","preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {},\n  \"status\": \"success\"\n}"}]},{"name":"Get Import Fields","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/import/fields","host":["{{baseUrl}}"],"path":["api","products","import","fields"]},"description":"Returns dynamic import fields including custom attributes for the column mapping UI.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/import/fields","host":["{{baseUrl}}"],"path":["api","products","import","fields"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"attributes\": [\n      {\n        \"key\": \"attribute_5\",\n        \"label\": \"Attr: Color\"\n      },\n      {\n        \"key\": \"attribute_6\",\n        \"label\": \"Attr: Size\"\n      }\n    ]\n  }\n}"}]},{"name":"Download Import Template","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/import/template","host":["{{baseUrl}}"],"path":["api","products","import","template"]},"description":"Download an XLSX import template with headers, sample data, field guide, and reference data from the tenant's account.\n\nAuthentication: Requires Bearer token.\n\nResponse: XLSX file download (application/vnd.openxmlformats-officedocument.spreadsheetml.sheet)\n\nThe template includes a **Product Type** column: the type's full path, e.g. `Diapering > Cloth diapers`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/import/template","host":["{{baseUrl}}"],"path":["api","products","import","template"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary XLSX file)"}]},{"name":"Validate Import","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/import/validate","host":["{{baseUrl}}"],"path":["api","products","import","validate"]},"description":"Validate mapped import data and return a preview. No data is created — safe to call repeatedly for client-side validation before executing the actual import.\n\nAuthentication: Requires Bearer token.\n\nBody fields:\n- rows (required, array, min 1): array of product row objects to validate\n\nRows may include `product_type` — the product type's full path (e.g. `Diapering > Cloth diapers`), or just its leaf name when that name is unique among leaf types. A value that matches no type fails the row's validation. The import template includes this column.","body":{"mode":"raw","raw":"{\n  \"rows\": [\n    {\n      \"sku\": \"SKU-NEW-001\",\n      \"name\": \"New Product\",\n      \"type\": \"standard\",\n      \"product_type\": \"Diapering > Cloth diapers\",\n      \"barcode\": \"1234567890123\",\n      \"brand\": \"Acme Co\",\n      \"weight\": 0.5,\n      \"weight_unit\": \"kg\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/products/import/validate","host":["{{baseUrl}}"],"path":["api","products","import","validate"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"valid\": [\n    {\n      \"row\": 1,\n      \"sku\": \"SKU-NEW-001\",\n      \"status\": \"valid\"\n    }\n  ],\n  \"invalid\": [],\n  \"total\": 1,\n  \"valid_count\": 1,\n  \"invalid_count\": 0\n}"}]},{"name":"Download Merge Template","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/merge/template","host":["{{baseUrl}}"],"path":["api","products","merge","template"]},"description":"Download a CSV template for bulk product merging. Contains columns: old_sku, destination_sku.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nResponse: CSV file download."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/merge/template","host":["{{baseUrl}}"],"path":["api","products","merge","template"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"old_sku,destination_sku\nEXAMPLE-OLD-SKU,EXAMPLE-NEW-SKU"}]},{"name":"Upload Merge CSV","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/products/merge/upload","host":["{{baseUrl}}"],"path":["api","products","merge","upload"]},"description":"Upload a CSV file with product merge pairs. Validates the SKU pairs and creates a session (cached for 2 hours) for preview and execution.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data\n\nBody fields:\n- file (required): CSV or TXT file with columns old_sku and destination_sku\n\nRows that cannot be merged — a source or destination SKU that no longer exists, a self-merge, or a duplicate row — are skipped rather than rejecting the whole file. The response `skipped` object summarises them (total count, a breakdown by reason, and a sample of skipped rows). The upload fails with 422 only when no row is mergeable.","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/products/merge/upload","host":["{{baseUrl}}"],"path":["api","products","merge","upload"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"session_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"pairs_count\": 5,\n  \"skipped\": {\n    \"count\": 2,\n    \"by_reason\": {\n      \"source_not_found\": 1,\n      \"duplicate_pair\": 1\n    },\n    \"sample\": [\n      {\n        \"line\": 4,\n        \"old_sku\": \"SKU-OLD-004\",\n        \"destination_sku\": \"SKU-NEW-004\",\n        \"reason\": \"source_not_found\"\n      },\n      {\n        \"line\": 7,\n        \"old_sku\": \"SKU-OLD-007\",\n        \"destination_sku\": \"SKU-NEW-007\",\n        \"reason\": \"duplicate_pair\"\n      }\n    ]\n  }\n}"}]},{"name":"Create Merge Session (Single Pair)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/merge/create-session","host":["{{baseUrl}}"],"path":["api","products","merge","create-session"]},"description":"Create a merge session for a single SKU pair without uploading a CSV. Validates that both SKUs exist and are compatible.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody fields:\n- old_sku (required, string): source SKU to merge from\n- destination_sku (required, string): destination SKU to merge into\n\nReturns 422 if SKUs are the same, either SKU doesn't exist, or types are incompatible.","body":{"mode":"raw","raw":"{\n  \"old_sku\": \"SKU-OLD-001\",\n  \"destination_sku\": \"SKU-NEW-001\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/products/merge/create-session","host":["{{baseUrl}}"],"path":["api","products","merge","create-session"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"session_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"pairs_count\": 1\n}"}]},{"name":"Get Merge Preview","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/merge/{session_id}/preview","host":["{{baseUrl}}"],"path":["api","products","merge",":session_id","preview"],"variable":[{"key":"session_id","value":"550e8400-e29b-41d4-a716-446655440000","description":"Session ID from upload or create-session"}]},"description":"Get a preview of what will happen when the merge is executed. Shows data changes for each SKU pair.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nSessions expire after 2 hours. Returns 404 if the session is not found or expired.\n\nThe preview is capped at the first 100 pairs to keep the response fast on large lists. `total_count` is the full number of pairs, `previewed_count` is how many are included, and `truncated` is true when the list was capped. Every pair is still merged when the merge is executed."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/merge/{session_id}/preview","host":["{{baseUrl}}"],"path":["api","products","merge",":session_id","preview"],"variable":[{"key":"session_id","value":"550e8400-e29b-41d4-a716-446655440000","description":"Session ID from upload or create-session"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"previews\": [\n    {\n      \"old_sku\": \"SKU-OLD-001\",\n      \"destination_sku\": \"SKU-NEW-001\",\n      \"changes\": {\n        \"sales_order_lines\": 3,\n        \"purchase_order_lines\": 1\n      }\n    }\n  ],\n  \"previewed_count\": 1,\n  \"total_count\": 1,\n  \"truncated\": false\n}"}]},{"name":"Execute Merge","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/merge/{session_id}/execute","host":["{{baseUrl}}"],"path":["api","products","merge",":session_id","execute"],"variable":[{"key":"session_id","value":"550e8400-e29b-41d4-a716-446655440000","description":"Session ID from upload or create-session"}]},"description":"Execute the product merge for all pairs in the session. Merges sales order lines, purchase order lines, and other references from the old SKU to the destination SKU. Clears the session cache after execution.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nSessions expire after 2 hours. Returns 404 if session is not found or expired.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/products/merge/{session_id}/execute","host":["{{baseUrl}}"],"path":["api","products","merge",":session_id","execute"],"variable":[{"key":"session_id","value":"550e8400-e29b-41d4-a716-446655440000","description":"Session ID from upload or create-session"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"merged_count\": 5,\n  \"errors\": []\n}"}]},{"name":"List Product Listings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{product}/listings?page=1&limit=10&archived=0&included[]=&excluded[]=","host":["{{baseUrl}}"],"path":["api","products",":product","listings"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"limit","value":"10","description":"Items per page","disabled":true},{"key":"archived","value":"0","description":"0 = active, 1 = archived, 2 = all","disabled":true},{"key":"included[]","value":"","description":"Columns to include","disabled":true},{"key":"excluded[]","value":"","description":"Columns to exclude","disabled":true}],"variable":[{"key":"product","value":"1","description":"Product ID"}]},"description":"Get paginated product listings (channel listings) for a specific product. Refreshes inventory cache for all listings before returning.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{product}/listings?page=1&limit=10&archived=0&included[]=&excluded[]=","host":["{{baseUrl}}"],"path":["api","products",":product","listings"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"limit","value":"10","description":"Items per page","disabled":true},{"key":"archived","value":"0","description":"0 = active, 1 = archived, 2 = all","disabled":true},{"key":"included[]","value":"","description":"Columns to include","disabled":true},{"key":"excluded[]","value":"","description":"Columns to exclude","disabled":true}],"variable":[{"key":"product","value":"1","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 20,\n      \"product_id\": 1,\n      \"sales_channel_listing_id\": \"amazon-asin-B001\",\n      \"sales_channel\": {\n        \"id\": 3,\n        \"name\": \"Amazon US\"\n      },\n      \"master_of_price\": true,\n      \"is_fba\": false\n    }\n  ],\n  \"meta\": {\n    \"current_page\": 1,\n    \"per_page\": 10,\n    \"total\": 3\n  }\n}"}]},{"name":"Set Product Revenue Mapping","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/{product}/revenue-mapping","host":["{{baseUrl}}"],"path":["api","products",":product","revenue-mapping"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Applies (or clears) a revenue-line mapping across every sales-channel listing of this product.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nWhen a listing carries a revenue mapping, future channel orders containing it create a revenue financial line on the order instead of a stock-consuming sales order line. Use this endpoint to apply that behaviour to a product everywhere it is sold; use the per-channel revenue-mapping endpoint to map a single listing.\n\nBody fields:\n- financial_line_type_id (required, nullable integer) — ID of an existing financial line type. It must have a revenue classification; non-revenue types are rejected with a 422. Pass null to clear the mapping from every listing of this product.\n\nReturns the number of listings updated.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"financial_line_type_id\": 12\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/products/{product}/revenue-mapping","host":["{{baseUrl}}"],"path":["api","products",":product","revenue-mapping"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"listings_updated\": 3\n  },\n  \"message\": \"Product SKU-1042 has been updated\"\n}"}]},{"name":"Update Product Default Financial Line Type","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/{product}/default-financial-line-type","host":["{{baseUrl}}"],"path":["api","products",":product","default-financial-line-type"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Sets or clears the default financial line type used when this product is added to financial documents.\n\nBody fields:\n- default_financial_line_type_id (nullable integer) — ID of an existing financial line type. It must have a revenue classification; non-revenue types are rejected with a 422. Pass null to clear the default.\n\nReturns the updated product including the resolved default_financial_line_type object (id, name, classification).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"default_financial_line_type_id\": 12\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/products/{product}/default-financial-line-type","host":["{{baseUrl}}"],"path":["api","products",":product","default-financial-line-type"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Product WIDGET-BLUE updated successfully\",\n  \"data\": {\n    \"id\": 42,\n    \"sku\": \"WIDGET-BLUE\",\n    \"name\": \"Blue Widget\",\n    \"type\": \"standard\",\n    \"barcode\": 123456789012,\n    \"default_financial_line_type_id\": 12,\n    \"default_financial_line_type\": {\n      \"id\": 12,\n      \"name\": \"Product Sales\",\n      \"classification\": \"revenue\"\n    },\n    \"sales_nominal_code_id\": 5,\n    \"is_taxable\": true,\n    \"archived_at\": null,\n    \"created_at\": \"2025-11-02T09:14:33.000000Z\",\n    \"updated_at\": \"2026-07-06T10:05:12.000000Z\"\n  }\n}"}]},{"name":"List Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products?page=1&per_page=10&sort=-created_at&filter[search]=widget&filter[type]=standard&filter[archived]=0&filter[brand_id]=1&filter[default_supplier_id]=2&filter[stock_status]=in_stock&filter[has_backorder]=false&filter[has_fba]=false&filter[ids]=1,2,3","host":["{{baseUrl}}"],"path":["api","v2","products"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default: 10)","disabled":true},{"key":"sort","value":"-created_at","description":"Sort field. Prefix with - for descending. Allowed: id, sku, name, type, barcode, mpn, hts_code, country_of_origin, unit_cost, average_cost, supplier_price, weight, length, width, height, case_quantity, case_weight, daily_average_consumption, daily_average_consumption_net_returns, min_stock_level, total_quantity_sold, total_orders, archived_at, created_at, updated_at","disabled":true},{"key":"filter[search]","value":"widget","description":"Full-text search across ID (exact), SKU, name, barcode, MPN, brand name, and product type path","disabled":true},{"key":"filter[type]","value":"standard","description":"Exact product type: standard, bundle, kit, blemished, matrix","disabled":true},{"key":"filter[archived]","value":"0","description":"Archived state: 0 or omit for active only, 'only' for archived only, 'all' for all","disabled":true},{"key":"filter[brand_id]","value":"1","description":"Filter by brand ID(s), comma-separated","disabled":true},{"key":"filter[default_supplier_id]","value":"2","description":"Filter by default supplier ID(s), comma-separated","disabled":true},{"key":"filter[stock_status]","value":"in_stock","description":"Stock status: in_stock, out_of_stock","disabled":true},{"key":"filter[has_backorder]","value":"false","description":"Filter by backorder presence: true or false","disabled":true},{"key":"filter[has_fba]","value":"false","description":"Filter by FBA listing presence: true or false","disabled":true},{"key":"filter[ids]","value":"1,2,3","description":"Comma-separated list of product IDs to include","disabled":true}]},"description":"Get a paginated list of products using Spatie QueryBuilder.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nSupports both simple query filters (filter[field]=value) and advanced grouped filters (filter_groups tree). Default: shows non-archived products sorted by -created_at.\n\nAllowed sorts: id, sku, name, type, barcode, mpn, hts_code, country_of_origin, unit_cost, average_cost, supplier_price, weight, length, width, height, case_quantity, case_weight, daily_average_consumption, daily_average_consumption_net_returns, min_stock_level, total_quantity_sold, total_orders, archived_at, created_at, updated_at\n\nDefault pagination: 10 per page.\n\nThe response also includes `unfiltered_total`, the count of rows in the base scope before any user filters are applied — used by the frontend to show export totals.\n\n**Stock UoM filters (added 2026-05-30):**\n- `filter[stock_uom_id]` — by uom_id (exact, comma-separated)\n- `filter[stock_uom_code]` — by UoM code (advanced text ops)\n- `filter[stock_uom_name]` — by UoM display name (advanced text ops)\n\nThe response includes `inventory_non_sellable` — physically-present stock in non-sellable bins. It is excluded from `inventory_available` but is part of On Hand (On Hand = Available + Allocated + On Hold + Non-Sellable).\n\nAlso supports `hts_code` (Harmonized Tariff Schedule code) and `country_of_origin` (ISO 3166-1 alpha-2 country of manufacture) for filtering and sorting.\n\n**Sparse fieldsets (`included`):**\nPass `included` a JSON array of response field names to receive only those fields. The server then loads only the data those fields need instead of the full product payload, which makes narrow lookups — resolving a barcode to a product, for example — markedly cheaper. `id` is always returned. Omitting the parameter returns the complete payload, unchanged.\n\nExample: `included=[\"id\",\"sku\",\"barcode\",\"inventory_available\"]`\n\n**Product type:** every product carries at most one leaf product type (Organization → Product Types). Rows include `product_type_id`, `product_type_name`, `product_type_path` and a `product_type` object (`id`, `name`, `path`, `archived_at`; null when the product has no type). Filter with `filter[product_type_id]` — ids, comma-separated; a parent type includes all of its descendants; `none` selects untyped products and can be combined, e.g. `7,none` — or with operator filters on `product_type_name` / `product_type_path` (e.g. `filter[product_type_path][starts_with]=Diapering`). `filter[search]` also matches the type path.\n\n**Bundle figures:** for products of type `bundle` (which hold no stock of their own), `bundle_sets_possible` is the number of complete sets the component stock can make, counted per warehouse and summed (in each warehouse, the lowest of each inventory-carrying component's available quantity divided by its quantity per set, never below 0; pieces in different warehouses do not make a set; nested kits are expanded to their components), and `bundle_component_avg_cost` is the sum of each component's average cost × quantity per set (nested bundles roll up; `null` when any component has no average cost). Both are `null` for non-bundle products and are not sortable or filterable."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products?page=1&per_page=10&sort=-created_at&filter[search]=widget&filter[type]=standard&filter[archived]=0&filter[brand_id]=1&filter[default_supplier_id]=2&filter[stock_status]=in_stock&filter[has_backorder]=false&filter[has_fba]=false&filter[ids]=1,2,3","host":["{{baseUrl}}"],"path":["api","v2","products"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"10","description":"Items per page (default: 10)","disabled":true},{"key":"sort","value":"-created_at","description":"Sort field. Prefix with - for descending. Allowed: id, sku, name, type, barcode, mpn, hts_code, country_of_origin, unit_cost, average_cost, supplier_price, weight, length, width, height, case_quantity, case_weight, daily_average_consumption, daily_average_consumption_net_returns, min_stock_level, total_quantity_sold, total_orders, archived_at, created_at, updated_at","disabled":true},{"key":"filter[search]","value":"widget","description":"Full-text search across ID (exact), SKU, name, barcode, MPN, brand name, and product type path","disabled":true},{"key":"filter[type]","value":"standard","description":"Exact product type: standard, bundle, kit, blemished, matrix","disabled":true},{"key":"filter[archived]","value":"0","description":"Archived state: 0 or omit for active only, 'only' for archived only, 'all' for all","disabled":true},{"key":"filter[brand_id]","value":"1","description":"Filter by brand ID(s), comma-separated","disabled":true},{"key":"filter[default_supplier_id]","value":"2","description":"Filter by default supplier ID(s), comma-separated","disabled":true},{"key":"filter[stock_status]","value":"in_stock","description":"Stock status: in_stock, out_of_stock","disabled":true},{"key":"filter[has_backorder]","value":"false","description":"Filter by backorder presence: true or false","disabled":true},{"key":"filter[has_fba]","value":"false","description":"Filter by FBA listing presence: true or false","disabled":true},{"key":"filter[ids]","value":"1,2,3","description":"Comma-separated list of product IDs to include","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 42,\n      \"sku\": \"WDG-001\",\n      \"name\": \"Blue Widget\",\n      \"type\": \"standard\",\n      \"barcode\": 123456789012,\n      \"mpn\": \"BW-001\",\n      \"hts_code\": \"8471.30.0100\",\n      \"country_of_origin\": \"US\",\n      \"density_g_per_ml\": null,\n      \"density_source\": null,\n      \"density_basis\": null,\n      \"unit_cost\": 5.5,\n      \"average_cost\": 5.75,\n      \"is_variation\": false,\n      \"is_dropshippable\": false,\n      \"is_lot_tracked\": false,\n      \"lot_tracking_method\": null,\n      \"shelf_life_days\": null,\n      \"near_expiry_threshold_days\": null,\n      \"min_remaining_shelf_life_days\": null,\n      \"expiry_aware_availability\": false,\n      \"archived_at\": null,\n      \"stock_uom_id\": 1,\n      \"stock_uom_code\": \"EACH\",\n      \"stock_uom_name\": \"Each\",\n      \"image_url\": \"https://example.com/images/widget.jpg\",\n      \"brand_id\": 1,\n      \"brand_name\": \"Acme\",\n      \"product_type_id\": 7,\n      \"product_type_name\": \"Cloth diapers\",\n      \"product_type_path\": \"Diapering > Cloth diapers\",\n      \"product_type\": {\n        \"id\": 7,\n        \"name\": \"Cloth diapers\",\n        \"path\": \"Diapering > Cloth diapers\",\n        \"archived_at\": null\n      },\n      \"default_supplier_id\": 3,\n      \"default_supplier_name\": \"Global Supplies Ltd\",\n      \"default_supplier_sku\": \"GS-WDG-001\",\n      \"supplier_count\": 2,\n      \"inventory_total\": 500,\n      \"inventory_available\": 450,\n      \"inventory_available_to_sell\": 450,\n      \"inventory_allocated\": 50,\n      \"inventory_backorder\": 0,\n      \"inventory_on_hold\": 0,\n      \"inventory_non_sellable\": 0,\n      \"inventory_in_transit\": 100,\n      \"inventory_consigned\": 50,\n      \"inventory_incoming\": 200,\n      \"inventory_stock_value\": 2875,\n      \"inventory_avg_cost\": 5.75,\n      \"daily_average_consumption\": 5.2,\n      \"min_stock_level\": 100,\n      \"weight\": 0.5,\n      \"weight_unit\": \"kg\",\n      \"created_at\": \"2024-01-15T10:30:00.000000Z\",\n      \"updated_at\": \"2024-06-01T14:20:00.000000Z\",\n      \"total_quantity_sold\": 1280,\n      \"total_orders\": 312,\n      \"bundle_sets_possible\": null,\n      \"bundle_component_avg_cost\": null\n    },\n    {\n      \"id\": 90,\n      \"sku\": \"T-DIN02-25-OAK(SET)\",\n      \"name\": \"Dining Table 2.5m Oak (Set)\",\n      \"type\": \"bundle\",\n      \"barcode\": 123456789012,\n      \"mpn\": \"BW-001\",\n      \"hts_code\": \"8471.30.0100\",\n      \"country_of_origin\": \"US\",\n      \"density_g_per_ml\": null,\n      \"density_source\": null,\n      \"density_basis\": null,\n      \"unit_cost\": 5.5,\n      \"average_cost\": null,\n      \"is_variation\": false,\n      \"is_dropshippable\": false,\n      \"is_lot_tracked\": false,\n      \"lot_tracking_method\": null,\n      \"shelf_life_days\": null,\n      \"near_expiry_threshold_days\": null,\n      \"min_remaining_shelf_life_days\": null,\n      \"expiry_aware_availability\": false,\n      \"archived_at\": null,\n      \"stock_uom_id\": 1,\n      \"stock_uom_code\": \"EACH\",\n      \"stock_uom_name\": \"Each\",\n      \"image_url\": \"https://example.com/images/widget.jpg\",\n      \"brand_id\": 1,\n      \"brand_name\": \"Acme\",\n      \"product_type_id\": 7,\n      \"product_type_name\": \"Cloth diapers\",\n      \"product_type_path\": \"Diapering > Cloth diapers\",\n      \"product_type\": {\n        \"id\": 7,\n        \"name\": \"Cloth diapers\",\n        \"path\": \"Diapering > Cloth diapers\",\n        \"archived_at\": null\n      },\n      \"default_supplier_id\": 3,\n      \"default_supplier_name\": \"Global Supplies Ltd\",\n      \"default_supplier_sku\": \"GS-WDG-001\",\n      \"supplier_count\": 2,\n      \"inventory_total\": null,\n      \"inventory_available\": null,\n      \"inventory_available_to_sell\": null,\n      \"inventory_allocated\": null,\n      \"inventory_backorder\": null,\n      \"inventory_on_hold\": null,\n      \"inventory_non_sellable\": null,\n      \"inventory_in_transit\": null,\n      \"inventory_consigned\": null,\n      \"inventory_incoming\": 200,\n      \"inventory_stock_value\": null,\n      \"inventory_avg_cost\": null,\n      \"daily_average_consumption\": 5.2,\n      \"min_stock_level\": 100,\n      \"weight\": 0.5,\n      \"weight_unit\": \"kg\",\n      \"created_at\": \"2024-01-15T10:30:00.000000Z\",\n      \"updated_at\": \"2024-06-01T14:20:00.000000Z\",\n      \"total_quantity_sold\": 1280,\n      \"total_orders\": 312,\n      \"bundle_sets_possible\": 14,\n      \"bundle_component_avg_cost\": 3013.9338\n    }\n  ],\n  \"first_page_url\": \"https://sku2.test/api/v2/products?page=1\",\n  \"from\": 1,\n  \"last_page\": 50,\n  \"last_page_url\": \"https://sku2.test/api/v2/products?page=50\",\n  \"next_page_url\": \"https://sku2.test/api/v2/products?page=2\",\n  \"path\": \"https://sku2.test/api/v2/products\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 10,\n  \"total\": 498,\n  \"unfiltered_total\": 1843\n}"}]},{"name":"Search Products","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products","host":["{{baseUrl}}"],"path":["api","v2","products"]},"description":"Returns the same paginated product catalog list as the GET version of this endpoint, but accepts filters as a JSON request body instead of the query string. Use this POST variant when the filter set is too large or too complex to fit in a URL (for example, filtering by a long list of SKUs or ids). The response includes an `unfiltered_total` alongside the standard paginator totals so clients can show both the filtered and total record counts.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nProvide advanced filters under `filter_groups`, a tree of conditions combined with a `conjunction` of `and` or `or`. Each condition names a `column`, an `operator`, and a `value`. A free-text `search` term matches against id, SKU, name, barcode, MPN, and brand.\n\nText columns (`sku`, `name`, `barcode`, `mpn`, `hts_code`, `country_of_origin`, `brand_name`, `supplier_name`, `supplier_sku`, `tag`, `category`, `stock_uom_code`, `stock_uom_name`, `component_skus`, `parent_product`) accept: is, is_not, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, is_one_of, is_not_one_of.\nNumeric columns (`id`, `unit_cost`, `average_cost`, `supplier_price`, `default_supplier_leadtime`, `default_supplier_moq`, `default_supplier_target_stock_days`) accept: is, is_not, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between, is_empty, is_not_empty. Additional filters include `type`, `archived`, `brand_id`, `default_supplier_id`, `stock_uom_id`, `stock_status` (in_stock / out_of_stock), `has_backorder`, and `ids`.\n\nSortable columns (via `sort`, prefix with `-` for descending): id, sku, name, type, barcode, mpn, hts_code, country_of_origin, unit_cost, average_cost, supplier_price, weight, case_quantity, daily_average_consumption, min_stock_level, archived_at, created_at, updated_at, plus inventory snapshot sorts (inventory_available, inventory_available_to_sell, inventory_allocated, inventory_on_hold, inventory_in_transit, inventory_total, inventory_backorder, inventory_stock_value, inventory_incoming, total_quantity_sold, total_orders). Results default to `-created_at`.","body":{"mode":"raw","raw":"{\n  \"per_page\": 25,\n  \"page\": 1,\n  \"sort\": \"-created_at\",\n  \"filter_groups\": {\n    \"conjunction\": \"and\",\n    \"children\": [\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"stock_status\",\n          \"operator\": \"is\",\n          \"value\": \"in_stock\"\n        }\n      },\n      {\n        \"type\": \"group\",\n        \"group\": {\n          \"conjunction\": \"or\",\n          \"children\": [\n            {\n              \"type\": \"condition\",\n              \"condition\": {\n                \"column\": \"brand_name\",\n                \"operator\": \"is\",\n                \"value\": \"Acme\"\n              }\n            },\n            {\n              \"type\": \"condition\",\n              \"condition\": {\n                \"column\": \"sku\",\n                \"operator\": \"starts_with\",\n                \"value\": \"WIDGET\"\n              }\n            }\n          ]\n        }\n      }\n    ]\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/products","host":["{{baseUrl}}"],"path":["api","v2","products"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"sku\": \"WIDGET-BLUE-01\",\n      \"id\": 3312,\n      \"unit_cost\": 13.75,\n      \"image_url\": \"https://cdn.example.com/products/3312.jpg\",\n      \"name\": \"Blue Widget\",\n      \"brand\": {\n        \"id\": 5,\n        \"name\": \"Acme\"\n      },\n      \"barcode\": \"8801234567890\",\n      \"mpn\": \"ACM-WB-01\",\n      \"hts_code\": \"8471.30.0100\",\n      \"country_of_origin\": \"US\",\n      \"density_g_per_ml\": null,\n      \"density_source\": null,\n      \"density_basis\": null,\n      \"tags\": [\n        \"bestseller\"\n      ],\n      \"type\": \"standard\",\n      \"is_lot_tracked\": false,\n      \"has_fba\": true,\n      \"is_variation\": false,\n      \"inventory\": {\n        \"inventory_available\": 240,\n        \"inventory_allocated\": 12,\n        \"inventory_total\": 252,\n        \"inventory_stock_value\": 3465\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"per_page\": 25,\n  \"from\": 1,\n  \"to\": 1,\n  \"last_page\": 58,\n  \"total\": 1442,\n  \"unfiltered_total\": 3901,\n  \"next_page_url\": \"https://api.example.com/api/v2/products?page=2\",\n  \"prev_page_url\": null\n}"}]},{"name":"Get Product Aggregates","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/aggregates?aggregates[unit_cost]=sum","host":["{{baseUrl}}"],"path":["api","v2","products","aggregates"],"query":[{"key":"aggregates[unit_cost]","value":"sum","description":"Map of column => calculation. Repeat for each column to aggregate. Calculations: sum, avg, min, max, range, count_all, count_values, count_unique, count_empty, count_not_empty.","disabled":true}]},"description":"Compute summary aggregations over the full filtered product catalog in a single request.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAccepts the same filter and search parameters as List Products, plus an `aggregates` map of column => calculation. Archived products are excluded by default, matching List Products.\n\n**Calculations:** sum, avg, min, max, range (max - min), count_all (matching products), count_values / count_not_empty (products with a value), count_unique (distinct values), count_empty (products without a value).\n\n**Aggregatable columns:** id, unit_cost, average_cost, min_stock_level, daily_average_consumption, daily_average_consumption_net_returns, weight, length, width, height, case_quantity, case_weight, case_length, case_width, case_height, proforma_shipping_cost, proforma_landed_cost_percentage, proforma_marketplace_cost_percentage, inventory_on_hand, inventory_available, inventory_available_to_sell, inventory_allocated, inventory_on_hold, inventory_non_sellable, inventory_in_transit, inventory_total, inventory_backorder, inventory_backorder_planned, inventory_backorder_awaiting_receipt, inventory_stock_value, inventory_incoming, total_quantity_sold, total_orders.\n\nUnknown columns are silently ignored. A `null` value means no matching products carried a value for that column. An unsupported calculation returns a 422 validation error.\n\nAuthentication: Requires Bearer token. Supply `ids[]` to constrain the calculation to specific records; omit it to cover the whole filtered set."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/aggregates?aggregates[unit_cost]=sum","host":["{{baseUrl}}"],"path":["api","v2","products","aggregates"],"query":[{"key":"aggregates[unit_cost]","value":"sum","description":"Map of column => calculation. Repeat for each column to aggregate. Calculations: sum, avg, min, max, range, count_all, count_values, count_unique, count_empty, count_not_empty.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"unit_cost\": {\n      \"calc\": \"sum\",\n      \"value\": 152304.5\n    },\n    \"inventory_available\": {\n      \"calc\": \"sum\",\n      \"value\": 8421\n    }\n  }\n}"}]},{"name":"Get Product","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}?with=brand,images,tags,supplierProducts.supplier,components,variations,productPricingTiers,productAttributeValues.attribute.attributeGroup,productAttributeValues.attribute.values","host":["{{baseUrl}}"],"path":["api","v2","products",":product"],"query":[{"key":"with","value":"brand,images,tags,supplierProducts.supplier,components,variations,productPricingTiers,productAttributeValues.attribute.attributeGroup,productAttributeValues.attribute.values","description":"Comma-separated additional relations to load. Allowed: brand, images, tags, primaryCategory, otherCategories, supplierProducts.supplier, components, variations, productPricingTiers, salesNominalCode, cogsNominalCode, inventoryNominalCode, inventoryInTransitNominalCode, accruedPurchasesNominalCode, discountNominalCode, warehousesInventory.warehouse, productBlemished.originalProduct, productAttributeValues.attribute.attributeGroup, productAttributeValues.attribute.values, notes.user, parent, defaultSupplierProduct.supplier, totalInventory, purchaseOrderLines","disabled":true}],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get a single product with default relations loaded (brand, primaryImage, primaryCategory, tags, defaultSupplierProduct.supplier, totalInventory).\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nUse the `with` query parameter to load additional relations.\n\nLoad `productAttributeValues.attribute.attributeGroup` and `productAttributeValues.attribute.values` to include `product_attributes` in the response. Each entry carries the stored `value` plus the attribute's editing metadata:\n- `type` — stored data type: `string`, `longtext`, `numeric`, `integer`, `date`, `datetime`, `checkbox`\n- `display_options` — `has_options`, `type` (`Single` or `Multi`), `option_display_type` (e.g. `swatch`), `allow_custom_value`, `is_html`, `is_link`, `sort_order`\n- `validation` — optional constraints: `limit` (max characters), `precision` (decimal places), `min`, `max`\n- `options` — the configured option list with per-option swatch data (`value`, `swatch_hex`, `swatch_image_url`, `sort_order`); empty when the attribute is not option-backed or when `productAttributeValues.attribute.values` was not requested\n- `group_id` / `group_name` — the attribute group the attribute belongs to\n\nMulti-value attributes (`display_options.type` = `Multi`) hold every selected value in a single newline-separated string.\n\nThe response includes the product's type: `product_type_id`, `product_type_name`, `product_type_path` and a `product_type` object (`id`, `name`, `path`, `archived_at`) — null when the product has no type. The type decides which channel category the product publishes into on each sales channel; see Channel Categories."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}?with=brand,images,tags,supplierProducts.supplier,components,variations,productPricingTiers,productAttributeValues.attribute.attributeGroup,productAttributeValues.attribute.values","host":["{{baseUrl}}"],"path":["api","v2","products",":product"],"query":[{"key":"with","value":"brand,images,tags,supplierProducts.supplier,components,variations,productPricingTiers,productAttributeValues.attribute.attributeGroup,productAttributeValues.attribute.values","description":"Comma-separated additional relations to load. Allowed: brand, images, tags, primaryCategory, otherCategories, supplierProducts.supplier, components, variations, productPricingTiers, salesNominalCode, cogsNominalCode, inventoryNominalCode, inventoryInTransitNominalCode, accruedPurchasesNominalCode, discountNominalCode, warehousesInventory.warehouse, productBlemished.originalProduct, productAttributeValues.attribute.attributeGroup, productAttributeValues.attribute.values, notes.user, parent, defaultSupplierProduct.supplier, totalInventory, purchaseOrderLines","disabled":true}],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"sku\": \"WDG-001\",\n    \"name\": \"Blue Widget\",\n    \"type\": \"standard\",\n    \"barcode\": 123456789012,\n    \"mpn\": \"BW-001\",\n    \"hts_code\": \"8471.30.0100\",\n    \"country_of_origin\": \"US\",\n    \"density_g_per_ml\": null,\n    \"density_source\": null,\n    \"density_basis\": null,\n    \"unit_cost\": 5.5,\n    \"average_cost\": 5.75,\n    \"is_variation\": false,\n    \"is_dropshippable\": false,\n    \"is_lot_tracked\": true,\n    \"lot_tracking_method\": \"fifo\",\n    \"shelf_life_days\": 365,\n    \"near_expiry_threshold_days\": 45,\n    \"min_remaining_shelf_life_days\": 30,\n    \"expiry_aware_availability\": true,\n    \"archived_at\": null,\n    \"stock_uom_id\": 1,\n    \"stock_uom_code\": \"EACH\",\n    \"stock_uom_name\": \"Each\",\n    \"image_url\": \"https://example.com/images/widget.jpg\",\n    \"brand_id\": 1,\n    \"brand_name\": \"Acme\",\n    \"product_type_id\": 7,\n    \"product_type_name\": \"Cloth diapers\",\n    \"product_type_path\": \"Diapering > Cloth diapers\",\n    \"product_type\": {\n      \"id\": 7,\n      \"name\": \"Cloth diapers\",\n      \"path\": \"Diapering > Cloth diapers\",\n      \"archived_at\": null\n    },\n    \"default_supplier_id\": 3,\n    \"default_supplier_name\": \"Global Supplies Ltd\",\n    \"default_supplier_sku\": \"GS-WDG-001\",\n    \"default_supplier_leadtime\": 14,\n    \"default_supplier_moq\": 50,\n    \"supplier_count\": 2,\n    \"inventory_total\": 500,\n    \"inventory_available\": 450,\n    \"inventory_available_to_sell\": 450,\n    \"inventory_allocated\": 50,\n    \"inventory_on_hold\": 0,\n    \"inventory_in_transit\": 100,\n    \"inventory_consigned\": 50,\n    \"inventory_stock_value\": 2875,\n    \"inventory_avg_cost\": 5.75,\n    \"daily_average_consumption\": 5.2,\n    \"min_stock_level\": 100,\n    \"weight\": 0.5,\n    \"weight_unit\": \"kg\",\n    \"length\": 10,\n    \"width\": 5,\n    \"height\": 3,\n    \"dimension_unit\": \"cm\",\n    \"is_taxable\": true,\n    \"default_tax_rate_id\": 1,\n    \"sales_nominal_code_id\": 10,\n    \"cogs_nominal_code_id\": 11,\n    \"parent_id\": null,\n    \"has_fba\": false,\n    \"last_rebuilt_cogs_at\": \"2024-05-20T08:00:00.000000Z\",\n    \"tags\": [\n      \"blue\",\n      \"widget\"\n    ],\n    \"images\": [\n      {\n        \"id\": 5,\n        \"url\": \"https://example.com/images/widget.jpg\",\n        \"is_primary\": true,\n        \"sort_order\": 0\n      }\n    ],\n    \"product_attributes\": [\n      {\n        \"attribute_id\": 1,\n        \"attribute_name\": \"Color\",\n        \"type\": \"string\",\n        \"value\": \"Blue\",\n        \"group_id\": 2,\n        \"group_name\": \"Physical\",\n        \"display_options\": {\n          \"has_options\": true,\n          \"type\": \"Single\",\n          \"option_display_type\": \"swatch\",\n          \"allow_custom_value\": false,\n          \"is_html\": false,\n          \"is_link\": false,\n          \"sort_order\": 1\n        },\n        \"validation\": {\n          \"limit\": 60,\n          \"precision\": null,\n          \"min\": null,\n          \"max\": null\n        },\n        \"options\": [\n          {\n            \"value\": \"Red\",\n            \"swatch_hex\": \"#d32f2f\",\n            \"swatch_image_url\": null,\n            \"sort_order\": 1\n          },\n          {\n            \"value\": \"Blue\",\n            \"swatch_hex\": \"#1976d2\",\n            \"swatch_image_url\": null,\n            \"sort_order\": 2\n          },\n          {\n            \"value\": \"Green\",\n            \"swatch_hex\": \"#388e3c\",\n            \"swatch_image_url\": null,\n            \"sort_order\": 3\n          }\n        ]\n      },\n      {\n        \"attribute_id\": 2,\n        \"attribute_name\": \"Materials\",\n        \"type\": \"string\",\n        \"value\": \"Cotton\\nElastane\",\n        \"group_id\": 2,\n        \"group_name\": \"Physical\",\n        \"display_options\": {\n          \"has_options\": true,\n          \"type\": \"Multi\",\n          \"option_display_type\": \"dropdown\",\n          \"allow_custom_value\": true,\n          \"is_html\": false,\n          \"is_link\": false,\n          \"sort_order\": 2\n        },\n        \"validation\": null,\n        \"options\": [\n          {\n            \"value\": \"Cotton\",\n            \"swatch_hex\": null,\n            \"swatch_image_url\": null,\n            \"sort_order\": 1\n          },\n          {\n            \"value\": \"Elastane\",\n            \"swatch_hex\": null,\n            \"swatch_image_url\": null,\n            \"sort_order\": 2\n          },\n          {\n            \"value\": \"Wool\",\n            \"swatch_hex\": null,\n            \"swatch_image_url\": null,\n            \"sort_order\": 3\n          }\n        ]\n      },\n      {\n        \"attribute_id\": 3,\n        \"attribute_name\": \"Care Instructions\",\n        \"type\": \"longtext\",\n        \"value\": \"<p>Machine wash cold. Tumble dry low.</p>\",\n        \"group_id\": null,\n        \"group_name\": \"Ungrouped\",\n        \"display_options\": {\n          \"has_options\": false,\n          \"is_html\": true,\n          \"is_link\": false,\n          \"sort_order\": 3\n        },\n        \"validation\": null,\n        \"options\": []\n      }\n    ],\n    \"created_at\": \"2024-01-15T10:30:00.000000Z\",\n    \"updated_at\": \"2024-06-01T14:20:00.000000Z\"\n  }\n}"}]},{"name":"Update Product","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}","host":["{{baseUrl}}"],"path":["api","v2","products",":product"],"variable":[{"key":"product","value":"915","description":"Product ID"}]},"description":"Update a product. Currently this endpoint carries one field — the product-level pricing sync profile, inherited by the product's listings on every channel unless a listing sets its own. Other product attributes are updated through their existing endpoints.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nChanging the profile re-prices the product's listings that follow it: inline when there are 25 or fewer (`recompute.mode: \"sync\"`), otherwise as a background job identified by `recompute.tracked_job_log_id`.\n\nAuthentication: Requires Bearer token.\n\nRequires the `products.update` permission (API tokens: `products:write` scope).\n\nRequest body:\n- pricing_sync_profile_id (integer|null, required — the key must be present): the profile to apply, or null so the product's listings follow each channel's default. An archived profile is refused (422).\n\nResponse `data`:\n- id (integer): the product\n- pricing_sync_profile_id (integer|null)\n- profile (object|null): `{id, name}`\n- recompute: `{mode, listings, tracked_job_log_id}` — how many listings are being re-priced and whether that ran inline (`sync`) or was queued (`job`)","body":{"mode":"raw","raw":"{\n  \"pricing_sync_profile_id\": 2\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/products/{product}","host":["{{baseUrl}}"],"path":["api","v2","products",":product"],"variable":[{"key":"product","value":"915","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 915,\n    \"pricing_sync_profile_id\": 2,\n    \"profile\": {\n      \"id\": 2,\n      \"name\": \"MSRP ceiling\"\n    },\n    \"recompute\": {\n      \"mode\": \"sync\",\n      \"listings\": 3,\n      \"tracked_job_log_id\": null\n    }\n  },\n  \"message\": \"\\\"MSRP ceiling\\\" applies to this product's listings on every channel unless a listing sets its own.\"\n}"}]},{"name":"Get Product Inventory","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/inventory","host":["{{baseUrl}}"],"path":["api","v2","products",":product","inventory"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get inventory breakdown by warehouse for a product.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nIncludes FBA/AWD real-time data, Pan-European rollup, listing counts, inbound quantities, and backorder coverage (`backorder_planned` = unfulfilled demand without stock; `backorder_awaiting_receipt` = backordered demand covered by inbound POs). FBA/AWD warehouses show real-time data; standard warehouses use cached ProductInventory.\nEach warehouse row and the totals include `non_sellable` — physically-present stock in non-sellable bins (excluded from `available` but included in on-hand)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/inventory","host":["{{baseUrl}}"],"path":["api","v2","products",":product","inventory"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"warehouses\": [\n      {\n        \"warehouse_id\": false,\n        \"warehouse_name\": false,\n        \"warehouse_type\": false,\n        \"total\": false,\n        \"available\": false,\n        \"allocated\": false,\n        \"on_hold\": false,\n        \"in_transit\": false,\n        \"inbound\": false,\n        \"backorder_planned\": false,\n        \"backorder_awaiting_receipt\": false,\n        \"consigned\": false,\n        \"non_sellable\": 15,\n        \"listing_count\": false,\n        \"country_code\": false,\n        \"fba_warehouse_id\": false\n      },\n      {\n        \"warehouse_id\": false,\n        \"warehouse_name\": false,\n        \"warehouse_type\": false,\n        \"total\": false,\n        \"available\": false,\n        \"allocated\": false,\n        \"on_hold\": false,\n        \"in_transit\": false,\n        \"inbound\": false,\n        \"backorder_planned\": false,\n        \"backorder_awaiting_receipt\": false,\n        \"consigned\": false,\n        \"non_sellable\": 0,\n        \"listing_count\": false,\n        \"country_code\": false,\n        \"fba_warehouse_id\": false\n      }\n    ],\n    \"totals\": {\n      \"total\": false,\n      \"available\": false,\n      \"allocated\": false,\n      \"on_hold\": false,\n      \"in_transit\": false,\n      \"inbound\": false,\n      \"backorder_planned\": false,\n      \"backorder_awaiting_receipt\": false,\n      \"consigned\": false,\n      \"non_sellable\": 15,\n      \"incoming\": false,\n      \"stock_value\": false,\n      \"cache_updated_at\": false\n    }\n  }\n}"}]},{"name":"Get Product Suppliers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/suppliers","host":["{{baseUrl}}"],"path":["api","v2","products",":product","suppliers"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get supplier products for a product, including pricing tiers and supplier inventory.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`pricing` contains at most one entry per pricing tier, so `tier_id` is safe to key on.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/suppliers","host":["{{baseUrl}}"],"path":["api","v2","products",":product","suppliers"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 10,\n      \"supplier_id\": 3,\n      \"supplier_name\": \"Global Supplies Ltd\",\n      \"default_pricing_tier_id\": 1,\n      \"supplier_sku\": \"GS-WDG-001\",\n      \"lead_time\": 14,\n      \"minimum_order_quantity\": 50,\n      \"target_stock_days\": 30,\n      \"is_default\": true,\n      \"pricing\": [\n        {\n          \"tier_id\": 1,\n          \"tier_name\": \"Standard\",\n          \"price\": 4.5,\n          \"currency_code\": \"USD\"\n        }\n      ],\n      \"inventories\": [\n        {\n          \"id\": 7,\n          \"warehouse_id\": 10,\n          \"warehouse_name\": \"Supplier Warehouse A\",\n          \"quantity\": 2000,\n          \"in_stock\": true,\n          \"discontinued\": false,\n          \"eta\": null,\n          \"source\": \"manual\",\n          \"updated_at\": \"2024-05-15T09:00:00.000000Z\"\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"Get Product Pricing","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/pricing","host":["{{baseUrl}}"],"path":["api","v2","products",":product","pricing"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get product pricing tiers.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/pricing","host":["{{baseUrl}}"],"path":["api","v2","products",":product","pricing"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"tier_id\": 1,\n      \"tier_name\": \"Retail\",\n      \"price\": 12.99,\n      \"currency_code\": \"USD\",\n      \"is_default\": true\n    },\n    {\n      \"tier_id\": 2,\n      \"tier_name\": \"Wholesale\",\n      \"price\": 8.99,\n      \"currency_code\": \"USD\",\n      \"is_default\": false\n    }\n  ]\n}"}]},{"name":"Upsert Product Pricing","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/pricing","host":["{{baseUrl}}"],"path":["api","v2","products",":product","pricing"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Idempotently upsert a single pricing tier price for a product.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nUsed to save a pricing tier's master price without navigating to the product edit page. Returns the resolved tier row.","body":{"mode":"raw","raw":"{\n  \"tier_id\": 1,\n  \"price\": 12.99\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/pricing","host":["{{baseUrl}}"],"path":["api","v2","products",":product","pricing"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tier_id\": 1,\n    \"tier_name\": \"Retail\",\n    \"price\": 12.99,\n    \"currency_code\": \"USD\",\n    \"is_default\": true\n  }\n}"}]},{"name":"Get Product Components","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/components","host":["{{baseUrl}}"],"path":["api","v2","products",":product","components"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get components for a bundle or kit product.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/components","host":["{{baseUrl}}"],"path":["api","v2","products",":product","components"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 10,\n      \"sku\": \"COMP-A\",\n      \"name\": \"Component A\",\n      \"quantity\": 2,\n      \"type\": \"standard\"\n    },\n    {\n      \"id\": 11,\n      \"sku\": \"COMP-B\",\n      \"name\": \"Component B\",\n      \"quantity\": 1,\n      \"type\": \"standard\"\n    }\n  ]\n}"}]},{"name":"Get Parent Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/parent-products","host":["{{baseUrl}}"],"path":["api","v2","products",":product","parent-products"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get parent bundle/kit products that include this product as a component.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/parent-products","host":["{{baseUrl}}"],"path":["api","v2","products",":product","parent-products"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5,\n      \"sku\": \"BUNDLE-001\",\n      \"name\": \"Widget Bundle\",\n      \"type\": \"bundle\",\n      \"image_url\": null,\n      \"quantity\": 2\n    }\n  ]\n}"}]},{"name":"Get Bundle Potential","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/bundle-potential","host":["{{baseUrl}}"],"path":["api","v2","products",":product","bundle-potential"],"variable":[{"key":"product","value":"5","description":"Product ID (must be bundle or kit type)"}]},"description":"Get bundle assembly potential — how many units can be assembled from current component inventory.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns per-warehouse breakdown and bottleneck identification. Returns empty result for non-bundle/kit products."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/bundle-potential","host":["{{baseUrl}}"],"path":["api","v2","products",":product","bundle-potential"],"variable":[{"key":"product","value":"5","description":"Product ID (must be bundle or kit type)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"components\": [\n      {\n        \"product_id\": 10,\n        \"sku\": \"COMP-A\",\n        \"name\": \"Component A\",\n        \"required_quantity\": 2,\n        \"available_inventory\": 450,\n        \"max_assemblable\": 225,\n        \"is_bottleneck\": false\n      },\n      {\n        \"product_id\": 11,\n        \"sku\": \"COMP-B\",\n        \"name\": \"Component B\",\n        \"required_quantity\": 1,\n        \"available_inventory\": 100,\n        \"max_assemblable\": 100,\n        \"is_bottleneck\": true\n      }\n    ],\n    \"max_assemblable\": 100,\n    \"warehouses\": [\n      {\n        \"warehouse_id\": 1,\n        \"warehouse_name\": \"Main Warehouse\",\n        \"max_assemblable\": 100,\n        \"components\": []\n      }\n    ]\n  }\n}"}]},{"name":"Get Product Variations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/variations","host":["{{baseUrl}}"],"path":["api","v2","products",":product","variations"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get variations of a matrix product.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/variations","host":["{{baseUrl}}"],"path":["api","v2","products",":product","variations"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 43,\n      \"sku\": \"WDG-001-RED\",\n      \"name\": \"Blue Widget - Red\",\n      \"image_url\": null,\n      \"attributes\": [\n        {\n          \"name\": \"Color\",\n          \"value\": \"Red\"\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"Get Product Images","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/images","host":["{{baseUrl}}"],"path":["api","v2","products",":product","images"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get all images for a product, sorted by sort_order.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nEach image may carry `swatch_variant_attribute` (string, nullable): when set, the image is the variant swatch for the named attribute (e.g. \"Color\") on matrix variant children. Channels that support swatches (Walmart) emit it as that child's swatch image."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/images","host":["{{baseUrl}}"],"path":["api","v2","products",":product","images"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5,\n      \"url\": \"https://example.com/images/widget.jpg\",\n      \"is_primary\": true,\n      \"sort_order\": 0,\n      \"swatch_variant_attribute\": \"Color\",\n      \"created_at\": \"2024-01-15T10:30:00Z\",\n      \"updated_at\": \"2024-01-15T10:30:00Z\"\n    },\n    {\n      \"id\": 6,\n      \"url\": \"https://example.com/images/widget-alt.jpg\",\n      \"is_primary\": false,\n      \"sort_order\": 1,\n      \"swatch_variant_attribute\": null,\n      \"created_at\": \"2024-01-15T10:31:00Z\",\n      \"updated_at\": \"2024-01-15T10:31:00Z\"\n    }\n  ]\n}"}]},{"name":"Get Product Listings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/listings?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","v2","products",":product","listings"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true}],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get paginated sales channel listings for a product.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/listings?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","v2","products",":product","listings"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true}],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 100,\n      \"listing_sku\": \"WDG-001\",\n      \"is_fba\": false,\n      \"sales_channel_id\": 1,\n      \"sales_channel_name\": \"Shopify US\"\n    }\n  ],\n  \"total\": 3,\n  \"per_page\": 10\n}"}]},{"name":"Get Product Listings Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/listings/summary","host":["{{baseUrl}}"],"path":["api","v2","products",":product","listings","summary"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get listing counts grouped by integration for a product.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/listings/summary","host":["{{baseUrl}}"],"path":["api","v2","products",":product","listings","summary"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total\": 5,\n    \"integrations\": [\n      {\n        \"integration_name\": \"Shopify\",\n        \"integration_id\": 1,\n        \"count\": 3\n      },\n      {\n        \"integration_name\": \"Amazon\",\n        \"integration_id\": 2,\n        \"count\": 2\n      }\n    ]\n  }\n}"}]},{"name":"Get Product Inventory Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/inventory-movements?page=1&per_page=15","host":["{{baseUrl}}"],"path":["api","v2","products",":product","inventory-movements"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"15","description":"Results per page (default 10).","disabled":true}],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get paginated inventory movements for a product.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nCost fields on each movement: `unit_cost` is the FIFO layer's current average cost (falling back to the movement's own cost when it has no layer) and keeps that meaning for compatibility. `recorded_unit_cost` is the unit cost the movement was recorded at, the figure its cost-of-goods journal booked (null when unknown). `layer_unit_cost` is the FIFO layer's current per-unit cost after any later revaluation or landed cost (null when the movement has no layer). The two differ when a layer is revalued after the movement, for example by a supplier price true-up or a freight bill."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/inventory-movements?page=1&per_page=15","host":["{{baseUrl}}"],"path":["api","v2","products",":product","inventory-movements"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"15","description":"Results per page (default 10).","disabled":true}],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 1001,\n      \"type\": \"sale\",\n      \"inventory_status\": \"active\",\n      \"quantity\": -2,\n      \"reference\": \"SO-0042\",\n      \"link_type\": \"App\\\\Models\\\\SalesOrderLine\",\n      \"link_id\": 55,\n      \"parent_link_id\": 22,\n      \"detail_link_id\": null,\n      \"warehouse_id\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"unit_cost\": 5.75,\n      \"recorded_unit_cost\": 5.5,\n      \"layer_unit_cost\": 5.75,\n      \"unit_cost_provisional\": false,\n      \"layer\": {\n        \"fifo_layer_id\": 10,\n        \"name\": \"fifo\"\n      },\n      \"inventory_movement_date\": \"2024-06-01\",\n      \"created_at\": \"2024-06-01T14:20:00Z\"\n    }\n  ],\n  \"total\": 150,\n  \"per_page\": 15\n}"}]},{"name":"Export Product Inventory Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/inventory-movements/export?format=xlsx&scope=filtered","host":["{{baseUrl}}"],"path":["api","v2","products",":product","inventory-movements","export"],"query":[{"key":"format","value":"xlsx","description":"Export format: xlsx or csv (default: xlsx)","disabled":true},{"key":"scope","value":"filtered","description":"Export scope: filtered (default, applies active filters), all (ignores filters), or current_page","disabled":true}],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Exports a product's inventory movements as an XLSX or CSV file. Supports the same filters, search, and sort as the movements list, plus column selection via the `columns` parameter.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nColumns include: ID, Date, Type, Status, Quantity, Unit Cost, Layer Cost Now, Warehouse, FIFO Layer ID, Source, Reference, Link Type, Link ID, Created.\n\n`Unit Cost` is the cost the movement was recorded at (what its cost-of-goods journal booked); `Layer Cost Now` is the FIFO layer's current per-unit cost. `Layer Cost Now` is always exported alongside `Unit Cost` when `columns` includes `unit_cost`.\n\nAuthentication: Requires Bearer token.\n\nResponse: File download (binary), not JSON.\n\n**Requires permission:** `inventory.export`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/inventory-movements/export?format=xlsx&scope=filtered","host":["{{baseUrl}}"],"path":["api","v2","products",":product","inventory-movements","export"],"query":[{"key":"format","value":"xlsx","description":"Export format: xlsx or csv (default: xlsx)","disabled":true},{"key":"scope","value":"filtered","description":"Export scope: filtered (default, applies active filters), all (ignores filters), or current_page","disabled":true}],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"[Binary XLSX file download]"}]},{"name":"List Product Sales","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/sales","host":["{{baseUrl}}"],"path":["api","v2","products",":product","sales"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"List sales order lines for a product — one row per line with parent-order context (order number, date, sales channel, customer, order and fulfillment status) and per-line financials (unit price, line revenue, profit). Draft orders are excluded. Results are live order data.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFilters:\n- filter[search] — searches order number and customer name\n- filter[sales_channel_id] — exact sales channel ID\n- filter[warehouse_id] — exact warehouse ID (line-level)\n- filter[order_status] / filter[fulfillment_status] / filter[order_number] / filter[customer_name] — text filters; bare param matches exactly, or suffix an operator, e.g. filter[order_status.is_one_of]=open,closed, filter[order_number.contains]=1234. Operators: contains, does_not_contain, is, is_not, is_one_of, is_not_one_of, starts_with, does_not_start_with, ends_with, does_not_end_with, is_empty, is_not_empty\n- filter[`order_date.<operator>`] — date filters. Operators: is, is_not, before, after, on_or_before, on_or_after, between, is_empty, is_not_empty, today, yesterday, past_week, past_month, past_year\n- filter[`quantity.<operator>`] / filter[`unit_price.<operator>`] — numeric filters. Operators: is, is_not, is_one_of, is_not_one_of, greater_than, less_than, greater_than_or_equal, less_than_or_equal, between, is_empty, is_not_empty\n- filter_groups — base64-encoded JSON tree for grouped AND/OR filter conditions, e.g. {\"conjunction\":\"and\",\"children\":[{\"type\":\"condition\",\"condition\":{\"column\":\"quantity\",\"operator\":\"greater_than\",\"value\":1}}]}\n\nAllowed sorts: id, order_date, order_number, order_status, fulfillment_status, customer_name, quantity, unit_price. Prefix with - for descending. Default: -order_date.\n\nPagination: page, per_page (default 15)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/sales","host":["{{baseUrl}}"],"path":["api","v2","products",":product","sales"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 59881,\n      \"sales_order_id\": 295069,\n      \"order_number\": \"SO-10412\",\n      \"order_date\": \"2026-07-02T16:58:28.000000Z\",\n      \"sales_channel_id\": 30,\n      \"sales_channel_name\": \"TikTok Shop\",\n      \"customer_id\": 818699,\n      \"customer_name\": \"Jane Cooper\",\n      \"order_status\": \"closed\",\n      \"fulfillment_status\": \"fulfilled\",\n      \"quantity\": 1,\n      \"unit_price\": 149.99,\n      \"line_revenue\": 149.99,\n      \"profit\": 48.89,\n      \"warehouse_id\": 4,\n      \"warehouse_name\": \"Main Warehouse\"\n    }\n  ],\n  \"total\": 6,\n  \"per_page\": 15,\n  \"last_page\": 1,\n  \"from\": 1,\n  \"to\": 6\n}"}]},{"name":"Get Product FIFO Layers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/fifo-layers?page=1&per_page=15","host":["{{baseUrl}}"],"path":["api","v2","products",":product","fifo-layers"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"15","description":"Results per page (default 10).","disabled":true}],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get paginated FIFO cost layers for a product.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/fifo-layers?page=1&per_page=15","host":["{{baseUrl}}"],"path":["api","v2","products",":product","fifo-layers"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"15","description":"Results per page (default 10).","disabled":true}],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 10,\n      \"fifo_layer_date\": \"2024-05-01\",\n      \"quantity\": 200,\n      \"fulfilled_quantity\": 50,\n      \"available_quantity\": 150,\n      \"unit_cost\": 5.5,\n      \"total_cost\": 1100,\n      \"warehouse_id\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"source_type\": \"Purchase Order\",\n      \"source_reference\": \"PO-0015\",\n      \"link_type\": \"App\\\\Models\\\\PurchaseOrderLine\",\n      \"link_id\": 88,\n      \"parent_link_id\": 40,\n      \"detail_link_id\": null,\n      \"condition\": \"new\",\n      \"is_consignment\": false,\n      \"created_at\": \"2024-05-01T09:00:00Z\"\n    }\n  ],\n  \"total\": 8,\n  \"per_page\": 15\n}"}]},{"name":"Get Product Lots & Expiry","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/lots-expiry","host":["{{baseUrl}}"],"path":["api","v2","products",":product","lots-expiry"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Paginated lot/expiry breakdown scoped to a single product (the product detail \"Lots & Expiry\" tab).\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReuses the cross-warehouse expiry pipeline (internal FIFO layers + Amazon FBA layers), enriched with each lot's review/disposition state. Each row is one batch/expiry FIFO layer with quantities, valuation, days-to-expiry, shelf-life remaining percentage, and review status.\n\nAuthentication: Requires Bearer token.\n\nPagination: page (default 1), per_page (default 25). Returns the standard Laravel paginator envelope (current_page, last_page, per_page, total, data)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/lots-expiry","host":["{{baseUrl}}"],"path":["api","v2","products",":product","lots-expiry"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"fifo_layer_id\": 9012,\n      \"product_id\": 42,\n      \"sku\": \"WDG-001\",\n      \"product_name\": \"Blue Widget\",\n      \"brand_name\": \"Acme\",\n      \"supplier_name\": \"Global Supplies Ltd\",\n      \"warehouse_id\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"bucket\": \"0-30\",\n      \"days_to_expiry\": 18,\n      \"expiry_date\": \"2026-07-04T00:00:00.000000Z\",\n      \"manufacture_date\": \"2026-01-04T00:00:00.000000Z\",\n      \"batch_number\": \"LOT-2026-0042\",\n      \"shelf_life_remaining_pct\": 30.5,\n      \"link_type\": \"App\\\\Models\\\\PurchaseOrderLine\",\n      \"link_type_name\": \"Purchase Order Line\",\n      \"link_id\": 88,\n      \"quantity\": 150,\n      \"avg_unit_cost\": 5.5,\n      \"total_value\": 825,\n      \"valuation_source\": \"internal\",\n      \"is_fba\": false,\n      \"review_status\": \"open\",\n      \"review_status_label\": \"Open\",\n      \"review_notes\": null,\n      \"reviewed_at\": null,\n      \"reviewed_by_name\": null,\n      \"inventory_hold_id\": null\n    },\n    {\n      \"fifo_layer_id\": null,\n      \"product_id\": 42,\n      \"sku\": \"WDG-001\",\n      \"product_name\": \"Blue Widget\",\n      \"brand_name\": \"Acme\",\n      \"supplier_name\": null,\n      \"warehouse_id\": 87,\n      \"warehouse_name\": \"Amazon FBA (US)\",\n      \"bucket\": \"61-90\",\n      \"days_to_expiry\": 74,\n      \"expiry_date\": \"2026-08-29T00:00:00.000000Z\",\n      \"manufacture_date\": null,\n      \"batch_number\": \"FBA-LOT-7781\",\n      \"shelf_life_remaining_pct\": null,\n      \"link_type\": null,\n      \"link_type_name\": \"\",\n      \"link_id\": null,\n      \"quantity\": 60,\n      \"avg_unit_cost\": 5.75,\n      \"total_value\": 345,\n      \"valuation_source\": \"amazon\",\n      \"is_fba\": true,\n      \"review_status\": null,\n      \"review_status_label\": null,\n      \"review_notes\": null,\n      \"reviewed_at\": null,\n      \"reviewed_by_name\": null,\n      \"inventory_hold_id\": null\n    }\n  ],\n  \"from\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"to\": 2,\n  \"total\": 2\n}"}]},{"name":"Get Product Lot Genealogy","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/lots/{fifoLayer}/genealogy","host":["{{baseUrl}}"],"path":["api","v2","products",":product","lots",":fifoLayer","genealogy"],"variable":[{"key":"product","value":"42","description":"Product ID"},{"key":"fifoLayer","value":"9012","description":"FIFO layer ID (the lot) to trace. Must belong to the product, otherwise 404."}]},"description":"Read-only lot genealogy / traceability tree for a single FIFO layer (lot) of this product.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns two trees:\n- forward: downstream consumers (where the lot went — sales orders, manufacturing orders, transfers that consumed it).\n- backward: upstream ingredients (what was consumed to produce this lot, for manufactured lots). Purchased lots have an empty backward tree.\n\nEach node carries entity_type ('manufacturing_order' | 'purchase_order' | 'sales_order' | 'warehouse_transfer' | 'fifo_layer'), reference, product info, batch_number, quantity, consumed_quantity (qty travelling along the parent->node edge), date, fifo_layer_id, and recursive children.\n\nReturns 404 if the FIFO layer does not belong to the product.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/lots/{fifoLayer}/genealogy","host":["{{baseUrl}}"],"path":["api","v2","products",":product","lots",":fifoLayer","genealogy"],"variable":[{"key":"product","value":"42","description":"Product ID"},{"key":"fifoLayer","value":"9012","description":"FIFO layer ID (the lot) to trace. Must belong to the product, otherwise 404."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"forward\": {\n      \"id\": \"fifo-9012\",\n      \"entity_type\": \"purchase_order\",\n      \"reference\": \"PO-1234\",\n      \"product_id\": 42,\n      \"product_name\": \"Blue Widget\",\n      \"batch_number\": \"LOT-2026-0042\",\n      \"quantity\": 200,\n      \"consumed_quantity\": null,\n      \"date\": \"2026-01-04\",\n      \"fifo_layer_id\": 9012,\n      \"manufacturing_order_id\": null,\n      \"purchase_order_id\": 1234,\n      \"children\": [\n        {\n          \"id\": \"terminal-App-Models-SalesOrderLine-55347\",\n          \"entity_type\": \"sales_order\",\n          \"reference\": \"SO-8842\",\n          \"product_id\": null,\n          \"product_name\": null,\n          \"batch_number\": null,\n          \"quantity\": null,\n          \"consumed_quantity\": 50,\n          \"date\": \"2026-02-12\",\n          \"fifo_layer_id\": null,\n          \"manufacturing_order_id\": null,\n          \"purchase_order_id\": null,\n          \"children\": []\n        }\n      ]\n    },\n    \"backward\": {\n      \"id\": \"fifo-9012\",\n      \"entity_type\": \"purchase_order\",\n      \"reference\": \"PO-1234\",\n      \"product_id\": 42,\n      \"product_name\": \"Blue Widget\",\n      \"batch_number\": \"LOT-2026-0042\",\n      \"quantity\": 200,\n      \"consumed_quantity\": null,\n      \"date\": \"2026-01-04\",\n      \"fifo_layer_id\": 9012,\n      \"manufacturing_order_id\": null,\n      \"purchase_order_id\": 1234,\n      \"children\": []\n    }\n  }\n}"}]},{"name":"Get Product Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/activity-log?page=1&per_page=15&filter[search]=updated","host":["{{baseUrl}}"],"path":["api","v2","products",":product","activity-log"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"15","description":"Items per page (default: 15)","disabled":true},{"key":"filter[search]","value":"updated","description":"Search across causer name, properties, event, and subject type","disabled":true}],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get paginated activity log for a product (covers Product and SupplierProductPricing events).\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/activity-log?page=1&per_page=15&filter[search]=updated","host":["{{baseUrl}}"],"path":["api","v2","products",":product","activity-log"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"15","description":"Items per page (default: 15)","disabled":true},{"key":"filter[search]","value":"updated","description":"Search across causer name, properties, event, and subject type","disabled":true}],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 501,\n      \"description\": \"updated\",\n      \"event\": \"updated\",\n      \"subject_type\": \"Product\",\n      \"subject_id\": 42,\n      \"properties\": {\n        \"attributes\": {\n          \"unit_cost\": 5.5\n        },\n        \"old\": {\n          \"unit_cost\": 5\n        }\n      },\n      \"causer_name\": \"John Smith\",\n      \"created_at\": \"2024-06-01T14:20:00Z\"\n    }\n  ],\n  \"total\": 45,\n  \"per_page\": 15\n}"}]},{"name":"Get Product Notes","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/notes","host":["{{baseUrl}}"],"path":["api","v2","products",":product","notes"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get notes for a product, sorted by most recent first.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/notes","host":["{{baseUrl}}"],"path":["api","v2","products",":product","notes"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"note\": \"Check with supplier for Q3 pricing update\",\n      \"created_at\": \"2024-05-20T11:00:00Z\",\n      \"user\": {\n        \"id\": 2,\n        \"name\": \"Jane Doe\"\n      }\n    }\n  ]\n}"}]},{"name":"Add Product Note","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/notes","host":["{{baseUrl}}"],"path":["api","v2","products",":product","notes"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Add a note to a product. The note is attributed to the authenticated user.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest body:\n- note (string, required): The note text.\n\nAuthentication: Requires Bearer token with the products.update permission.","body":{"mode":"raw","raw":"{\n  \"note\": \"Received used and out of warranty — discarding.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/notes","host":["{{baseUrl}}"],"path":["api","v2","products",":product","notes"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 13,\n    \"note\": \"Received used and out of warranty — discarding.\",\n    \"created_at\": \"2026-08-22T14:15:00Z\",\n    \"user\": {\n      \"id\": 2,\n      \"name\": \"Jane Doe\"\n    }\n  },\n  \"message\": \"The note created successfully\"\n}"}]},{"name":"Delete Product Note","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/notes/{noteId}","host":["{{baseUrl}}"],"path":["api","v2","products",":product","notes",":noteId"],"variable":[{"key":"product","value":"42","description":"Product ID"},{"key":"noteId","value":"13","description":"Note ID"}]},"description":"Delete a note from a product. The deletion is scoped to the product — a note belonging to a different product cannot be removed through this route.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token with the products.update permission."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/notes/{noteId}","host":["{{baseUrl}}"],"path":["api","v2","products",":product","notes",":noteId"],"variable":[{"key":"product","value":"42","description":"Product ID"},{"key":"noteId","value":"13","description":"Note ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"The note 13 deleted successfully\"\n}"}]},{"name":"Get Product Purchase Order Lines","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/purchase-order-lines","host":["{{baseUrl}}"],"path":["api","v2","products",":product","purchase-order-lines"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get purchase order lines for a product, filtered by status.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nExcludes draft purchase orders.\n\nEach row carries an effective ETA (`eta`) describing when the stock is expected to arrive. When the line is packed onto an inbound shipment that is still arriving (planned, in transit, or partially received), the earliest such shipment's expected arrival date wins and `eta_source` is `inbound_shipment`, with `eta_shipment_id` and `eta_shipment_number` identifying it. Otherwise the purchase order's own estimated delivery date is used and `eta_source` is `purchase_order`. When neither is set, `eta` and `eta_source` are null.\n\nRows with `status=inbound` are returned ordered by `eta` ascending, with undated rows last, so the next stock to land is first."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/purchase-order-lines","host":["{{baseUrl}}"],"path":["api","v2","products",":product","purchase-order-lines"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"po_id\": 91,\n      \"po_date\": \"2026-04-08\",\n      \"po_number\": \"PO-0026-3\",\n      \"supplier_name\": \"Global Supplies Ltd\",\n      \"quantity\": 12,\n      \"received_quantity\": 0,\n      \"unreceived_quantity\": 12,\n      \"eta\": \"2026-09-10T00:00:00.000000Z\",\n      \"eta_source\": \"inbound_shipment\",\n      \"eta_shipment_id\": 57,\n      \"eta_shipment_number\": \"IS-00057\"\n    },\n    {\n      \"po_id\": 88,\n      \"po_date\": \"2026-05-15\",\n      \"po_number\": \"PO-0034\",\n      \"supplier_name\": \"Global Supplies Ltd\",\n      \"quantity\": 200,\n      \"received_quantity\": 0,\n      \"unreceived_quantity\": 200,\n      \"eta\": \"2026-09-20T00:00:00.000000Z\",\n      \"eta_source\": \"purchase_order\",\n      \"eta_shipment_id\": null,\n      \"eta_shipment_number\": null\n    },\n    {\n      \"po_id\": 94,\n      \"po_date\": \"2026-08-27\",\n      \"po_number\": \"PO-0068\",\n      \"supplier_name\": \"Global Supplies Ltd\",\n      \"quantity\": 40,\n      \"received_quantity\": 0,\n      \"unreceived_quantity\": 40,\n      \"eta\": null,\n      \"eta_source\": null,\n      \"eta_shipment_id\": null,\n      \"eta_shipment_number\": null\n    }\n  ]\n}"}]},{"name":"Get Product Inventory Tally","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/inventory-tally?warehouse_id=1","host":["{{baseUrl}}"],"path":["api","v2","products",":product","inventory-tally"],"query":[{"key":"warehouse_id","value":"1","description":"Required. Warehouse ID to tally","disabled":true}],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get the enriched inventory tally for a product at a specific warehouse — a chronological movement ledger with running balances.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe `movements` array is a merged ledger of two row kinds, discriminated by `row_kind`:\n- `movement` — a real inventory movement. Carries `id`, `type`, `quantity`, `reference`, and the ledger `running_balance`.\n- `channel_debt` — a sales order line the sales channel fulfilled while the warehouse had no stock. No inventory movement exists for these units, so the row never changes `running_balance`. Carries `sales_order_id`, `sales_order_line_id`, the gross owed `quantity` (negative), `outstanding_quantity` (gross minus active allocations), and `is_covered`.\n\nEvery row also carries `implied_physical_balance` — the ledger running balance minus cumulative channel debt so far, i.e. the physical stock level implied once channel-shipped-but-never-deducted units are counted.\n\nChannel-debt rows are only interleaved when `inventory_status` is `active` (the default); an `in_transit` tally returns zeroed channel-debt totals.\n\nSummary fields: `movement_count` counts movement rows only; `channel_debt_count`, `channel_debt_units` (gross), `channel_debt_outstanding_units`, and `channel_debt_order_count` describe the debt; `implied_physical_closing_balance` = `closing_balance` minus gross channel debt.\n\nAuthentication: Requires Bearer token.\n\nMovement rows also carry `recorded_at` — when the movement was booked. A sale recorded well after its movement date is the settled-fulfillment-debt signature: the deduction was minted when stock arrived, backdated to the ship time.\n\nMovement rows carry the polymorphic source document (`link_type`, `link_id`, `parent_link_id`, `detail_link_id`) so the reference can link to its page. A `channel_debt` row's `date` is its chronological settlement position: the channel ship time when a recorded shipment exists, else the order date — the date its movement will carry once settled. Scarce arriving stock settles debt in exactly this order."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/inventory-tally?warehouse_id=1","host":["{{baseUrl}}"],"path":["api","v2","products",":product","inventory-tally"],"query":[{"key":"warehouse_id","value":"1","description":"Required. Warehouse ID to tally","disabled":true}],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"movements\": [\n      {\n        \"id\": 8101,\n        \"uid\": \"m-8101\",\n        \"row_kind\": \"movement\",\n        \"date\": \"2026-07-31T00:00:00+00:00\",\n        \"type\": \"stock_take\",\n        \"quantity\": 8,\n        \"reference\": \"2\",\n        \"running_balance\": 8,\n        \"implied_physical_balance\": 8,\n        \"recorded_at\": \"2026-07-31T00:00:00+00:00\",\n        \"link_type\": \"App\\\\Models\\\\StockTake\",\n        \"link_id\": 501,\n        \"parent_link_id\": 2101,\n        \"detail_link_id\": null\n      },\n      {\n        \"id\": 8298,\n        \"uid\": \"m-8298\",\n        \"row_kind\": \"movement\",\n        \"date\": \"2026-08-06T09:09:33+00:00\",\n        \"type\": \"sale\",\n        \"quantity\": -1,\n        \"reference\": \"#917275R.1\",\n        \"running_balance\": 7,\n        \"implied_physical_balance\": 7,\n        \"recorded_at\": \"2026-08-27T15:24:10+00:00\",\n        \"link_type\": \"App\\\\Models\\\\SalesOrderFulfillmentLine\",\n        \"link_id\": 501,\n        \"parent_link_id\": 2101,\n        \"detail_link_id\": null\n      },\n      {\n        \"id\": null,\n        \"uid\": \"debt-2143\",\n        \"row_kind\": \"channel_debt\",\n        \"date\": \"2026-08-11T19:48:31+00:00\",\n        \"type\": \"channel_debt\",\n        \"quantity\": -1,\n        \"reference\": \"#917396R\",\n        \"sales_order_id\": 2088,\n        \"sales_order_line_id\": 2143,\n        \"outstanding_quantity\": 1,\n        \"is_covered\": false,\n        \"running_balance\": 7,\n        \"implied_physical_balance\": 6\n      }\n    ],\n    \"summary\": {\n      \"opening_balance\": 0,\n      \"total_in\": 8,\n      \"total_out\": 1,\n      \"net_change\": 7,\n      \"closing_balance\": 7,\n      \"movement_count\": 2,\n      \"channel_debt_count\": 1,\n      \"channel_debt_units\": 1,\n      \"channel_debt_outstanding_units\": 1,\n      \"channel_debt_order_count\": 1,\n      \"implied_physical_closing_balance\": 6\n    },\n    \"warehouse_name\": \"Main Warehouse\"\n  }\n}"}]},{"name":"Export Product Inventory Tally","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/inventory-tally/export?warehouse_id=1","host":["{{baseUrl}}"],"path":["api","v2","products",":product","inventory-tally","export"],"query":[{"key":"warehouse_id","value":"1","description":"Required. Warehouse ID to tally","disabled":true}],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Exports the product's inventory tally ledger for a warehouse as an XLSX or CSV file, mirroring the on-screen Movement Ledger: chronological rows with Date, Type, Reference, Qty In, Qty Out, and running Balance.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nWhen channel-debt events exist (units the sales channel fulfilled with no stock, so no movement exists), two extra columns are appended: Channel Debt (the debt quantity, kept separate so Qty In minus Qty Out still reconciles to the closing balance) and Implied Physical Balance (ledger balance minus cumulative channel debt).\n\nAuthentication: Requires Bearer token.\n\nResponse: File download (binary), not JSON.\n\n**Requires permission:** `inventory.export`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/inventory-tally/export?warehouse_id=1","host":["{{baseUrl}}"],"path":["api","v2","products",":product","inventory-tally","export"],"query":[{"key":"warehouse_id","value":"1","description":"Required. Warehouse ID to tally","disabled":true}],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"[Binary CSV file download]"}]},{"name":"Get Product Fulfillment Debt Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/fulfillment-debt-summary","host":["{{baseUrl}}"],"path":["api","v2","products",":product","fulfillment-debt-summary"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get the channel fulfillment-debt summary for a product — units the sales channel shipped that have never been deducted from inventory (no inventory movements exist for them), with a per-warehouse breakdown.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`total_units` is the gross owed quantity; `outstanding_units` nets out active allocations (planned / awaiting receipt / allocated) that will settle automatically when stock arrives. `first_date` / `last_date` bound the affected order dates.\n\nPass `warehouse_id` to scope the summary (and the breakdown) to a single warehouse.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/fulfillment-debt-summary","host":["{{baseUrl}}"],"path":["api","v2","products",":product","fulfillment-debt-summary"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_units\": 19,\n    \"outstanding_units\": 19,\n    \"order_count\": 18,\n    \"line_count\": 18,\n    \"first_date\": \"2026-08-11T19:48:31+00:00\",\n    \"last_date\": \"2026-08-26T04:12:40+00:00\",\n    \"warehouses\": [\n      {\n        \"warehouse_id\": 3,\n        \"warehouse_name\": \"Main Warehouse\",\n        \"units\": 19,\n        \"outstanding_units\": 19,\n        \"order_count\": 18,\n        \"line_count\": 18\n      }\n    ]\n  }\n}"}]},{"name":"Get Product Warehouse Transfer Lines","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/warehouse-transfer-lines","host":["{{baseUrl}}"],"path":["api","v2","products",":product","warehouse-transfer-lines"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get every warehouse transfer line for a product with a breakdown of where the stock physically is: ordered (net of cancellation), shipped, received, in transit (shipped but not yet received) and awaiting shipment (ordered but not yet dispatched).\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nEach line carries a `buckets` array naming the states it is in:\n- `in_transit` — open transfer, shipped units not yet received\n- `awaiting_shipment` — open transfer, units not yet dispatched\n- `received` — anything received, whatever the transfer's status\n\nA partially shipped line is in both in-flight buckets. Closed transfers never count as in transit or awaiting shipment. Lines in no bucket are omitted.\n\nFor Amazon FBA/AWD destinations, `quantity_received` is Amazon's confirmed receipt and `quantity_in_transit` is shipped minus that; a shipment Amazon has not acknowledged yet is entirely in transit.\n\n`quantity_unreceived` is the open balance (ordered net of cancellation minus received). It includes units that have not shipped, so it is not an in-transit figure; it is retained for compatibility — read `quantity_in_transit` and `quantity_unshipped` instead.\n\nEach row is one transfer LINE, identified by `line_id` — a transfer can carry more than one line for the same product. Results are ordered newest transfer first."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/warehouse-transfer-lines","host":["{{baseUrl}}"],"path":["api","v2","products",":product","warehouse-transfer-lines"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"line_id\": 4100,\n      \"transfer_id\": 102,\n      \"transfer_date\": \"2026-08-12T00:00:00.000000Z\",\n      \"warehouse_transfer_number\": \"WH-0102\",\n      \"transfer_status\": \"open\",\n      \"shipment_status\": \"partially_shipped\",\n      \"receipt_status\": \"unreceived\",\n      \"from_warehouse_id\": 1,\n      \"from_warehouse\": \"Main Warehouse\",\n      \"to_warehouse_id\": 8,\n      \"to_warehouse\": \"East Coast 3PL\",\n      \"to_warehouse_type\": \"3pl\",\n      \"quantity\": 4535,\n      \"quantity_cancelled\": 0,\n      \"quantity_shipped\": 138,\n      \"quantity_received\": 0,\n      \"quantity_in_transit\": 138,\n      \"quantity_unshipped\": 4397,\n      \"quantity_unreceived\": 4535,\n      \"buckets\": [\n        \"in_transit\",\n        \"awaiting_shipment\"\n      ]\n    },\n    {\n      \"line_id\": 4107,\n      \"transfer_id\": 98,\n      \"transfer_date\": \"2026-08-03T00:00:00.000000Z\",\n      \"warehouse_transfer_number\": \"WH-0098\",\n      \"transfer_status\": \"open\",\n      \"shipment_status\": \"shipped\",\n      \"receipt_status\": \"partially_received\",\n      \"from_warehouse_id\": 1,\n      \"from_warehouse\": \"Main Warehouse\",\n      \"to_warehouse_id\": 8,\n      \"to_warehouse\": \"East Coast 3PL\",\n      \"to_warehouse_type\": \"3pl\",\n      \"quantity\": 200,\n      \"quantity_cancelled\": 0,\n      \"quantity_shipped\": 200,\n      \"quantity_received\": 50,\n      \"quantity_in_transit\": 150,\n      \"quantity_unshipped\": 0,\n      \"quantity_unreceived\": 150,\n      \"buckets\": [\n        \"in_transit\",\n        \"received\"\n      ]\n    },\n    {\n      \"line_id\": 4114,\n      \"transfer_id\": 91,\n      \"transfer_date\": \"2026-07-20T00:00:00.000000Z\",\n      \"warehouse_transfer_number\": \"WH-0091\",\n      \"transfer_status\": \"open\",\n      \"shipment_status\": \"shipped\",\n      \"receipt_status\": \"unreceived\",\n      \"from_warehouse_id\": 1,\n      \"from_warehouse\": \"Main Warehouse\",\n      \"to_warehouse_id\": 49,\n      \"to_warehouse\": \"FBA US\",\n      \"to_warehouse_type\": \"amazon_fba\",\n      \"quantity\": 120,\n      \"quantity_cancelled\": 0,\n      \"quantity_shipped\": 120,\n      \"quantity_received\": 45,\n      \"quantity_in_transit\": 75,\n      \"quantity_unshipped\": 0,\n      \"quantity_unreceived\": 75,\n      \"buckets\": [\n        \"in_transit\",\n        \"received\"\n      ]\n    }\n  ]\n}"}]},{"name":"Get Product Holds","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/holds","host":["{{baseUrl}}"],"path":["api","v2","products",":product","holds"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get inventory holds for a product.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/holds","host":["{{baseUrl}}"],"path":["api","v2","products",":product","holds"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3,\n      \"quantity\": 5,\n      \"reason\": \"Quality check\",\n      \"warehouse_id\": 1,\n      \"created_at\": \"2024-05-30T08:00:00Z\"\n    }\n  ]\n}"}]},{"name":"Get Product Reservation Integrity","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/reservation-integrity","host":["{{baseUrl}}"],"path":["api","v2","products",":product","reservation-integrity"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get reservation integrity issues for a product.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/reservation-integrity","host":["{{baseUrl}}"],"path":["api","v2","products",":product","reservation-integrity"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"issues\": [],\n    \"total_issues\": 0\n  }\n}"}]},{"name":"Get Product Active Reservations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/active-reservations","host":["{{baseUrl}}"],"path":["api","v2","products",":product","active-reservations"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get active (outstanding) inventory reservations for a product.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/active-reservations","host":["{{baseUrl}}"],"path":["api","v2","products",":product","active-reservations"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"type\": \"sales_order\",\n      \"quantity\": 10,\n      \"reference\": \"SO-0042\",\n      \"warehouse_id\": 1\n    }\n  ]\n}"}]},{"name":"Get Product Pricing Sync Profile","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/pricing-sync-profile","host":["{{baseUrl}}"],"path":["api","v2","products",":product","pricing-sync-profile"],"variable":[{"key":"product","value":"915","description":"Product ID"}]},"description":"The product's own pricing sync profile and, for every listing of the product, the profile that actually governs it on that channel. A listing's own profile wins over the product's, which wins over the channel default; a channel with no profile at any level pushes with its inline settings.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequires the `products:read` scope for API tokens.\n\nResponse `data`:\n- pricing_sync_profile_id (integer|null): the product-level profile\n- profile (object|null): that profile as `{id, name}`\n- channels (array): one entry per listing of the product —\n  - integration_instance_id, channel_name (the channel as named in SKU.io, e.g. `AMZ partusa`), integration_name (the integration the channel runs on, e.g. `Amazon`), product_listing_id, listing_sku\n  - effective_profile (object|null): `{id, name}` of the profile in force, null for inline settings\n  - source (string): where it came from — `listing`, `product`, `channel` or `inline`\n  - not_priced (boolean): no level in the governing sequence has a price for this product, so nothing is sent for that listing\n  - note (string|null): the explanation when not_priced is true"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/pricing-sync-profile","host":["{{baseUrl}}"],"path":["api","v2","products",":product","pricing-sync-profile"],"variable":[{"key":"product","value":"915","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"pricing_sync_profile_id\": 2,\n    \"profile\": {\n      \"id\": 2,\n      \"name\": \"MSRP ceiling\"\n    },\n    \"channels\": [\n      {\n        \"integration_instance_id\": 12,\n        \"channel_name\": \"AMZ partusa\",\n        \"integration_name\": \"Amazon\",\n        \"product_listing_id\": 4821,\n        \"listing_sku\": \"PT-4471-FBA\",\n        \"effective_profile\": {\n          \"id\": 2,\n          \"name\": \"MSRP ceiling\"\n        },\n        \"source\": \"product\",\n        \"not_priced\": false,\n        \"note\": null\n      },\n      {\n        \"integration_instance_id\": 12,\n        \"channel_name\": \"AMZ partusa\",\n        \"integration_name\": \"Amazon\",\n        \"product_listing_id\": 4822,\n        \"listing_sku\": \"PT-4471-MFN\",\n        \"effective_profile\": {\n          \"id\": 3,\n          \"name\": \"MAP protected\"\n        },\n        \"source\": \"listing\",\n        \"not_priced\": false,\n        \"note\": null\n      },\n      {\n        \"integration_instance_id\": 7,\n        \"channel_name\": \"Shopify partusa.com\",\n        \"integration_name\": \"Shopify\",\n        \"product_listing_id\": 3310,\n        \"listing_sku\": \"PT-4471\",\n        \"effective_profile\": {\n          \"id\": 2,\n          \"name\": \"MSRP ceiling\"\n        },\n        \"source\": \"product\",\n        \"not_priced\": true,\n        \"note\": \"Retail has no price for this product on Shopify partusa.com → Not priced\"\n      },\n      {\n        \"integration_instance_id\": 9,\n        \"channel_name\": \"Walmart partusa\",\n        \"integration_name\": \"Walmart\",\n        \"product_listing_id\": 3891,\n        \"listing_sku\": \"PT-4471\",\n        \"effective_profile\": null,\n        \"source\": \"inline\",\n        \"not_priced\": false,\n        \"note\": null\n      }\n    ]\n  }\n}"}]},{"name":"Bulk Assign Product Pricing Sync Profile","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/bulk/pricing-sync-profile","host":["{{baseUrl}}"],"path":["api","v2","products","bulk","pricing-sync-profile"]},"description":"Set — or clear — the product-level pricing sync profile on many products at once, by ID list or by the same `filter[...]` query List Products accepts (send the filter keys without the `filter[]` wrapper). Exactly one of the two must be sent.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAn explicit selection of up to 200 products is applied immediately (`mode: \"sync\"`, HTTP 200) and the affected listings — those of the changed products that carry no profile of their own — are re-priced before the response returns. A larger selection, or any filter scope, runs as a background job (`mode: \"job\"`, HTTP 202); the filter is resolved to a fixed list of product IDs when the job is queued.\n\nAuthentication: Requires Bearer token.\n\nRequires the `products.update` permission (API tokens: `products:write` scope).\n\nRequest body:\n- pricing_sync_profile_id (integer|null, required — the key must be present): the profile to apply, or null to clear. An archived profile is refused (422).\n- product_ids (array of integers, optional): the products to update.\n- filters (object, optional): the product list filters to resolve into products.\n\nResponse `data` when applied immediately (`mode: \"sync\"`):\n- assigned (integer): products whose profile changed\n- unchanged (integer): products that already had this profile\n- not_priced (integer): of the re-priced listings, how many have no price at any level of the new profile's sequence\n- not_sku_mastered (integer): of the re-priced listings, how many are not mastered by SKU.io and so are never pushed\n- not_found (integer): requested IDs that are not products\n\nResponse `data` when queued (`mode: \"job\"`):\n- tracked_job_log_id (integer): the background job to follow\n- total (integer): products the job will process","body":{"mode":"raw","raw":"{\n  \"pricing_sync_profile_id\": 2,\n  \"product_ids\": [\n    915,\n    916,\n    917\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/products/bulk/pricing-sync-profile","host":["{{baseUrl}}"],"path":["api","v2","products","bulk","pricing-sync-profile"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"mode\": \"sync\",\n    \"assigned\": 3,\n    \"unchanged\": 0,\n    \"not_priced\": 1,\n    \"not_sku_mastered\": 0,\n    \"not_found\": 0\n  },\n  \"message\": \"Profile applied to 3 products. 1 have no price at its levels.\"\n}"}]},{"name":"Get Product Unlotted Stock","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/unlotted-stock","host":["{{baseUrl}}"],"path":["api","v2","products",":product","unlotted-stock"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"On-hand stock for a product that has no lot identity yet — open cost layers with no batch number and no expiry date, summed per stock-holding warehouse. This is stock received before lot tracking was switched on for the product; it still fulfils normally but carries nothing for FEFO, near-expiry alerts or recall tracing until it is backfilled.\n\nUse it to decide whether a backfill is needed, then download a pre-filled CSV with GET /api/inventory-expiry/lot-backfill/template?product_id={product} and upload it to POST /api/inventory-expiry/lot-backfill. Once every layer carries a lot, total_quantity is 0 and warehouses is empty.\n\nReported for products that are not lot-tracked yet too (is_lot_tracked: false) — there it is simply all of the product's stock, which is the amount that will be left without a lot if tracking is enabled now.\n\nSupplier and archived warehouses are excluded. Quantities are in the product's stock unit.\n\nAuthentication: Requires Bearer token. Scope: products:read.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/unlotted-stock","host":["{{baseUrl}}"],"path":["api","v2","products",":product","unlotted-stock"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product_id\": 42,\n    \"sku\": \"POWDER-1KG\",\n    \"is_lot_tracked\": true,\n    \"total_quantity\": 1012.5,\n    \"layer_count\": 3,\n    \"warehouses\": [\n      {\n        \"warehouse_id\": 1,\n        \"warehouse_name\": \"Main Warehouse\",\n        \"quantity\": 1000,\n        \"layer_count\": 2\n      },\n      {\n        \"warehouse_id\": 4,\n        \"warehouse_name\": \"Overflow Warehouse\",\n        \"quantity\": 12.5,\n        \"layer_count\": 1\n      }\n    ]\n  }\n}"}]},{"name":"Get Product Inbound Shipment Lines","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/inbound-shipment-lines","host":["{{baseUrl}}"],"path":["api","v2","products",":product","inbound-shipment-lines"],"variable":[{"key":"product","value":"42","description":"Product ID"}]},"description":"Get every inbound shipment line for a product with where the shipment stands: expected, received and what is still to come.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nEach line carries a `buckets` array naming the states it is in:\n- `in_transit` — shipment has left the supplier (in transit or partially received), units not yet received\n- `planned` — shipment is planned but has not left the supplier yet\n- `received` — anything received, whatever the shipment's status\n\nA partially received line is in both `in_transit` and `received`. Closed shipments never count as in transit; draft shipments, cancelled shipments that received nothing, and lines in no bucket are omitted.\n\n`unreceived_quantity` is expected less received, floored at zero — still on the way for a shipped line, still to ship for a planned one. `supplier_id` and `supplier_name` come from the shipment when it names a supplier, otherwise from the line's purchase order. `created_from_receipt` marks a shipment that was generated automatically to wrap a receipt recorded directly against a purchase order. `carrier` and `tracking_number` are the shipment's primary tracking; `additional_tracking` lists any further carrier/tracking pairs (for example one per container).\n\nThe top-level `uses_inbound_shipments` flag is true when the account has ever created an inbound shipment itself (automatically generated ones do not count), so a client can tell whether inbound shipments are in use at all.\n\nEach row is one shipment LINE, identified by `line_id` — a shipment can carry more than one line for the same product. Results are ordered newest shipment first."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/inbound-shipment-lines","host":["{{baseUrl}}"],"path":["api","v2","products",":product","inbound-shipment-lines"],"variable":[{"key":"product","value":"42","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"line_id\": 911,\n      \"shipment_id\": 72,\n      \"shipment_number\": \"IS-00072\",\n      \"shipment_status\": \"in_transit\",\n      \"shipment_status_label\": \"In Transit\",\n      \"created_from_receipt\": false,\n      \"late_shipment\": false,\n      \"po_id\": 72,\n      \"po_number\": \"PO-72\",\n      \"supplier_id\": 5,\n      \"supplier_name\": \"Shenzhen Idea Light Limited\",\n      \"destination_warehouse_id\": 3,\n      \"destination_warehouse\": \"ShipPro - US (New York)\",\n      \"carrier\": \"AML - ALS JUNO\",\n      \"tracking_number\": \"OOCU0855120\",\n      \"additional_tracking\": [\n        {\n          \"carrier\": \"AML\",\n          \"tracking_number\": \"TCLU1741881\"\n        }\n      ],\n      \"eta\": \"2026-09-11T00:00:00.000000Z\",\n      \"shipped_at\": \"2026-08-20T09:15:00.000000Z\",\n      \"arrived_at\": null,\n      \"expected_quantity\": 50,\n      \"received_quantity\": 0,\n      \"unreceived_quantity\": 50,\n      \"buckets\": [\n        \"in_transit\"\n      ]\n    },\n    {\n      \"line_id\": 880,\n      \"shipment_id\": 68,\n      \"shipment_number\": \"IS-00068\",\n      \"shipment_status\": \"partially_received\",\n      \"shipment_status_label\": \"Partially Received\",\n      \"created_from_receipt\": false,\n      \"late_shipment\": true,\n      \"po_id\": 64,\n      \"po_number\": \"PO-64\",\n      \"supplier_id\": 5,\n      \"supplier_name\": \"Shenzhen Idea Light Limited\",\n      \"destination_warehouse_id\": 3,\n      \"destination_warehouse\": \"ShipPro - US (New York)\",\n      \"carrier\": \"AML\",\n      \"tracking_number\": \"FFAU6161263\",\n      \"additional_tracking\": [],\n      \"eta\": \"2026-08-30T00:00:00.000000Z\",\n      \"shipped_at\": \"2026-08-01T14:02:00.000000Z\",\n      \"arrived_at\": null,\n      \"expected_quantity\": 100,\n      \"received_quantity\": 60,\n      \"unreceived_quantity\": 40,\n      \"buckets\": [\n        \"in_transit\",\n        \"received\"\n      ]\n    },\n    {\n      \"line_id\": 812,\n      \"shipment_id\": 61,\n      \"shipment_number\": \"IS-00061\",\n      \"shipment_status\": \"received\",\n      \"shipment_status_label\": \"Received\",\n      \"created_from_receipt\": false,\n      \"late_shipment\": false,\n      \"po_id\": 58,\n      \"po_number\": \"PO-58\",\n      \"supplier_id\": 5,\n      \"supplier_name\": \"Shenzhen Idea Light Limited\",\n      \"destination_warehouse_id\": 3,\n      \"destination_warehouse\": \"ShipPro - US (New York)\",\n      \"carrier\": \"AML\",\n      \"tracking_number\": \"FSCU8958205\",\n      \"additional_tracking\": [],\n      \"eta\": \"2026-07-15T00:00:00.000000Z\",\n      \"shipped_at\": \"2026-06-28T08:00:00.000000Z\",\n      \"arrived_at\": \"2026-07-14T00:00:00.000000Z\",\n      \"expected_quantity\": 150,\n      \"received_quantity\": 150,\n      \"unreceived_quantity\": 0,\n      \"buckets\": [\n        \"received\"\n      ]\n    }\n  ],\n  \"uses_inbound_shipments\": true\n}"}]},{"name":"Get Bulk Edit Metadata","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/bulk-edit-metadata","host":["{{baseUrl}}"],"path":["api","v2","products","bulk-edit-metadata"]},"description":"Returns the dynamic field definitions available to bulk product edits: product attributes (with their type, display configuration, validation rules and configured options), product pricing tiers, supplier pricing tiers, and suppliers.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach attribute includes:\n- `type` — stored data type: `string`, `longtext`, `numeric`, `integer`, `date`, `datetime`, `checkbox`\n- `has_options` — whether values are restricted to a configured option list\n- `options` — the configured option values as plain strings\n- `attribute_options` — the same options with per-option swatch data (`value`, `swatch_hex`, `swatch_image_url`, `sort_order`)\n- `display_options` — `has_options`, `type` (`Single` or `Multi`), `option_display_type` (e.g. `swatch`), `allow_custom_value`, `is_html`, `is_link`, `sort_order`\n- `validation` — optional constraints: `limit` (max characters), `precision` (decimal places), `min`, `max`\n- `group_name` — the attribute group the attribute belongs to, or `Ungrouped`\n\nMulti-value attributes (`display_options.type` = `Multi`) hold every selected value in a single newline-separated string. Send them back the same way when setting a value.\n\nArchived attributes are excluded. Attributes are ordered by name.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/bulk-edit-metadata","host":["{{baseUrl}}"],"path":["api","v2","products","bulk-edit-metadata"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"attributes\": [\n      {\n        \"id\": 1,\n        \"name\": \"Color\",\n        \"type\": \"string\",\n        \"has_options\": true,\n        \"options\": [\n          \"Red\",\n          \"Blue\",\n          \"Green\"\n        ],\n        \"group_name\": \"Physical\",\n        \"display_options\": {\n          \"has_options\": true,\n          \"type\": \"Single\",\n          \"option_display_type\": \"swatch\",\n          \"allow_custom_value\": false,\n          \"is_html\": false,\n          \"is_link\": false,\n          \"sort_order\": 1\n        },\n        \"validation\": {\n          \"limit\": 60,\n          \"precision\": null,\n          \"min\": null,\n          \"max\": null\n        },\n        \"attribute_options\": [\n          {\n            \"value\": \"Red\",\n            \"swatch_hex\": \"#d32f2f\",\n            \"swatch_image_url\": null,\n            \"sort_order\": 1\n          },\n          {\n            \"value\": \"Blue\",\n            \"swatch_hex\": \"#1976d2\",\n            \"swatch_image_url\": null,\n            \"sort_order\": 2\n          },\n          {\n            \"value\": \"Green\",\n            \"swatch_hex\": \"#388e3c\",\n            \"swatch_image_url\": null,\n            \"sort_order\": 3\n          }\n        ]\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Materials\",\n        \"type\": \"string\",\n        \"has_options\": true,\n        \"options\": [\n          \"Cotton\",\n          \"Polyester\",\n          \"Wool\"\n        ],\n        \"group_name\": \"Physical\",\n        \"display_options\": {\n          \"has_options\": true,\n          \"type\": \"Multi\",\n          \"option_display_type\": \"dropdown\",\n          \"allow_custom_value\": true,\n          \"is_html\": false,\n          \"is_link\": false,\n          \"sort_order\": 2\n        },\n        \"validation\": null,\n        \"attribute_options\": [\n          {\n            \"value\": \"Cotton\",\n            \"swatch_hex\": null,\n            \"swatch_image_url\": null,\n            \"sort_order\": 1\n          },\n          {\n            \"value\": \"Polyester\",\n            \"swatch_hex\": null,\n            \"swatch_image_url\": null,\n            \"sort_order\": 2\n          },\n          {\n            \"value\": \"Wool\",\n            \"swatch_hex\": null,\n            \"swatch_image_url\": null,\n            \"sort_order\": 3\n          }\n        ]\n      },\n      {\n        \"id\": 3,\n        \"name\": \"Care Instructions\",\n        \"type\": \"longtext\",\n        \"has_options\": false,\n        \"options\": [],\n        \"group_name\": \"Ungrouped\",\n        \"display_options\": {\n          \"has_options\": false,\n          \"is_html\": true,\n          \"is_link\": false,\n          \"sort_order\": 0\n        },\n        \"validation\": {\n          \"limit\": 5000,\n          \"precision\": null,\n          \"min\": null,\n          \"max\": null\n        },\n        \"attribute_options\": []\n      },\n      {\n        \"id\": 4,\n        \"name\": \"Net Weight (kg)\",\n        \"type\": \"numeric\",\n        \"has_options\": false,\n        \"options\": [],\n        \"group_name\": \"Shipping\",\n        \"display_options\": {\n          \"has_options\": false,\n          \"is_html\": false,\n          \"is_link\": false,\n          \"sort_order\": 0\n        },\n        \"validation\": {\n          \"limit\": null,\n          \"precision\": 2,\n          \"min\": 0,\n          \"max\": 500\n        },\n        \"attribute_options\": []\n      }\n    ],\n    \"pricing_tiers\": [\n      {\n        \"id\": 1,\n        \"name\": \"Retail\",\n        \"is_default\": true\n      },\n      {\n        \"id\": 2,\n        \"name\": \"Wholesale\",\n        \"is_default\": false\n      }\n    ],\n    \"supplier_pricing_tiers\": [\n      {\n        \"id\": 1,\n        \"name\": \"Default\",\n        \"is_default\": true,\n        \"currency_code\": \"USD\"\n      }\n    ],\n    \"suppliers\": [\n      {\n        \"id\": 3,\n        \"name\": \"Global Supplies Ltd\"\n      }\n    ]\n  }\n}"}]},{"name":"Fetch Products for Bulk Edit","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/bulk-edit-fetch?ids=42,43,44","host":["{{baseUrl}}"],"path":["api","v2","products","bulk-edit-fetch"],"query":[{"key":"ids","value":"42,43,44","description":"Required. Comma-separated product IDs to fetch for editing","disabled":true}]},"description":"Fetch products with all editable fields loaded for the bulk edit page.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nIncludes basic info, shipping dimensions, costing, accounting nominal codes, tax, attributes, pricing, suppliers, and supplier pricing (per supplier × tier).\n\nEach product also carries its lot tracking state: is_lot_tracked (boolean) and lot_tracking_method (fifo | fefo | null)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/bulk-edit-fetch?ids=42,43,44","host":["{{baseUrl}}"],"path":["api","v2","products","bulk-edit-fetch"],"query":[{"key":"ids","value":"42,43,44","description":"Required. Comma-separated product IDs to fetch for editing","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 42,\n      \"sku\": \"WDG-001\",\n      \"name\": \"Blue Widget\",\n      \"type\": \"standard\",\n      \"barcode\": 123456789012,\n      \"mpn\": \"BW-001\",\n      \"hts_code\": \"8471.30.0100\",\n      \"country_of_origin\": \"US\",\n      \"unit_cost\": 5.5,\n      \"is_dropshippable\": false,\n      \"brand_id\": 1,\n      \"brand_name\": \"Acme\",\n      \"weight\": 0.5,\n      \"weight_unit\": \"kg\",\n      \"attributes\": [\n        {\n          \"attribute_id\": 1,\n          \"attribute_name\": \"Color\",\n          \"value\": \"Blue\"\n        }\n      ],\n      \"pricing\": [\n        {\n          \"tier_id\": 1,\n          \"tier_name\": \"Retail\",\n          \"price\": 12.99\n        }\n      ],\n      \"suppliers\": [\n        {\n          \"supplier_id\": 3,\n          \"supplier_name\": \"Global Supplies Ltd\",\n          \"is_default\": true\n        }\n      ],\n      \"supplier_pricing\": [\n        {\n          \"supplier_product_id\": 11,\n          \"supplier_id\": 3,\n          \"supplier_name\": \"Global Supplies Ltd\",\n          \"tier_id\": 1,\n          \"tier_name\": \"Default\",\n          \"price\": 4.5,\n          \"currency_code\": \"USD\"\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"Bulk Edit Products (Apply to All)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/bulk-edit","host":["{{baseUrl}}"],"path":["api","v2","products","bulk-edit"]},"description":"Apply the same set of changes to all selected products.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- product_ids (required, array, max 500): Product IDs to update\n- changes (required, array): List of changes, each with:\n  - field (required, string): Field name to change. Supports scalar product fields (e.g. weight, weight_unit, unit_cost, brand_id, product_type_id — 'set' with an active leaf product type id, or 'clear'), 'pricing' (customer pricing tiers), 'supplier_pricing' (per-supplier pricing tiers — applies to ALL suppliers of each product), 'attribute' (custom attributes), and 'supplier' (link suppliers).\n  - operation (required, string): Operation type. Scalars use 'set' / 'clear'; boolean fields (is_dropshippable, is_taxable, is_lot_tracked) use 'set_true' / 'set_false'. Numeric/pricing/supplier_pricing also accept 'increase_by_amount', 'decrease_by_amount', 'increase_by_percent', 'decrease_by_percent'.\n  - value (nullable): New value\n  - tier_id (nullable, integer): Required for 'pricing' (references product_pricing_tiers) and 'supplier_pricing' (references supplier_pricing_tiers). Validation routes to the correct table based on the sibling 'field' value.\n  - attribute_id (nullable, integer): Required for attribute field changes\n  - supplier_id (nullable, integer): Required for supplier field changes\n\nLot tracking:\n- is_lot_tracked with 'set_true' switches lot tracking on for every selected product. A product that has no lot_tracking_method yet receives the tenant default from the Serial & Lot Tracking settings; a method the product already has is kept.\n- lot_tracking_method with 'set' and value fifo | fefo changes the consumption order; 'clear' removes it. Any other value returns 422.\n\n**Requires permission:** `products.update`","body":{"mode":"raw","raw":"{\n  \"product_ids\": [\n    42,\n    43,\n    44\n  ],\n  \"changes\": [\n    {\n      \"field\": \"weight\",\n      \"operation\": \"set\",\n      \"value\": 0.6\n    },\n    {\n      \"field\": \"weight_unit\",\n      \"operation\": \"set\",\n      \"value\": \"kg\"\n    },\n    {\n      \"field\": \"product_type_id\",\n      \"operation\": \"set\",\n      \"value\": 7\n    },\n    {\n      \"field\": \"pricing\",\n      \"operation\": \"set\",\n      \"tier_id\": 1,\n      \"value\": 14.99\n    },\n    {\n      \"field\": \"supplier_pricing\",\n      \"operation\": \"increase_by_percent\",\n      \"tier_id\": 1,\n      \"value\": 5\n    },\n    {\n      \"field\": \"attribute\",\n      \"operation\": \"set\",\n      \"attribute_id\": 1,\n      \"value\": \"Blue\"\n    },\n    {\n      \"field\": \"supplier\",\n      \"operation\": \"add\",\n      \"supplier_id\": 7\n    },\n    {\n      \"field\": \"supplier_sku\",\n      \"operation\": \"set\",\n      \"supplier_id\": 7,\n      \"value\": \"VENDOR-PART-123\"\n    },\n    {\n      \"field\": \"is_lot_tracked\",\n      \"operation\": \"set_true\"\n    },\n    {\n      \"field\": \"lot_tracking_method\",\n      \"operation\": \"set\",\n      \"value\": \"fefo\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/products/bulk-edit","host":["{{baseUrl}}"],"path":["api","v2","products","bulk-edit"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"updated\": 3,\n    \"failed\": 0\n  }\n}"}]},{"name":"Bulk Edit Products (Individual)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/bulk-edit-individual","host":["{{baseUrl}}"],"path":["api","v2","products","bulk-edit-individual"]},"description":"Apply individual per-product changes.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nMax 100 products per request. Each product's changes object supports:\n- Basic: name, brand_id, product_type_id (an active leaf product type; null clears it), is_dropshippable, barcode, mpn, hts_code, country_of_origin\n- Shipping: weight, weight_unit, length, width, height, dimension_unit, case_quantity, case_weight, case_weight_unit, case_length, case_width, case_height, case_dimension_unit, fba_prep_instructions\n- Costing: unit_cost, proforma_shipping_cost, proforma_landed_cost_percentage, proforma_marketplace_cost_percentage\n- Accounting: sales_nominal_code_id, cogs_nominal_code_id, inventory_nominal_code_id, inventory_in_transit_nominal_code_id, accrued_purchases_nominal_code_id, discount_nominal_code_id\n- Tax: is_taxable, default_tax_rate_id\n- Taxonomy: primary_category_id, tags_add (array), tags_remove (array)\n- Inventory: min_stock_level, is_lot_tracked (boolean), lot_tracking_method (fifo | fefo). Setting is_lot_tracked true on a product with no method fills in the tenant default method.\n- Pricing: pricing[].tier_id + pricing[].price\n- Attributes: attributes[].attribute_id + attributes[].value. Send an empty string or null as the value to clear the attribute on that product. Multi-value attributes are newline-separated within a single value string.\n- Suppliers: default_supplier_id (marks that supplier as the product's default; the supplier link is created if it does not exist yet), supplier_skus[].supplier_id + supplier_skus[].supplier_sku","body":{"mode":"raw","raw":"{\n  \"products\": [\n    {\n      \"id\": 42,\n      \"changes\": {\n        \"name\": \"Blue Widget Pro\",\n        \"product_type_id\": 7,\n        \"weight\": 0.6,\n        \"weight_unit\": \"kg\",\n        \"unit_cost\": 5.75,\n        \"is_dropshippable\": false,\n        \"min_stock_level\": 100,\n        \"pricing\": [\n          {\n            \"tier_id\": 1,\n            \"price\": 13.99\n          }\n        ],\n        \"attributes\": [\n          {\n            \"attribute_id\": 1,\n            \"value\": \"Blue\"\n          }\n        ],\n        \"default_supplier_id\": 7,\n        \"supplier_skus\": [\n          {\n            \"supplier_id\": 7,\n            \"supplier_sku\": \"VENDOR-PART-123\"\n          }\n        ],\n        \"tags_add\": [\n          \"new-tag\"\n        ],\n        \"tags_remove\": [\n          \"old-tag\"\n        ],\n        \"is_lot_tracked\": true,\n        \"lot_tracking_method\": \"fefo\"\n      }\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/products/bulk-edit-individual","host":["{{baseUrl}}"],"path":["api","v2","products","bulk-edit-individual"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"updated\": 1,\n    \"failed\": 0\n  }\n}"}]},{"name":"Preview Bulk Edit","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/bulk-edit-preview","host":["{{baseUrl}}"],"path":["api","v2","products","bulk-edit-preview"]},"description":"Preview bulk edit changes without applying them (dry run). Safe to call repeatedly.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFor mode='apply_to_all': requires product_ids and changes.\nFor mode='individual': requires products[].id and products[].changes. The individual preview accepts the same changes object as POST /api/v2/products/bulk-edit-individual and reports a diff row for every field that would actually change, including:\n- attributes[] - reported as field 'attribute.{attribute_id}' with field_label 'Attribute: {Group}: {Name}' and operation 'set' or 'clear'. Setting an attribute to its current value, or clearing one the product does not have, is treated as a no-op and is omitted from the diff.\n- default_supplier_id - reported as field 'default_supplier' with field_label 'Default Supplier' and operation 'set_default'; old_value and new_value are supplier names.","body":{"mode":"raw","raw":"{\n  \"mode\": \"apply_to_all\",\n  \"product_ids\": [\n    42,\n    43\n  ],\n  \"changes\": [\n    {\n      \"field\": \"weight\",\n      \"operation\": \"set\",\n      \"value\": 0.6\n    },\n    {\n      \"field\": \"supplier_pricing\",\n      \"operation\": \"set\",\n      \"tier_id\": 1,\n      \"value\": 4.75\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/products/bulk-edit-preview","host":["{{baseUrl}}"],"path":["api","v2","products","bulk-edit-preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 42,\n      \"sku\": \"WDG-001\",\n      \"changes\": [\n        {\n          \"field\": \"weight\",\n          \"field_label\": \"Weight\",\n          \"old_value\": 0.5,\n          \"new_value\": 0.6\n        },\n        {\n          \"field\": \"supplier_pricing.1.3\",\n          \"field_label\": \"Supplier Price: Global Supplies Ltd — Default\",\n          \"old_value\": 4.5,\n          \"new_value\": 4.75\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"List Bundle Workshop Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/bundle-workshop?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","v2","products","bundle-workshop"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true}]},"description":"List all bundle/kit products with component counts and configuration status.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/bundle-workshop?page=1&per_page=10","host":["{{baseUrl}}"],"path":["api","v2","products","bundle-workshop"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 5,\n      \"sku\": \"BUNDLE-001\",\n      \"name\": \"Widget Bundle\",\n      \"type\": \"bundle\",\n      \"image_url\": null,\n      \"components_count\": 2,\n      \"is_configured\": true\n    }\n  ],\n  \"total\": 12,\n  \"per_page\": 10\n}"}]},{"name":"Auto-Detect Bundle Candidates","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/bundle-workshop/auto-detect?filter[confidence]=high&filter[mode]=suggestions&page=1&per_page=20","host":["{{baseUrl}}"],"path":["api","v2","products","bundle-workshop","auto-detect"],"query":[{"key":"filter[confidence]","value":"high","description":"Confidence filter: all, high, medium, low","disabled":true},{"key":"filter[mode]","value":"suggestions","description":"Mode: suggestions (pattern-matched only) or all (all standard products)","disabled":true},{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"20","description":"Results per page (default 10).","disabled":true}]},"description":"Auto-detect standard products that may be bundles/kits based on naming patterns.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns products with detected_pattern, suggested_type, confidence, has_inventory_movements, and components_count (number of components already configured on the product — used by the conversion queue to know whether component configuration is required before converting to bundle/kit)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/bundle-workshop/auto-detect?filter[confidence]=high&filter[mode]=suggestions&page=1&per_page=20","host":["{{baseUrl}}"],"path":["api","v2","products","bundle-workshop","auto-detect"],"query":[{"key":"filter[confidence]","value":"high","description":"Confidence filter: all, high, medium, low","disabled":true},{"key":"filter[mode]","value":"suggestions","description":"Mode: suggestions (pattern-matched only) or all (all standard products)","disabled":true},{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"20","description":"Results per page (default 10).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 100,\n      \"sku\": \"BUNDLE-KIT-001\",\n      \"name\": \"Widget Bundle Kit\",\n      \"type\": \"standard\",\n      \"image_url\": null,\n      \"detected_pattern\": \"bundle_suffix\",\n      \"suggested_type\": \"bundle\",\n      \"confidence\": \"high\",\n      \"has_inventory_movements\": false,\n      \"components_count\": 0\n    }\n  ],\n  \"total\": 8,\n  \"per_page\": 20\n}"}]},{"name":"Batch Convert Products to Bundle/Kit","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/bundle-workshop/batch-convert","host":["{{baseUrl}}"],"path":["api","v2","products","bundle-workshop","batch-convert"]},"description":"Batch convert standard products to bundle/kit types.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- products (required, array, min 1): Products to convert\n  - id (required, integer): Product ID\n  - type (required, string): Target type — bundle, kit, or standard\n  - components (optional, array): Components to save on the product BEFORE converting. Only applies when type is bundle or kit. Allows converting a product that currently has zero components in a single atomic call.\n    - id (required, integer, must exist in products): Component product ID. Components must be standard or kit products (a bundle cannot be a component).\n    - quantity (required, numeric, min 1): Quantity of this component per parent unit.\n\n**Component handling:** When `components` is provided for a bundle/kit target, they are saved first (via the bundle/kit component setter for the target type), then the type conversion proceeds. If the component save fails (e.g. a component is itself a bundle, a kit component references itself, or a circular kit reference is detected), the item is skipped with the validation message as `reason` and the product type is left unchanged.\n\n**Skip rules — returned in `data.skipped[]`:**\n- Converting to `bundle` when the source product has prior sale/assembly inventory movements (cannot become a bundle once it has movement history).\n- Converting to `bundle` or `kit` when the source product has zero components AND no `components` were provided in the request (you must add components before promoting it).\n- Provided `components` failed validation (invalid component type, self/circular reference) — the validation message is returned as the skip reason.\n\nThe response payload includes `data.converted_count` for successful conversions and `data.skipped[]` for entries that were skipped, each with `id`, `sku`, and a human-readable `reason`.","body":{"mode":"raw","raw":"{\n  \"products\": [\n    {\n      \"id\": 100,\n      \"type\": \"bundle\",\n      \"components\": [\n        {\n          \"id\": 200,\n          \"quantity\": 2\n        },\n        {\n          \"id\": 201,\n          \"quantity\": 1\n        }\n      ]\n    },\n    {\n      \"id\": 101,\n      \"type\": \"kit\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/products/bundle-workshop/batch-convert","host":["{{baseUrl}}"],"path":["api","v2","products","bundle-workshop","batch-convert"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"converted_count\": 2,\n    \"skipped\": [\n      {\n        \"id\": 102,\n        \"sku\": \"WIDGET-PACK\",\n        \"reason\": \"No components — add components before converting to bundle\"\n      }\n    ]\n  }\n}"}]},{"name":"Check Bundle Restrictions","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/bundle-workshop/check-restrictions","host":["{{baseUrl}}"],"path":["api","v2","products","bundle-workshop","check-restrictions"]},"description":"Check which products have sale/assembly inventory movements that would restrict type changes.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"product_ids\": [\n    42,\n    43,\n    44\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/products/bundle-workshop/check-restrictions","host":["{{baseUrl}}"],"path":["api","v2","products","bundle-workshop","check-restrictions"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"42\": {\n      \"has_restriction\": false,\n      \"movement_count\": 0\n    },\n    \"43\": {\n      \"has_restriction\": true,\n      \"movement_count\": 5\n    }\n  }\n}"}]},{"name":"Get Bundle Components","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/bundle-workshop/{product}/components","host":["{{baseUrl}}"],"path":["api","v2","products","bundle-workshop",":product","components"],"variable":[{"key":"product","value":"5","description":"Bundle/Kit Product ID"}]},"description":"Get components for a specific bundle/kit product in the Bundle Workshop.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/bundle-workshop/{product}/components","host":["{{baseUrl}}"],"path":["api","v2","products","bundle-workshop",":product","components"],"variable":[{"key":"product","value":"5","description":"Bundle/Kit Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 10,\n      \"sku\": \"COMP-A\",\n      \"name\": \"Component A\",\n      \"quantity\": 2\n    }\n  ]\n}"}]},{"name":"Save Bundle Components","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/bundle-workshop/{product}/components","host":["{{baseUrl}}"],"path":["api","v2","products","bundle-workshop",":product","components"],"variable":[{"key":"product","value":"5","description":"Bundle/Kit Product ID"}]},"description":"Save/sync components for a bundle/kit. Replaces all existing components.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- components (required, array, **min 1 entry**): Component list. Empty arrays are rejected — a bundle/kit must always have at least one component.\n  - id (required, integer): Component product ID\n  - quantity (required, numeric, min 1): Quantity of this component in the bundle\n\nReturns a 422 validation error if `components` is empty or missing.","body":{"mode":"raw","raw":"{\n  \"components\": [\n    {\n      \"id\": 10,\n      \"quantity\": 2\n    },\n    {\n      \"id\": 11,\n      \"quantity\": 1\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/products/bundle-workshop/{product}/components","host":["{{baseUrl}}"],"path":["api","v2","products","bundle-workshop",":product","components"],"variable":[{"key":"product","value":"5","description":"Bundle/Kit Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"components_count\": 2\n  }\n}"}]},{"name":"Get Restricting Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/bundle-workshop/{product}/restricting-movements?page=1&per_page=15","host":["{{baseUrl}}"],"path":["api","v2","products","bundle-workshop",":product","restricting-movements"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"15","description":"Items per page (default: 15)","disabled":true}],"variable":[{"key":"product","value":"5","description":"Product ID"}]},"description":"Get paginated sale/assembly inventory movements that restrict type changes for a product.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/bundle-workshop/{product}/restricting-movements?page=1&per_page=15","host":["{{baseUrl}}"],"path":["api","v2","products","bundle-workshop",":product","restricting-movements"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"15","description":"Items per page (default: 15)","disabled":true}],"variable":[{"key":"product","value":"5","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 2001,\n      \"type\": \"assembly\",\n      \"quantity\": -2,\n      \"reference\": \"ASM-0015\",\n      \"warehouse_name\": \"Main Warehouse\",\n      \"inventory_movement_date\": \"2024-04-10\"\n    }\n  ],\n  \"total\": 5,\n  \"per_page\": 15\n}"}]},{"name":"Update Bundle Type","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/bundle-workshop/{product}/type","host":["{{baseUrl}}"],"path":["api","v2","products","bundle-workshop",":product","type"],"variable":[{"key":"product","value":"5","description":"Bundle/Kit Product ID"}]},"description":"Update the type of an existing bundle/kit product (bundle ↔ kit ↔ standard).\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nOnly bundle or kit products can have their type changed via this endpoint.\n\n**Component requirement:** Converting to `bundle` or `kit` requires the product to already have at least one component. Returns a 422 with `Cannot convert to a bundle or kit without at least one component. Add components first.` when the source product has zero components.","body":{"mode":"raw","raw":"{\n  \"type\": \"kit\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/products/bundle-workshop/{product}/type","host":["{{baseUrl}}"],"path":["api","v2","products","bundle-workshop",":product","type"],"variable":[{"key":"product","value":"5","description":"Bundle/Kit Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"type\": \"kit\"\n  }\n}"}]},{"name":"Import Products","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/import","host":["{{baseUrl}}"],"path":["api","v2","products","import"]},"description":"Imports run as a tracked job — a bulk import can exceed a ~100s edge-proxy limit (Cloudflare 524), so the rows are processed off the request cycle. Returns a tracked_job_log_id; progress and the created/updated/failed counts surface via the tracked job log endpoints.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRows may include `product_type` — the product type's full path (e.g. `Diapering > Cloth diapers`), or just its leaf name when that name is unique among leaf types. A value that matches no type fails the row's validation. The import template includes this column.","body":{"mode":"raw","raw":"{\n  \"products\": [\n    {\n      \"sku\": \"NEW-001\",\n      \"name\": \"New Product\",\n      \"type\": \"standard\",\n      \"product_type\": \"Diapering > Cloth diapers\",\n      \"unit_cost\": 5\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/products/import","host":["{{baseUrl}}"],"path":["api","v2","products","import"]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 5123\n  },\n  \"message\": \"Import queued — track progress in the job tray.\"\n}"}]},{"name":"Download Supplier Link Template","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/import-supplier-links/template","host":["{{baseUrl}}"],"path":["api","v2","products","import-supplier-links","template"]},"description":"Download the supplier link import template XLSX.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nResponse: XLSX file download."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/import-supplier-links/template","host":["{{baseUrl}}"],"path":["api","v2","products","import-supplier-links","template"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary XLSX file)"}]},{"name":"Validate Supplier Link Import","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/import-supplier-links/validate","host":["{{baseUrl}}"],"path":["api","v2","products","import-supplier-links","validate"]},"description":"Validate supplier link import rows and return a per-row preview before importing.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRows whose supplier_name is blank are skipped (products exported without a supplier link) and counted in summary.total_skipped.\n\nA supplier_name that does not match an existing supplier is reported as a warning and the supplier is created during the import. Send create_missing_suppliers=false to have those rows fail validation with an error instead.\n\nRequest fields:\n- rows (required, array, min 1): rows to validate\n  - product_sku (required, string): must match an existing product SKU\n  - supplier_name (required, string): matched case-insensitively; created on import when new\n  - supplier_sku (optional, string): supplier's own SKU for the product. Omit the key entirely to leave an existing value untouched on update\n  - moq (optional, numeric): minimum order quantity, non-negative\n  - leadtime (optional, integer): lead time in days, non-negative\n  - target_stock_days (optional, integer): target days of stock, non-negative\n  - is_default (optional, string): yes / true / 1 / y marks this supplier as the product's default\n  - tier_{pricingTierId} (optional, numeric): price for that supplier pricing tier, e.g. tier_1\n- create_missing_suppliers (optional, boolean, default true): when false, an unknown supplier_name is an error rather than a warning\n\nEach returned link carries errors and warnings arrays; a link is importable when errors is empty. supplier_will_be_created is true when the supplier does not exist yet, in which case supplier is null and the supplier is created during the import. is_update / supplier_product_id are set when the product+supplier combination already exists, so the import updates it instead of creating a duplicate.","body":{"mode":"raw","raw":"{\n  \"rows\": [\n    {\n      \"product_sku\": \"WDG-001\",\n      \"supplier_name\": \"Global Supplies Ltd\",\n      \"supplier_sku\": \"GS-WDG-001\",\n      \"moq\": \"50\",\n      \"leadtime\": \"14\",\n      \"target_stock_days\": \"30\",\n      \"is_default\": \"yes\",\n      \"tier_1\": \"12.50\"\n    },\n    {\n      \"product_sku\": \"WDG-002\",\n      \"supplier_name\": \"Northwind Trading\"\n    }\n  ],\n  \"create_missing_suppliers\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/products/import-supplier-links/validate","host":["{{baseUrl}}"],"path":["api","v2","products","import-supplier-links","validate"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"links\": [\n    {\n      \"row_index\": 0,\n      \"product_sku\": \"WDG-001\",\n      \"product\": {\n        \"id\": 42,\n        \"name\": \"Widget Pro - Blue\"\n      },\n      \"supplier_name\": \"Global Supplies Ltd\",\n      \"supplier\": {\n        \"id\": 3,\n        \"name\": \"Global Supplies Ltd\"\n      },\n      \"supplier_will_be_created\": false,\n      \"supplier_sku\": \"GS-WDG-001\",\n      \"supplier_sku_column_present\": true,\n      \"moq\": 50,\n      \"leadtime\": 14,\n      \"target_stock_days\": 30,\n      \"is_default\": true,\n      \"is_update\": true,\n      \"supplier_product_id\": 918,\n      \"pricing\": [\n        {\n          \"tier_id\": 1,\n          \"tier_name\": \"standard\",\n          \"price\": 12.5\n        }\n      ],\n      \"errors\": [],\n      \"warnings\": [\n        \"This will replace the current default supplier for 'WDG-001'.\"\n      ]\n    },\n    {\n      \"row_index\": 1,\n      \"product_sku\": \"WDG-002\",\n      \"product\": {\n        \"id\": 43,\n        \"name\": \"Widget Pro - Red\"\n      },\n      \"supplier_name\": \"Northwind Trading\",\n      \"supplier\": null,\n      \"supplier_will_be_created\": true,\n      \"supplier_sku\": \"\",\n      \"supplier_sku_column_present\": false,\n      \"moq\": null,\n      \"leadtime\": null,\n      \"target_stock_days\": null,\n      \"is_default\": false,\n      \"is_update\": false,\n      \"supplier_product_id\": null,\n      \"pricing\": [],\n      \"errors\": [],\n      \"warnings\": [\n        \"Supplier 'Northwind Trading' does not exist yet - it will be created.\"\n      ]\n    }\n  ],\n  \"summary\": {\n    \"total_links\": 2,\n    \"total_errors\": 0,\n    \"total_warnings\": 2,\n    \"total_skipped\": 0,\n    \"new_suppliers\": 1\n  }\n}"}]},{"name":"Import Supplier Links","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/import-supplier-links","host":["{{baseUrl}}"],"path":["api","v2","products","import-supplier-links"]},"description":"Import supplier links previewed by the validate endpoint. Send back the links the preview returned, minus any row whose errors array was non-empty.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nsupplier_id may be omitted when the supplier does not exist yet - the supplier is created from supplier_name, once per distinct name across the whole payload, so ten rows naming the same new supplier produce one supplier. Either supplier_id or supplier_name must be present on every link.\n\nA link carrying supplier_product_id updates that existing supplier link; without it a new link is created.\n\nRequest fields:\n- links (required, array, min 1)\n  - product_id (required, integer): must exist\n  - supplier_id (optional, integer): must exist when present; omit for a supplier that will be created\n  - supplier_name (required when supplier_id is absent, string, max 255): name of the supplier to create or match\n  - supplier_product_id (optional, integer): existing supplier link to update; must exist when present\n  - product_sku (optional, string): echoed back in the result summary\n  - supplier_sku (optional, string): supplier's own SKU for the product\n  - supplier_sku_column_present (optional, boolean): false leaves an existing supplier_sku untouched on update\n  - moq (optional, numeric), leadtime (optional, integer), target_stock_days (optional, integer)\n  - is_default (optional, boolean): makes this the product's default supplier\n  - pricing (optional, array): [{ tier_id, price }] per supplier pricing tier\n\nImports run in the background - a bulk import can exceed a ~100s edge-proxy limit (Cloudflare 524), so the rows are processed off the request cycle. The response returns a tracked_job_log_id; poll the tracked job log endpoints for progress and for the created / updated / failed counts plus the names of any suppliers created along the way.","body":{"mode":"raw","raw":"{\n  \"links\": [\n    {\n      \"product_id\": 42,\n      \"supplier_id\": 3,\n      \"supplier_name\": \"Global Supplies Ltd\",\n      \"supplier_product_id\": 918,\n      \"product_sku\": \"WDG-001\",\n      \"supplier_sku\": \"GS-WDG-001\",\n      \"supplier_sku_column_present\": true,\n      \"moq\": 50,\n      \"leadtime\": 14,\n      \"target_stock_days\": 30,\n      \"is_default\": true,\n      \"pricing\": [\n        {\n          \"tier_id\": 1,\n          \"price\": 12.5\n        }\n      ]\n    },\n    {\n      \"product_id\": 43,\n      \"supplier_name\": \"Northwind Trading\",\n      \"product_sku\": \"WDG-002\",\n      \"is_default\": false,\n      \"pricing\": []\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/products/import-supplier-links","host":["{{baseUrl}}"],"path":["api","v2","products","import-supplier-links"]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 5123\n  },\n  \"message\": \"Import queued — track progress in the job tray.\"\n}"}]},{"name":"Download Recipe Import Template","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/import-recipes/template","host":["{{baseUrl}}"],"path":["api","v2","products","import-recipes","template"]},"description":"Download the manufacturing recipe (BOM) import template XLSX.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe workbook has three sheets: **Import Here** (the columns to fill in, with required columns highlighted), **Field Guide** (what every column does, its accepted values and an example), and **Reference Data** (existing recipes and product SKUs from your account).\n\nThe template is one row per component. Repeat `product_sku` and `recipe_name` on every row of the same recipe - rows are grouped by that pair. Leaving `recipe_name` blank names the recipe \"&lt;product_sku> Recipe\".\n\nRe-importing an unchanged recipe does nothing; changing one saves a new version of it; a new `recipe_name` creates a separate recipe, since a product can have several.\n\nValidating and running the import use the shared data import endpoints with the entity type `recipes`.\n\nAuthentication: Requires Bearer token.\n\nResponse: XLSX file download."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/import-recipes/template","host":["{{baseUrl}}"],"path":["api","v2","products","import-recipes","template"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary XLSX file)"}]},{"name":"Export Product List","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/list/export?format=xlsx&scope=filtered&content_mode=summary&columns=id,sku,name,type,inventory_total","host":["{{baseUrl}}"],"path":["api","v2","products","list","export"],"query":[{"key":"format","value":"xlsx","description":"Export format: xlsx (default) or csv","disabled":true},{"key":"scope","value":"filtered","description":"Export scope: filtered (default, uses active filters), all, current_page, selected","disabled":true},{"key":"content_mode","value":"summary","description":"Content mode: summary (default, one row per product) or suppliers (one row per product-supplier link; emits the per-link supplier columns and omits the duplicate product-level Default Supplier columns)","disabled":true},{"key":"columns","value":"id,sku,name,type,inventory_total","description":"Comma-separated column keys to include. Omit for all columns. Available columns include the default pricing tier price (default_price) and each product pricing tier price (pricing_tier_{id}), plus stock_uom_code and stock_uom_name.","disabled":true}]},"description":"Export products as XLSX or CSV.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nScope options:\n- filtered: Uses current filter parameters (same as the products list)\n- all: All products (ignores filters)\n- current_page: First page of current filter results\n- selected: Specific product IDs (requires ids param)\n\nContent modes:\n- summary: One row per product (includes the product-level Default Supplier columns when requested)\n- suppliers: One row per product-supplier link. Emits the per-link supplier columns (Supplier Name, Supplier SKU, MOQ, Leadtime, Target Stock Days, Is Default, pricing tiers) and omits the duplicate product-level Default Supplier columns, so the file round-trips 1:1 with the supplier-links import template.\n\nResponse (dual mode):\n- Small exports (≤ 1000 records, or scope=current_page): Returns the file directly as a binary download (XLSX or CSV).\n- Large exports (> 1000 records): Returns 200 application/json with `{ data: { tracked_job_log_id, queued: true }, message }`. The export is dispatched as a background job; track it via the tracked job log endpoints. Once complete, download via `GET /api/v2/products/list/export/download?file=<filename>` (the job's `results.file` value).\n\n**Requires permission:** `products.export`\n\n**Response shape - `delivery`.** By default an export of up to 1,000 rows is returned directly in this response, and a larger one runs in the background and returns `{\"tracked_job_log_id\": ..., \"queued\": true}`. Pass `delivery=job` to get the job response every time: a small export still runs immediately (it is never queued) and comes back already finished - `{\"tracked_job_log_id\": ..., \"queued\": false, \"status\": \"completed\", \"results\": {\"download_url\": ..., \"record_count\": ...}}` - while a large one is queued exactly as before. `scope=current_page` always returns the page directly, whatever `delivery` says. Use it when your client expects one flow for every export (start, then check `GET /api/tracked-job-logs/{trackedJobLog}`, then download), such as an ETL connector. Subscribe to the `export.completed` webhook to be told when a queued export is ready instead of polling."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/list/export?format=xlsx&scope=filtered&content_mode=summary&columns=id,sku,name,type,inventory_total","host":["{{baseUrl}}"],"path":["api","v2","products","list","export"],"query":[{"key":"format","value":"xlsx","description":"Export format: xlsx (default) or csv","disabled":true},{"key":"scope","value":"filtered","description":"Export scope: filtered (default, uses active filters), all, current_page, selected","disabled":true},{"key":"content_mode","value":"summary","description":"Content mode: summary (default, one row per product) or suppliers (one row per product-supplier link; emits the per-link supplier columns and omits the duplicate product-level Default Supplier columns)","disabled":true},{"key":"columns","value":"id,sku,name,type,inventory_total","description":"Comma-separated column keys to include. Omit for all columns. Available columns include the default pricing tier price (default_price) and each product pricing tier price (pricing_tier_{id}), plus stock_uom_code and stock_uom_name.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary XLSX file)"}]},{"name":"Download Export File","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/list/export/download?file=products-export-2026-05-12-153045.xlsx","host":["{{baseUrl}}"],"path":["api","v2","products","list","export","download"],"query":[{"key":"file","value":"products-export-2026-05-12-153045.xlsx","description":"Filename returned by the queued export tracked job (`results.file`). Only basename is used server-side.","disabled":true}]},"description":"Download a completed products export file produced by a queued export tracked job.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nUse this after `GET /api/v2/products/list/export` returned a queued JSON response. Poll the tracked job log endpoints for completion, then fetch the `results.file` value from the TrackedJobLog and pass it as the `file` query parameter.\n\nResponse: File download (XLSX or CSV). Returns 404 if the file does not exist on the `model-exports` disk.\n\n**Requires permission:** `products.export`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/list/export/download?file=products-export-2026-05-12-153045.xlsx","host":["{{baseUrl}}"],"path":["api","v2","products","list","export","download"],"query":[{"key":"file","value":"products-export-2026-05-12-153045.xlsx","description":"Filename returned by the queued export tracked job (`results.file`). Only basename is used server-side.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary XLSX file)"}]},{"name":"List Composition Revisions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/component-revisions","host":["{{baseUrl}}"],"path":["api","v2","products",":product","component-revisions"],"variable":[{"key":"product","value":"42","description":"Product ID (bundle or kit)"}]},"description":"Newest-first, paginated timeline of effective-dated composition (recipe) revisions for a bundle or kit. Each revision is an immutable snapshot of the product's component set for an effective date band [valid_from, valid_to). The active revision has a null valid_to.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/component-revisions","host":["{{baseUrl}}"],"path":["api","v2","products",":product","component-revisions"],"variable":[{"key":"product","value":"42","description":"Product ID (bundle or kit)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5,\n      \"parent_product_id\": 42,\n      \"revision_number\": 3,\n      \"valid_from\": \"2026-07-20\",\n      \"valid_to\": null,\n      \"status\": \"active\",\n      \"source\": \"manual\",\n      \"change_summary\": \"Swapped the cap component\",\n      \"created_by_id\": 7,\n      \"is_active\": true,\n      \"lines_count\": 2,\n      \"lines\": [\n        {\n          \"component_product_id\": 10,\n          \"quantity\": 1,\n          \"sku\": \"CAP-BLK\",\n          \"name\": \"Black Cap\"\n        },\n        {\n          \"component_product_id\": 11,\n          \"quantity\": 2,\n          \"sku\": \"BTL-500\",\n          \"name\": \"500ml Bottle\"\n        }\n      ],\n      \"created_at\": \"2026-07-20T14:03:11.000000Z\",\n      \"updated_at\": \"2026-07-20T14:03:11.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"per_page\": 10,\n  \"last_page\": 1,\n  \"total\": 3,\n  \"from\": 1,\n  \"to\": 3\n}"}]},{"name":"Apply or Schedule a Recipe Change","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/component-revisions","host":["{{baseUrl}}"],"path":["api","v2","products",":product","component-revisions"],"variable":[{"key":"product","value":"42","description":"Product ID (bundle or kit)"}]},"description":"Apply a recipe change to a bundle or kit effective as-of a date. When effective_from is today the change applies to the live recipe immediately; a future date is SCHEDULED (the live recipe stays unchanged until that day, when a daily job flips it); a past date is a CORRECTION that rewrites what historical orders resolved to in that window and therefore requires allow_backdate=true. If the band covering that date already carries exactly this recipe nothing is written and mode is UNCHANGED.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nDates are calendar days in the account's timezone. Only bundle and kit products accept a recipe.\n\nFields:\n- components (required, array, min 1): each { id (product id, unique within the list), quantity (>= 1) }.\n- effective_from (required, date YYYY-MM-DD): the day the new recipe takes effect.\n- change_summary (optional, string, max 1000): note stored on the revision.\n- allow_backdate (optional, boolean): must be true when effective_from is in the past, else the request is rejected.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"components\": [\n    {\n      \"id\": 10,\n      \"quantity\": 1\n    },\n    {\n      \"id\": 11,\n      \"quantity\": 2\n    }\n  ],\n  \"effective_from\": \"2026-09-01\",\n  \"change_summary\": \"New formulation ships Sep 1\",\n  \"allow_backdate\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/component-revisions","host":["{{baseUrl}}"],"path":["api","v2","products",":product","component-revisions"],"variable":[{"key":"product","value":"42","description":"Product ID (bundle or kit)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"revision\": {\n      \"id\": 6,\n      \"revision_number\": 4,\n      \"valid_from\": \"2026-09-01\",\n      \"valid_to\": null,\n      \"status\": \"scheduled\",\n      \"is_correction\": false\n    },\n    \"mode\": \"scheduled\"\n  }\n}"}]},{"name":"Get Composition Revision","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/component-revisions/{revision}","host":["{{baseUrl}}"],"path":["api","v2","products",":product","component-revisions",":revision"],"variable":[{"key":"product","value":"42","description":"Product ID (bundle or kit)"},{"key":"revision","value":"5","description":"Composition revision ID"}]},"description":"Get a single composition revision (with its component lines) scoped to its parent product. Returns 404 if the revision does not belong to the given product.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/component-revisions/{revision}","host":["{{baseUrl}}"],"path":["api","v2","products",":product","component-revisions",":revision"],"variable":[{"key":"product","value":"42","description":"Product ID (bundle or kit)"},{"key":"revision","value":"5","description":"Composition revision ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"parent_product_id\": 42,\n    \"revision_number\": 3,\n    \"valid_from\": \"2026-07-20\",\n    \"valid_to\": null,\n    \"status\": \"active\",\n    \"source\": \"manual\",\n    \"change_summary\": \"Swapped the cap component\",\n    \"created_by_id\": 7,\n    \"is_active\": true,\n    \"lines_count\": 2,\n    \"lines\": [\n      {\n        \"component_product_id\": 10,\n        \"quantity\": 1,\n        \"sku\": \"CAP-BLK\",\n        \"name\": \"Black Cap\"\n      },\n      {\n        \"component_product_id\": 11,\n        \"quantity\": 2,\n        \"sku\": \"BTL-500\",\n        \"name\": \"500ml Bottle\"\n      }\n    ],\n    \"created_at\": \"2026-07-20T14:03:11.000000Z\",\n    \"updated_at\": \"2026-07-20T14:03:11.000000Z\"\n  }\n}"}]},{"name":"Resolve Composition As Of Date","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/composition","host":["{{baseUrl}}"],"path":["api","v2","products",":product","composition"],"variable":[{"key":"product","value":"42","description":"Product ID (bundle or kit)"}]},"description":"Resolve the component set effective on a given date, with provenance (which revision applied). Answers \"what was this product's recipe on date X\". When as_of precedes the earliest revision, the earliest is returned with is_pre_history = true; a product with no revisions returns a null revision and empty lines.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/composition","host":["{{baseUrl}}"],"path":["api","v2","products",":product","composition"],"variable":[{"key":"product","value":"42","description":"Product ID (bundle or kit)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"parent_product_id\": 42,\n    \"resolved_as_of\": \"2026-02-15\",\n    \"revision_id\": 4,\n    \"revision_number\": 2,\n    \"valid_from\": \"2026-01-10\",\n    \"valid_to\": \"2026-03-01\",\n    \"is_pre_history\": false,\n    \"lines\": [\n      {\n        \"component_product_id\": 10,\n        \"quantity\": 1,\n        \"sku\": \"CAP-BLK\",\n        \"name\": \"Black Cap\"\n      },\n      {\n        \"component_product_id\": 11,\n        \"quantity\": 2,\n        \"sku\": \"BTL-500\",\n        \"name\": \"500ml Bottle\"\n      }\n    ]\n  }\n}"}]},{"name":"List Orders Affected by a Correction","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/component-revisions/{revision}/affected-orders","host":["{{baseUrl}}"],"path":["api","v2","products",":product","component-revisions",":revision","affected-orders"],"variable":[{"key":"product","value":"42","description":"Bundle product ID"},{"key":"revision","value":"5","description":"Corrective revision ID"}]},"description":"Paginated sales orders affected by a corrective (back-dated) revision's window, with a before -> after component diff and a shipped/unshipped summary. Bundles only — for a kit the response is { supported:false } with an explanatory message (kits have no per-order components). Filter with status=all|unshipped|shipped.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nEach order carries skip_reason (null when it will be rewritten in place; otherwise why the rewrite job will skip it, e.g. fulfillment activity or tax/discount allocations). Component rows are grouped per bundle occurrence on the order via sales_channel_line_id, and order_date is the calendar day in the account's timezone."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/component-revisions/{revision}/affected-orders","host":["{{baseUrl}}"],"path":["api","v2","products",":product","component-revisions",":revision","affected-orders"],"variable":[{"key":"product","value":"42","description":"Bundle product ID"},{"key":"revision","value":"5","description":"Corrective revision ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"supported\": true,\n  \"data\": [\n    {\n      \"sales_order_id\": 231,\n      \"order_number\": \"SO-10231\",\n      \"order_date\": \"2025-07-01\",\n      \"order_status\": \"open\",\n      \"fulfillment_status\": \"unfulfilled\",\n      \"is_shipped\": false,\n      \"components\": [\n        {\n          \"component_product_id\": 10,\n          \"sku\": \"CAP-BLK\",\n          \"name\": \"Black Cap\",\n          \"current_quantity\": 1,\n          \"corrected_quantity\": 1,\n          \"sales_channel_line_id\": null\n        },\n        {\n          \"component_product_id\": 11,\n          \"sku\": \"BTL-500\",\n          \"name\": \"500ml Bottle\",\n          \"current_quantity\": 2,\n          \"corrected_quantity\": 5,\n          \"sales_channel_line_id\": null\n        }\n      ],\n      \"skip_reason\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 15,\n  \"per_page\": 10,\n  \"total\": 142,\n  \"from\": 1,\n  \"to\": 10,\n  \"summary\": {\n    \"total\": 142,\n    \"unshipped\": 118,\n    \"shipped\": 24\n  }\n}"}]},{"name":"Rewrite Affected Orders","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/component-revisions/{revision}/rewrite-orders","host":["{{baseUrl}}"],"path":["api","v2","products",":product","component-revisions",":revision","rewrite-orders"],"variable":[{"key":"product","value":"42","description":"Bundle product ID"},{"key":"revision","value":"5","description":"Corrective revision ID"}]},"description":"Dispatch the background job that rewrites the affected orders' bundle component lines to the corrected recipe. Target either an explicit sales_order_ids list, or apply_to_all=true with a status filter. Unshipped orders are rewritten cleanly; shipped orders are skipped unless a system setting enables inventory/COGS compensation. The returned tracked_job_log_id can be polled for progress.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields:\n- sales_order_ids (required unless apply_to_all): order ids to rewrite.\n- apply_to_all (optional, boolean): rewrite every affected order matching the status filter.\n- status (optional): all | unshipped | shipped (scopes apply_to_all).\n\nAuthentication: Requires Bearer token.\n\nOnly orders inside the correction window whose bundle lines carry no fulfillment activity (nothing fulfilled, canceled or returned, no fulfillment lines or inventory movements) are rewritten; everything else is skipped and counted in the job result. The bundle's extended value on each order is preserved.","body":{"mode":"raw","raw":"{\n  \"sales_order_ids\": [\n    231,\n    240\n  ],\n  \"apply_to_all\": false,\n  \"status\": \"unshipped\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/component-revisions/{revision}/rewrite-orders","host":["{{baseUrl}}"],"path":["api","v2","products",":product","component-revisions",":revision","rewrite-orders"],"variable":[{"key":"product","value":"42","description":"Bundle product ID"},{"key":"revision","value":"5","description":"Corrective revision ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 9012\n  }\n}"}]},{"name":"List Product Channel Categories","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/channel-categories","host":["{{baseUrl}}"],"path":["api","v2","products",":product","channel-categories"],"variable":[{"key":"product","value":"88","description":"Product id"}]},"description":"Where the product would publish on every sales channel that supports listings, and why — one entry per channel (or just the one named by `channel`). Resolution order: the product's own override on the channel, else the category its product type is mapped to on the channel, else nothing.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach resolution carries:\n- `product_id`, `sales_channel_id`\n- `sales_channel_category` — `{id, name, path, external_id, is_leaf}`, or null when nothing resolved\n- `provenance` — `override` (the product's own override on the channel) or `type_map` (its product type's map row on the channel); null when nothing resolved\n- `product_type` — `{id, name, path}` of the type the product resolves under (a variant with no type of its own uses its parent's), or null\n- `template` — `{id, name, is_default}`: the template pinned by the override, else the resolved category's default template; null when there is none\n- `override` — `{id, sales_channel_category_id, sales_channel_product_template_id, created_at}` when an override is set, else null\n- `type_map_category` — set only while an override is in force AND the type map points at a different category, so the difference can be shown; otherwise null\n- `sales_channel` — `{id, name, integration_name}`\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/channel-categories","host":["{{baseUrl}}"],"path":["api","v2","products",":product","channel-categories"],"variable":[{"key":"product","value":"88","description":"Product id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 88,\n      \"sales_channel_id\": 30,\n      \"sales_channel_category\": {\n        \"id\": 1184,\n        \"name\": \"Cloth Diapers\",\n        \"path\": \"Baby > Diapering > Cloth Diapers\",\n        \"external_id\": \"5406\",\n        \"is_leaf\": true\n      },\n      \"provenance\": \"type_map\",\n      \"product_type\": {\n        \"id\": 7,\n        \"name\": \"Cloth diapers\",\n        \"path\": \"Diapering > Cloth diapers\"\n      },\n      \"template\": {\n        \"id\": 21,\n        \"name\": \"Cloth diapers — Walmart\",\n        \"is_default\": true,\n        \"sales_channel_listing_profile_id\": 3\n      },\n      \"override\": null,\n      \"type_map_category\": null,\n      \"sales_channel\": {\n        \"id\": 30,\n        \"name\": \"Walmart US\",\n        \"integration_name\": \"Walmart\"\n      }\n    },\n    {\n      \"product_id\": 88,\n      \"sales_channel_id\": 31,\n      \"sales_channel_category\": {\n        \"id\": 5090,\n        \"name\": \"Cloth Nappies\",\n        \"path\": \"Baby > Nappies > Cloth Nappies\",\n        \"external_id\": \"177675\",\n        \"is_leaf\": true\n      },\n      \"provenance\": \"override\",\n      \"product_type\": {\n        \"id\": 7,\n        \"name\": \"Cloth diapers\",\n        \"path\": \"Diapering > Cloth diapers\"\n      },\n      \"template\": {\n        \"id\": 44,\n        \"name\": \"eBay nappies\",\n        \"is_default\": false,\n        \"sales_channel_listing_profile_id\": 3\n      },\n      \"override\": {\n        \"id\": 3,\n        \"sales_channel_category_id\": 5090,\n        \"sales_channel_product_template_id\": 44,\n        \"created_at\": \"2026-09-12T08:30:00.000000Z\"\n      },\n      \"type_map_category\": {\n        \"id\": 5091,\n        \"name\": \"Nappy Covers\",\n        \"path\": \"Baby > Nappies > Nappy Covers\",\n        \"external_id\": \"177676\",\n        \"is_leaf\": true\n      },\n      \"sales_channel\": {\n        \"id\": 31,\n        \"name\": \"eBay Store\",\n        \"integration_name\": \"Ebay\"\n      }\n    },\n    {\n      \"product_id\": 88,\n      \"sales_channel_id\": 32,\n      \"sales_channel_category\": null,\n      \"provenance\": null,\n      \"product_type\": {\n        \"id\": 7,\n        \"name\": \"Cloth diapers\",\n        \"path\": \"Diapering > Cloth diapers\"\n      },\n      \"template\": null,\n      \"override\": null,\n      \"type_map_category\": null,\n      \"sales_channel\": {\n        \"id\": 32,\n        \"name\": \"Shopify\",\n        \"integration_name\": \"Shopify\"\n      }\n    }\n  ]\n}"}]},{"name":"Set Product Channel Category Override","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/channel-categories/{salesChannel}","host":["{{baseUrl}}"],"path":["api","v2","products",":product","channel-categories",":salesChannel"],"variable":[{"key":"product","value":"88","description":"Product id"},{"key":"salesChannel","value":"31","description":"Sales channel id"}]},"description":"Pin the product to a category on this channel — and optionally to a listing template — regardless of what its product type's map says. Replaces any existing override for the product on the channel. Responds with the product's resolution on the channel (`provenance` = `override`).\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- `sales_channel_category_id` (required, integer) — a leaf category belonging to this channel\n- `sales_channel_product_template_id` (nullable, integer) — a template of this channel that belongs to that category (or to no category); omit to use the category's default template\n\nEach resolution carries:\n- `product_id`, `sales_channel_id`\n- `sales_channel_category` — `{id, name, path, external_id, is_leaf}`, or null when nothing resolved\n- `provenance` — `override` (the product's own override on the channel) or `type_map` (its product type's map row on the channel); null when nothing resolved\n- `product_type` — `{id, name, path}` of the type the product resolves under (a variant with no type of its own uses its parent's), or null\n- `template` — `{id, name, is_default}`: the template pinned by the override, else the resolved category's default template; null when there is none\n- `override` — `{id, sales_channel_category_id, sales_channel_product_template_id, created_at}` when an override is set, else null\n- `type_map_category` — set only while an override is in force AND the type map points at a different category, so the difference can be shown; otherwise null\n\n**Requires permission:** `products.update`","body":{"mode":"raw","raw":"{\n  \"sales_channel_category_id\": 5090,\n  \"sales_channel_product_template_id\": 44\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/channel-categories/{salesChannel}","host":["{{baseUrl}}"],"path":["api","v2","products",":product","channel-categories",":salesChannel"],"variable":[{"key":"product","value":"88","description":"Product id"},{"key":"salesChannel","value":"31","description":"Sales channel id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product_id\": 88,\n    \"sales_channel_id\": 31,\n    \"sales_channel_category\": {\n      \"id\": 5090,\n      \"name\": \"Cloth Nappies\",\n      \"path\": \"Baby > Nappies > Cloth Nappies\",\n      \"external_id\": \"177675\",\n      \"is_leaf\": true\n    },\n    \"provenance\": \"override\",\n    \"product_type\": {\n      \"id\": 7,\n      \"name\": \"Cloth diapers\",\n      \"path\": \"Diapering > Cloth diapers\"\n    },\n    \"template\": {\n      \"id\": 44,\n      \"name\": \"eBay nappies\",\n      \"is_default\": false,\n      \"sales_channel_listing_profile_id\": 3\n    },\n    \"override\": {\n      \"id\": 3,\n      \"sales_channel_category_id\": 5090,\n      \"sales_channel_product_template_id\": 44,\n      \"created_at\": \"2026-09-12T08:30:00.000000Z\"\n    },\n    \"type_map_category\": {\n      \"id\": 5091,\n      \"name\": \"Nappy Covers\",\n      \"path\": \"Baby > Nappies > Nappy Covers\",\n      \"external_id\": \"177676\",\n      \"is_leaf\": true\n    }\n  }\n}"}]},{"name":"Remove Product Channel Category Override","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/channel-categories/{salesChannel}","host":["{{baseUrl}}"],"path":["api","v2","products",":product","channel-categories",":salesChannel"],"variable":[{"key":"product","value":"88","description":"Product id"},{"key":"salesChannel","value":"31","description":"Sales channel id"}]},"description":"Remove the product's override on this channel so it follows its product type's map again. Responds with the resolution now in force — `provenance` `type_map` when the type is mapped on the channel, otherwise null with no category. Removing an override that does not exist is a no-op.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `products.update`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/channel-categories/{salesChannel}","host":["{{baseUrl}}"],"path":["api","v2","products",":product","channel-categories",":salesChannel"],"variable":[{"key":"product","value":"88","description":"Product id"},{"key":"salesChannel","value":"31","description":"Sales channel id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product_id\": 88,\n    \"sales_channel_id\": 31,\n    \"sales_channel_category\": {\n      \"id\": 5091,\n      \"name\": \"Nappy Covers\",\n      \"path\": \"Baby > Nappies > Nappy Covers\",\n      \"external_id\": \"177676\",\n      \"is_leaf\": true\n    },\n    \"provenance\": \"type_map\",\n    \"product_type\": {\n      \"id\": 7,\n      \"name\": \"Cloth diapers\",\n      \"path\": \"Diapering > Cloth diapers\"\n    },\n    \"template\": null,\n    \"override\": null,\n    \"type_map_category\": null\n  }\n}"}]},{"name":"List Product Channel Availability","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/channel-exclusions","host":["{{baseUrl}}"],"path":["api","v2","products",":product","channel-exclusions"],"variable":[{"key":"product","value":"123","description":"Product ID"}]},"description":"List every publishable sales channel for a product, each flagged with whether the product is excluded from it. Excluding a product from a channel removes it from that channel's listing-readiness routing while leaving it eligible on every other channel — the per-channel counterpart to the product's global \"not for sale on channels\" setting.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/channel-exclusions","host":["{{baseUrl}}"],"path":["api","v2","products",":product","channel-exclusions"],"variable":[{"key":"product","value":"123","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"sales_channel\": {\n        \"id\": 30,\n        \"name\": \"My Amazon Store\",\n        \"integration_name\": \"Amazon\"\n      },\n      \"excluded\": true\n    },\n    {\n      \"sales_channel\": {\n        \"id\": 31,\n        \"name\": \"My Walmart Store\",\n        \"integration_name\": \"Walmart\"\n      },\n      \"excluded\": false\n    }\n  ]\n}"}]},{"name":"Exclude Product From Channel","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/channel-exclusions/{salesChannel}","host":["{{baseUrl}}"],"path":["api","v2","products",":product","channel-exclusions",":salesChannel"],"variable":[{"key":"product","value":"123","description":"Product ID"},{"key":"salesChannel","value":"31","description":"Sales channel ID"}]},"description":"Exclude a product from a sales channel. The product stops appearing as a listing candidate for that channel; it stays eligible on every other channel. Idempotent — excluding an already-excluded product has no additional effect.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/channel-exclusions/{salesChannel}","host":["{{baseUrl}}"],"path":["api","v2","products",":product","channel-exclusions",":salesChannel"],"variable":[{"key":"product","value":"123","description":"Product ID"},{"key":"salesChannel","value":"31","description":"Sales channel ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"sales_channel\": {\n      \"id\": 31,\n      \"name\": \"My Walmart Store\",\n      \"integration_name\": \"Walmart\"\n    },\n    \"excluded\": true\n  }\n}"}]},{"name":"Allow Product On Channel","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/channel-exclusions/{salesChannel}","host":["{{baseUrl}}"],"path":["api","v2","products",":product","channel-exclusions",":salesChannel"],"variable":[{"key":"product","value":"123","description":"Product ID"},{"key":"salesChannel","value":"31","description":"Sales channel ID"}]},"description":"Remove a channel exclusion, making the product eligible on that sales channel again.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/channel-exclusions/{salesChannel}","host":["{{baseUrl}}"],"path":["api","v2","products",":product","channel-exclusions",":salesChannel"],"variable":[{"key":"product","value":"123","description":"Product ID"},{"key":"salesChannel","value":"31","description":"Sales channel ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"sales_channel\": {\n      \"id\": 31,\n      \"name\": \"My Walmart Store\",\n      \"integration_name\": \"Walmart\"\n    },\n    \"excluded\": false\n  }\n}"}]},{"name":"Lookup Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/lookup","host":["{{baseUrl}}"],"path":["api","v2","products","lookup"]},"description":"Lightweight paginated product lookup that returns only core identifying fields (id, sku, name, barcode, type, unit_cost, price, image_url) — much faster than the full List Products endpoint when you only need to resolve products by SKU, name, barcode, or id.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nWhen a search term is given, results are ranked exact-SKU match first, SKU prefix match second, then everything else; within each rank results are ordered by SKU. A fully-numeric term additionally matches the product id directly. Without a search term, all products are returned ordered by SKU.\n\nArchived products are excluded unless include_archived=true. Pass sellable_only=true to exclude non-sellable products (e.g. internal consumables).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/lookup","host":["{{baseUrl}}"],"path":["api","v2","products","lookup"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 42,\n      \"sku\": \"WIDGET-BLUE\",\n      \"name\": \"Blue Widget\",\n      \"barcode\": 123456789012,\n      \"type\": \"standard\",\n      \"unit_cost\": 4.5,\n      \"price\": 4.5,\n      \"image_url\": \"https://example.sku.io/storage/images/widget-blue.jpg\"\n    },\n    {\n      \"id\": 87,\n      \"sku\": \"WIDGET-BLUE-2PK\",\n      \"name\": \"Blue Widget - 2 Pack\",\n      \"barcode\": 123456789029,\n      \"type\": \"bundle\",\n      \"unit_cost\": 9,\n      \"price\": 9,\n      \"image_url\": null\n    }\n  ],\n  \"first_page_url\": \"https://example.sku.io/api/v2/products/lookup?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://example.sku.io/api/v2/products/lookup?page=1\",\n  \"links\": [\n    {\n      \"url\": null,\n      \"label\": \"&laquo; Previous\",\n      \"active\": false\n    },\n    {\n      \"url\": \"https://example.sku.io/api/v2/products/lookup?page=1\",\n      \"label\": \"1\",\n      \"active\": true\n    },\n    {\n      \"url\": null,\n      \"label\": \"Next &raquo;\",\n      \"active\": false\n    }\n  ],\n  \"next_page_url\": null,\n  \"path\": \"https://example.sku.io/api/v2/products/lookup\",\n  \"per_page\": 20,\n  \"prev_page_url\": null,\n  \"to\": 2,\n  \"total\": 2\n}"}]},{"name":"Get Product Duty Profile","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{product}/duty-profile","host":["{{baseUrl}}"],"path":["api","products",":product","duty-profile"],"variable":[{"key":"product","value":"88","description":"The product ID."}]},"description":"Returns, for every destination country your warehouses receive into, the tariff code used (per-destination override or the product HS code), the matched duty rates, estimated duty per unit at the default supplier cost, pro forma freight per unit, landed cost per unit (supplier cost + pro forma freight + estimated duty) and the last actual duty per unit booked on a FIFO layer at that destination. Per-entry fees depend on the shipment and are excluded (flag `entry_fees_excluded`). Amounts are in base currency.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (scope products:read, permission products.show)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{product}/duty-profile","host":["{{baseUrl}}"],"path":["api","products",":product","duty-profile"],"variable":[{"key":"product","value":"88","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product_id\": 88,\n    \"sku\": \"WIDGET-A\",\n    \"hts_code\": \"8471.30.0100\",\n    \"country_of_origin\": \"CN\",\n    \"tenant_currency_code\": \"USD\",\n    \"default_supplier\": {\n      \"id\": 12,\n      \"name\": \"Shenzhen Co\",\n      \"unit_cost\": 10,\n      \"currency_code\": \"USD\",\n      \"incoterm_code\": \"FOB\",\n      \"country_code\": \"CN\"\n    },\n    \"tariff_codes\": [\n      {\n        \"id\": 5,\n        \"destination_country_code\": \"AU\",\n        \"tariff_code\": \"84713000\",\n        \"tariff_code_formatted\": \"8471.30.00\"\n      }\n    ],\n    \"destinations\": [\n      {\n        \"destination_country_code\": \"US\",\n        \"tariff_code\": \"8471300100\",\n        \"tariff_code_formatted\": \"8471.30.0100\",\n        \"tariff_code_source\": \"product\",\n        \"origin_country_code\": \"CN\",\n        \"status\": \"estimated\",\n        \"status_label\": \"Estimated\",\n        \"flags\": [\n          \"entry_fees_excluded\"\n        ],\n        \"duty_currency_code\": \"USD\",\n        \"valuation_basis\": \"fob\",\n        \"matched_rates\": [\n          {\n            \"duty_rate_id\": 51,\n            \"programme\": \"section_232\",\n            \"programme_label\": \"Section 232\",\n            \"programme_reference\": \"9903.81.91\",\n            \"matched_code\": \"847130\",\n            \"matched_code_formatted\": \"8471.30\",\n            \"match_reason\": \"6-digit prefix, origin CN\",\n            \"ad_valorem_percent\": 25,\n            \"specific_amount\": null,\n            \"specific_uom\": null,\n            \"amount_duty_currency\": 2.5,\n            \"amount\": 2.5\n          }\n        ],\n        \"excluded\": [\n          {\n            \"duty_rate_id\": 40,\n            \"programme\": \"section_301\",\n            \"programme_reference\": \"9903.88.03\",\n            \"matched_code\": \"84713001\",\n            \"reason\": \"not stacked with section_232\"\n          }\n        ],\n        \"supplier_unit_cost\": 10,\n        \"est_duty_per_unit\": 2.5,\n        \"effective_rate_percent\": 25,\n        \"proforma_freight_per_unit\": 0.5,\n        \"landed_cost_per_unit\": 13,\n        \"last_actual_duty_per_unit\": 2.62,\n        \"last_actual_fifo_layer\": {\n          \"id\": 9921,\n          \"fifo_layer_date\": \"2026-08-02\"\n        }\n      }\n    ]\n  }\n}"}]},{"name":"Get Product Duty Summaries","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/duty-summaries?product_ids[]=88","host":["{{baseUrl}}"],"path":["api","products","duty-summaries"],"query":[{"key":"product_ids[]","value":"88","description":"Required. Product ids to summarise (1 to 250 per request).","disabled":true}]},"description":"Returns the duty status and estimated effective duty rate for a batch of products at one destination country, computed in a single pass. Each product is estimated per unit at its default supplier cost on the default pricing tier, using the same rules as Get Product Duty Profile (per-destination tariff code override, then the product HS code; origin from the product, else the supplier's country, flagged `origin_assumed_from_supplier`). Per-entry fees and de minimis depend on the shipment and are excluded.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`status` is one of `estimated`, `seller_clears`, `missing_hs_code`, `missing_origin`, `missing_destination`, `no_rate`. `effective_rate_percent` is null when the product has no default supplier cost. Amounts are in base currency.\n\nAuthentication: Requires Bearer token (scope products:read, permission products.index)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/duty-summaries?product_ids[]=88","host":["{{baseUrl}}"],"path":["api","products","duty-summaries"],"query":[{"key":"product_ids[]","value":"88","description":"Required. Product ids to summarise (1 to 250 per request).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"destination_country_code\": \"US\",\n    \"tenant_currency_code\": \"USD\",\n    \"rate_date\": \"2026-10-09\",\n    \"products\": {\n      \"88\": {\n        \"product_id\": 88,\n        \"status\": \"estimated\",\n        \"status_label\": \"Estimated\",\n        \"flags\": [],\n        \"tariff_code\": \"8471300100\",\n        \"origin_country_code\": \"CN\",\n        \"supplier_unit_cost\": 10,\n        \"est_duty_per_unit\": 2.5,\n        \"effective_rate_percent\": 25\n      },\n      \"89\": {\n        \"product_id\": 89,\n        \"status\": \"missing_hs_code\",\n        \"status_label\": \"No HS code\",\n        \"flags\": [\n          \"no_incoterm_assumed_buyer\"\n        ],\n        \"tariff_code\": null,\n        \"origin_country_code\": \"CN\",\n        \"supplier_unit_cost\": 6,\n        \"est_duty_per_unit\": null,\n        \"effective_rate_percent\": null\n      }\n    }\n  }\n}"}]},{"name":"Update Product Tariff Codes","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/{product}/tariff-codes","host":["{{baseUrl}}"],"path":["api","products",":product","tariff-codes"],"variable":[{"key":"product","value":"88","description":"The product ID."}]},"description":"Replaces the product's per-destination tariff code overrides. Codes accept dots and spaces and are stored as 2–10 digits. Destinations not in the list lose their override; an empty list removes every override. The product HS code remains the default for destinations without an override.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody:\n- `tariff_codes` (required, array, may be empty)\n- `tariff_codes.*.destination_country_code` (required, ISO alpha-2, distinct)\n- `tariff_codes.*.tariff_code` (required, string, 2–10 digits after removing separators)\n\nAuthentication: Requires Bearer token (scope products:write, permission products.update).","body":{"mode":"raw","raw":"{\n  \"tariff_codes\": [\n    {\n      \"destination_country_code\": \"AU\",\n      \"tariff_code\": \"8471.30.00\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/products/{product}/tariff-codes","host":["{{baseUrl}}"],"path":["api","products",":product","tariff-codes"],"variable":[{"key":"product","value":"88","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5,\n      \"destination_country_code\": \"AU\",\n      \"tariff_code\": \"84713000\",\n      \"tariff_code_formatted\": \"8471.30.00\"\n    }\n  ]\n}"}]},{"name":"List Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Paginated list of Magento 2 products scoped to one integration instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Path params:**\n- `magento2IntegrationInstance` (required, integer) — Magento 2 IntegrationInstance ID.\n\n**Pagination:** default `per_page=25`. Default sort: `-id`.\n\n**Quick filters (exact):** `type_id`, `status`, `visibility`, `magento_parent_id`, `attribute_set_id`, `integration_instance_id`.\n\n**Special filters:**\n- `filter[search]` — LIKE across sku + name; numeric values also match magento_entity_id.\n- `filter[sku_product_id]` — `mapped` / `unmapped` / `all`, or a numeric SKU.io product id.\n- `filter[archived]` — `true` (archived only), `false` (active only — default behavior), `all`.\n- `filter[integration_instance_ids]` — accepted as a no-op for channel-layout parity.\n\n**Advanced filters (AdvancedFilterBuilder, operator-aware):**\n- Text columns (operators: equals, not_equals, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty): `sku`, `name`, `type_id`, `status`, `visibility`.\n- Numeric columns (operators: equals, not_equals, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between, is_empty, is_not_empty): `price`, `cost`, `weight`.\n- Datetime columns (operators: equals, not_equals, greater_than, less_than, is_within, is_empty, is_not_empty): `magento_created_at`, `magento_updated_at`, `created_at`, `updated_at`.\n\n**Sorts:** id, magento_entity_id, sku, name, type_id, status, visibility, price, cost, weight, attribute_set_id, magento_parent_id, magento_created_at, magento_updated_at, created_at, updated_at.\n\n**Eager-loads:** `integrationInstance`, `parent` (the configurable parent for simple variants).\n\nThe mapped SKU.io product is returned as a `product` object alongside `subscription_offering` and `revenue_line` — the three things a channel listing can be mapped to. `is_mapped` is true when ANY of them is set, so a listing mapped to a revenue line reports mapped even though it has no product (revenue lines never touch inventory). `mapped_at` is when the mapping was created."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 312,\n      \"integration_instance_id\": 42,\n      \"magento_id\": 1024,\n      \"magento_entity_id\": 1024,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Acme Widget\",\n      \"type_id\": \"simple\",\n      \"status\": 1,\n      \"visibility\": 4,\n      \"price\": 19.99,\n      \"cost\": 7.5,\n      \"weight\": 0.45,\n      \"attribute_set_id\": 4,\n      \"qty\": 128,\n      \"store_id\": 1,\n      \"store_view_code\": \"default\",\n      \"url_key\": \"acme-widget\",\n      \"image_url\": \"https://shop.example.com/pub/media/catalog/product/a/c/acme-widget.jpg\",\n      \"magento_parent_id\": null,\n      \"magento_created_at\": \"2026-02-12T18:01:33+00:00\",\n      \"magento_updated_at\": \"2026-06-15T11:22:18+00:00\",\n      \"created_at\": \"2026-02-12T18:30:00+00:00\",\n      \"updated_at\": \"2026-06-15T11:30:00+00:00\",\n      \"archived_at\": null,\n      \"last_sync_at\": \"2026-06-15T11:30:00+00:00\",\n      \"is_mapped\": true,\n      \"mapped_at\": \"2026-06-15T11:30:00+00:00\",\n      \"magento_admin_url\": \"https://shop.example.com/admin/catalog/product/edit/id/1024\",\n      \"inventory_caches\": [],\n      \"raw_payload\": null,\n      \"product_listing_id\": 902,\n      \"product\": {\n        \"id\": 8821,\n        \"sku\": \"WIDGET-001\",\n        \"name\": \"Acme Widget\",\n        \"listing_id\": 902\n      },\n      \"subscription_offering\": null,\n      \"revenue_line\": null,\n      \"mapped_sku\": \"WIDGET-001\"\n    },\n    {\n      \"id\": 313,\n      \"integration_instance_id\": 42,\n      \"magento_id\": 1025,\n      \"magento_entity_id\": 1025,\n      \"sku\": \"WIDGET-002\",\n      \"name\": \"Acme Widget Pro\",\n      \"type_id\": \"simple\",\n      \"status\": 1,\n      \"visibility\": 4,\n      \"price\": 29.99,\n      \"cost\": 11.25,\n      \"weight\": 0.6,\n      \"attribute_set_id\": 4,\n      \"qty\": 42,\n      \"store_id\": 1,\n      \"store_view_code\": \"default\",\n      \"url_key\": \"acme-widget-pro\",\n      \"image_url\": null,\n      \"magento_parent_id\": null,\n      \"magento_created_at\": \"2026-02-12T18:02:01+00:00\",\n      \"magento_updated_at\": \"2026-06-15T11:22:18+00:00\",\n      \"created_at\": \"2026-02-12T18:30:00+00:00\",\n      \"updated_at\": \"2026-06-15T11:30:00+00:00\",\n      \"archived_at\": null,\n      \"last_sync_at\": \"2026-06-15T11:30:00+00:00\",\n      \"is_mapped\": false,\n      \"mapped_at\": null,\n      \"magento_admin_url\": \"https://shop.example.com/admin/catalog/product/edit/id/1025\",\n      \"inventory_caches\": [],\n      \"raw_payload\": null,\n      \"product_listing_id\": null,\n      \"product\": null,\n      \"subscription_offering\": null,\n      \"revenue_line\": null,\n      \"mapped_sku\": null\n    }\n  ],\n  \"first_page_url\": \"https://sku-io-test/api/magento2/integration-instances/42/products?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://sku-io-test/api/magento2/integration-instances/42/products?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://sku-io-test/api/magento2/integration-instances/42/products\",\n  \"per_page\": 25,\n  \"prev_page_url\": null,\n  \"to\": 2,\n  \"total\": 2\n}"}]},{"name":"Get Products Latest Sync Info","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/latest-sync-info","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","latest-sync-info"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Returns a summary of the most recent product sync: the latest product title and `magento_updated_at`, total / mapped / unmapped product counts, and the `last_synced_at` timestamp (the local `updated_at` of the most recently fetched product).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/latest-sync-info","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","latest-sync-info"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"latest_product_title\": \"Acme Widget Pro\",\n    \"latest_product_date\": \"2026-06-15T11:22:18+00:00\",\n    \"total_products\": 412,\n    \"mapped_count\": 318,\n    \"unmapped_count\": 94,\n    \"last_synced_at\": \"2026-06-15T11:30:00+00:00\"\n  }\n}"}]},{"name":"Get Products Filter Options","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/filter-options","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","filter-options"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Aggregated dropdown options for the products filter panel — `attribute_sets` (distinct `attribute_set_id` values surfaced as `{id, name}` pairs) and `categories` (distinct Magento category ids derived from each product's raw_payload category links, surfaced as `{id, name}` pairs labelled `Category #{id}` until category-name sync lands). Drawn live from the locally synced product rows; empty / never-synced instances surface empty lists.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/filter-options","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","filter-options"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"attribute_sets\": [\n      {\n        \"id\": 4,\n        \"name\": \"Attribute Set #4\"\n      },\n      {\n        \"id\": 9,\n        \"name\": \"Attribute Set #9\"\n      }\n    ],\n    \"categories\": [\n      {\n        \"id\": 3,\n        \"name\": \"Category #3\"\n      },\n      {\n        \"id\": 5,\n        \"name\": \"Category #5\"\n      }\n    ]\n  }\n}"}]},{"name":"Search Products (Magento live)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/search","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","search"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Searches the upstream Magento catalog directly, rather than products already synced to SKU.io. Currently returns an empty `data` array — live catalog search is not yet available. Use the product list endpoint with `filter[search]=…` to search synced products.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/search","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","search"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": []\n}"}]},{"name":"Get Field Mappings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/field-mappings","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","field-mappings"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Returns the Magento 2 attribute → SKU.io product field mappings for this store, together with the vocabulary needed to edit them.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n`available_magento_fields` is **store-specific**: alongside the fixed product fields it lists every custom attribute code discovered on this store's synced products, grouped under `Custom Attributes` and ordered by how many products carry a value for them. `available_sku_fields` lists only SKU.io product fields that can actually be written on create.\n\n`mappings` are the rules in effect (the saved rules, or the system defaults when none have been saved); `custom_mappings` are only the saved ones; `default_mappings` are the system defaults.\n\n**Path params:**\n- `magento2IntegrationInstance` (required, integer)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/field-mappings","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","field-mappings"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"mappings\": [\n      {\n        \"magento_field\": \"sku\",\n        \"sku_field\": \"sku\"\n      },\n      {\n        \"magento_field\": \"name\",\n        \"sku_field\": \"name\"\n      },\n      {\n        \"magento_field\": \"manufacturer_part_number\",\n        \"sku_field\": \"mpn\"\n      }\n    ],\n    \"magento_attributes\": [],\n    \"effective_mappings\": [\n      {\n        \"listing_field\": \"sku\",\n        \"sku_field\": \"sku\",\n        \"parsers\": []\n      },\n      {\n        \"listing_field\": \"name\",\n        \"sku_field\": \"name\",\n        \"parsers\": []\n      },\n      {\n        \"listing_field\": \"manufacturer_part_number\",\n        \"sku_field\": \"mpn\",\n        \"parsers\": []\n      }\n    ],\n    \"default_mappings\": [\n      {\n        \"listing_field\": \"sku\",\n        \"sku_field\": \"sku\",\n        \"parsers\": []\n      },\n      {\n        \"listing_field\": \"name\",\n        \"sku_field\": \"name\",\n        \"parsers\": []\n      }\n    ],\n    \"custom_mappings\": [\n      {\n        \"listing_field\": \"manufacturer_part_number\",\n        \"sku_field\": \"mpn\",\n        \"parsers\": []\n      }\n    ],\n    \"available_magento_fields\": {\n      \"sku\": {\n        \"name\": \"sku\",\n        \"label\": \"SKU\",\n        \"type\": \"string\",\n        \"group\": \"Product\"\n      },\n      \"name\": {\n        \"name\": \"name\",\n        \"label\": \"Name\",\n        \"type\": \"string\",\n        \"group\": \"Product\"\n      },\n      \"weight\": {\n        \"name\": \"weight\",\n        \"label\": \"Weight\",\n        \"type\": \"number\",\n        \"group\": \"Product\"\n      },\n      \"manufacturer_part_number\": {\n        \"name\": \"manufacturer_part_number\",\n        \"label\": \"Manufacturer Part Number\",\n        \"type\": \"string\",\n        \"group\": \"Custom Attributes\",\n        \"description\": \"Found on 1,284 products. Example: ACM-4417-B\"\n      },\n      \"package_length\": {\n        \"name\": \"package_length\",\n        \"label\": \"Package Length\",\n        \"type\": \"string\",\n        \"group\": \"Custom Attributes\",\n        \"description\": \"Found on 1,102 products. Example: 12.5\"\n      }\n    },\n    \"available_sku_fields\": {\n      \"sku\": {\n        \"name\": \"sku\",\n        \"label\": \"SKU\",\n        \"type\": \"string\"\n      },\n      \"name\": {\n        \"name\": \"name\",\n        \"label\": \"Name\",\n        \"type\": \"string\"\n      },\n      \"mpn\": {\n        \"name\": \"mpn\",\n        \"label\": \"MPN\",\n        \"type\": \"string\"\n      },\n      \"length\": {\n        \"name\": \"length\",\n        \"label\": \"Length\",\n        \"type\": \"number\"\n      }\n    }\n  }\n}"}]},{"name":"Update Field Mappings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/field-mappings","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","field-mappings"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Replaces the stored field mappings for this store. Send the full list — the server overwrites rather than merges, and an empty array clears every mapping.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Body:** `{ mappings: [{ magento_field, sku_field }] }`. `mappings` is required and may be empty; `magento_field` and `sku_field` are required strings on each entry.\n\nEntries whose `magento_field` this store does not offer (see `available_magento_fields` on the GET) are discarded rather than stored — a mapping that can never be applied would otherwise look saved and silently do nothing. The response returns what was actually stored plus `skipped_count`, the number of submitted entries that were dropped.\n\n**Path params:**\n- `magento2IntegrationInstance` (required, integer).","body":{"mode":"raw","raw":"{\n  \"mappings\": [\n    {\n      \"magento_field\": \"sku\",\n      \"sku_field\": \"sku\"\n    },\n    {\n      \"magento_field\": \"name\",\n      \"sku_field\": \"name\"\n    },\n    {\n      \"magento_field\": \"manufacturer_part_number\",\n      \"sku_field\": \"mpn\"\n    },\n    {\n      \"magento_field\": \"package_length\",\n      \"sku_field\": \"length\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/field-mappings","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","field-mappings"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Field mappings saved.\",\n  \"data\": {\n    \"mappings\": [\n      {\n        \"magento_field\": \"sku\",\n        \"sku_field\": \"sku\"\n      },\n      {\n        \"magento_field\": \"name\",\n        \"sku_field\": \"name\"\n      },\n      {\n        \"magento_field\": \"manufacturer_part_number\",\n        \"sku_field\": \"mpn\"\n      },\n      {\n        \"magento_field\": \"package_length\",\n        \"sku_field\": \"length\"\n      }\n    ],\n    \"skipped_count\": 0\n  }\n}"}]},{"name":"Refresh Products","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/refresh","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","refresh"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Dispatch the tracked job that refreshes Magento 2 products. Returns the `tracked_job_log_id` so the frontend can pivot the global JobProgressTray to it. Job name prefix is `Refresh Magento 2 Products: ` — the page's `lastCompletedJob` watcher matches on this prefix.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Body params (all optional):**\n- `mode` — one of `since_latest` (default), `full_catalog`, `from_start_date`, `date_range`. Drives the cursor the job resolves.\n- `updated_at` — ISO datetime string used when `mode=since_latest` to override the auto-detected high-water mark.\n- `date_from` / `date_to` — ISO datetime strings used when `mode=from_start_date` (uses `date_from` only) or `mode=date_range` (uses both).","body":{"mode":"raw","raw":"{\n  \"mode\": \"since_latest\",\n  \"updated_at\": \"2026-06-15 12:00:00\",\n  \"date_from\": null,\n  \"date_to\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/refresh","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","refresh"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 92041\n  },\n  \"message\": \"Product sync queued.\"\n}"}]},{"name":"Bulk Map Products","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/bulk-map","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","bulk-map"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Bulk-map Magento products to SKU.io Products by SKU / SKU-contains / name. **Synchronous** (no tracked job) and **caps at 500 products per call** to keep the request bounded.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Body:**\n- `selection.type` — `selected` (use `ids`) or `filtered` (apply across the currently filtered set; the unmapped-only constraint always applies).\n- `selection.ids` — array of `magento2_products.id` (required when `type=selected`).\n- `selection.filters` — filter object (used when `type=filtered`).\n- `options.match_by` — `sku` (exact Magento SKU == SKU.io Product SKU), `sku_contains` (SKU.io Product SKU `LIKE %magento_sku%`), or `name` (SKU.io Product name == Magento name).\n- `options.create_if_no_match` — when true, create a new SKU.io Product for any Magento product that doesn't match (uses the Magento sku + name).\n\nReturns counts: `mapped`, `skipped`, `total`. `tracked_job_log_id` is always `null` (kept in the response shape for frontend symmetry with the other bulk ops).","body":{"mode":"raw","raw":"{\n  \"selection\": {\n    \"type\": \"selected\",\n    \"ids\": [\n      312,\n      313,\n      314\n    ]\n  },\n  \"options\": {\n    \"match_by\": \"sku\",\n    \"create_if_no_match\": false\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/bulk-map","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","bulk-map"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": null,\n    \"mapped\": 2,\n    \"skipped\": 1,\n    \"total\": 3\n  },\n  \"message\": \"Mapped 2 of 3 products (1 skipped).\"\n}"}]},{"name":"Bulk Unmap Products","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/bulk-unmap","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","bulk-unmap"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Clear `sku_product_id` + `sku_product_listing_id` on the selected products. Synchronous.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Body:**\n- `select_all` — when true, applies across every product in the instance (ignores `ids`).\n- `ids` — array of `magento2_products.id` to unmap (used when `select_all` is false / omitted).\n\nThe mapped SKU.io product is returned as a `product` object alongside `subscription_offering` and `revenue_line` — the three things a channel listing can be mapped to. `is_mapped` is true when ANY of them is set, so a listing mapped to a revenue line reports mapped even though it has no product (revenue lines never touch inventory). `mapped_at` is when the mapping was created.","body":{"mode":"raw","raw":"{\n  \"select_all\": false,\n  \"ids\": [\n    312,\n    313\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/bulk-unmap","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","bulk-unmap"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 product(s) unmapped.\"\n}"}]},{"name":"Bulk Archive Products","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/bulk-archive","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","bulk-archive"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Sets `archived_at = now()` on the selected products. Synchronous. Same body shape as `Bulk Unmap Products` (`select_all` + `ids`).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.","body":{"mode":"raw","raw":"{\n  \"select_all\": false,\n  \"ids\": [\n    312,\n    313\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/bulk-archive","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","bulk-archive"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 product(s) archived.\"\n}"}]},{"name":"Bulk Unarchive Products","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/bulk-unarchive","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","bulk-unarchive"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Clears `archived_at` on the selected products. Synchronous. Same body shape as `Bulk Unmap Products` (`select_all` + `ids`).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.","body":{"mode":"raw","raw":"{\n  \"select_all\": false,\n  \"ids\": [\n    312,\n    313\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/bulk-unarchive","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","bulk-unarchive"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 product(s) unarchived.\"\n}"}]},{"name":"Bulk Delete Magento Records","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/bulk-delete-magento-records","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","bulk-delete-magento-records"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Delete the local Magento product records for the selected ids (does NOT delete anything in Magento itself — only the locally synced rows). Synchronous. Same body shape as the other bulk ops (`select_all` + `ids`).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nResponse keeps `tracked_job_log_id` in the data envelope (always `null`) for frontend symmetry with the other bulk handlers.","body":{"mode":"raw","raw":"{\n  \"select_all\": false,\n  \"ids\": [\n    312,\n    313\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/bulk-delete-magento-records","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products","bulk-delete-magento-records"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": null\n  },\n  \"message\": \"2 product(s) deleted.\"\n}"}]},{"name":"Get Product","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]},"description":"Single product detail. Includes the product's Magento 2 custom attributes, each annotated with the SKU.io product field it is mapped onto. For a configurable product the response also carries its child (variant) products.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Path params:**\n- `magento2IntegrationInstance` (required, integer).\n- `product` (required, integer) — Internal Magento 2 product record ID.\n\n**`custom_attributes[]`** — every attribute captured on the last sync of this product, excluding the ones already mirrored into dedicated columns (`cost`, `description`, `image`, `barcode`, `upc`, `ean`, `gtin`). Dropdown and multi-select values are resolved to their option labels rather than the numeric option IDs Magento stores. Sorted mapped-first, then alphabetically by code. Each entry contains:\n- `code` — the Magento attribute code.\n- `label` — a human-readable label derived from the code.\n- `value` — the captured value as a string, or `null` when empty.\n- `mapped` — whether a field mapping copies this attribute into a SKU.io product field.\n- `sku_field` — the SKU.io product field it is copied into, or `null` when unmapped.\n- `target_label` — the display label of that SKU.io field, or `null` when unmapped.\n\nThis array is returned on the detail endpoint only — the products list omits it.\n\n**Error responses:**\n- `404` — product does not exist OR belongs to a different instance.\n- `401` — caller is not authenticated.\n\nThe mapped SKU.io product is returned as a `product` object alongside `subscription_offering` and `revenue_line` — the three things a channel listing can be mapped to. `is_mapped` is true when ANY of them is set, so a listing mapped to a revenue line reports mapped even though it has no product (revenue lines never touch inventory). `mapped_at` is when the mapping was created."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 312,\n    \"integration_instance_id\": 42,\n    \"magento_id\": 1024,\n    \"magento_entity_id\": 1024,\n    \"sku\": \"WIDGET-001\",\n    \"name\": \"Acme Widget\",\n    \"type_id\": \"simple\",\n    \"status\": 1,\n    \"visibility\": 4,\n    \"price\": 19.99,\n    \"cost\": 7.5,\n    \"weight\": 0.45,\n    \"attribute_set_id\": 4,\n    \"qty\": 128,\n    \"store_id\": 1,\n    \"store_view_code\": \"default\",\n    \"url_key\": \"acme-widget\",\n    \"image_url\": \"https://shop.example.com/pub/media/catalog/product/a/c/acme-widget.jpg\",\n    \"magento_parent_id\": null,\n    \"magento_created_at\": \"2026-02-12T18:01:33+00:00\",\n    \"magento_updated_at\": \"2026-06-15T11:22:18+00:00\",\n    \"created_at\": \"2026-02-12T18:30:00+00:00\",\n    \"updated_at\": \"2026-06-15T11:30:00+00:00\",\n    \"archived_at\": null,\n    \"last_sync_at\": \"2026-06-15T11:30:00+00:00\",\n    \"is_mapped\": true,\n    \"mapped_at\": \"2026-06-15T11:30:00+00:00\",\n    \"magento_admin_url\": \"https://shop.example.com/admin/catalog/product/edit/id/1024\",\n    \"inventory_caches\": [],\n    \"parent\": null,\n    \"children\": [],\n    \"custom_attributes\": [\n      {\n        \"code\": \"manufacturer_part_number\",\n        \"label\": \"Manufacturer Part Number\",\n        \"value\": \"ACM-4417-B\",\n        \"mapped\": true,\n        \"sku_field\": \"mpn\",\n        \"target_label\": \"MPN\"\n      },\n      {\n        \"code\": \"package_length\",\n        \"label\": \"Package Length\",\n        \"value\": \"12.5\",\n        \"mapped\": true,\n        \"sku_field\": \"length\",\n        \"target_label\": \"Length\"\n      },\n      {\n        \"code\": \"color\",\n        \"label\": \"Color\",\n        \"value\": \"Midnight Blue\",\n        \"mapped\": false,\n        \"sku_field\": null,\n        \"target_label\": null\n      },\n      {\n        \"code\": \"country_of_manufacture\",\n        \"label\": \"Country Of Manufacture\",\n        \"value\": \"US\",\n        \"mapped\": false,\n        \"sku_field\": null,\n        \"target_label\": null\n      },\n      {\n        \"code\": \"material\",\n        \"label\": \"Material\",\n        \"value\": \"Cotton, Polyester\",\n        \"mapped\": false,\n        \"sku_field\": null,\n        \"target_label\": null\n      }\n    ],\n    \"raw_payload\": null,\n    \"product_listing_id\": 902,\n    \"product\": {\n      \"id\": 8821,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Acme Widget\",\n      \"listing_id\": 902\n    },\n    \"subscription_offering\": null,\n    \"revenue_line\": null,\n    \"mapped_sku\": \"WIDGET-001\"\n  }\n}"}]},{"name":"Delete Product","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]},"description":"Delete the local Magento product row (does NOT delete anything in Magento itself). No body.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Error responses:**\n- `404` — product does not exist OR belongs to a different instance."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Product deleted.\"\n}"}]},{"name":"Get Orders For Product","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/orders","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","orders"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]},"description":"Paginated list of Magento 2 orders that contain this product. The match is `lineItems.magento_product_id = product.magento_entity_id` OR (when the product has a SKU) `lineItems.sku = product.sku`. Ordered by `magento_created_at DESC`. Eager-loads `integrationInstance` + `lineItems`.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Error responses:**\n- `404` — product does not exist OR belongs to a different instance."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/orders","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","orders"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 7821,\n      \"integration_instance_id\": 42,\n      \"magento_id\": 50012,\n      \"magento_entity_id\": 50012,\n      \"increment_id\": \"000050012\",\n      \"status\": \"complete\",\n      \"customer_email\": \"buyer@example.com\",\n      \"grand_total\": 39.98,\n      \"magento_created_at\": \"2026-06-12T14:18:09+00:00\",\n      \"magento_updated_at\": \"2026-06-12T15:02:33+00:00\"\n    }\n  ],\n  \"first_page_url\": \"https://sku-io-test/api/magento2/integration-instances/42/products/312/orders?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://sku-io-test/api/magento2/integration-instances/42/products/312/orders?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://sku-io-test/api/magento2/integration-instances/42/products/312/orders\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"Get Raw Product Payload","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/raw","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","raw"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]},"description":"Returns the stored `raw_payload` JSON (the exact Magento REST product shape we last upserted). Intended for debugging the mapping pipeline; the frontend exposes it in the detail drawer's Raw tab. Returns `{}` when no payload has been stored.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/raw","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","raw"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product\": {\n      \"id\": 1024,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Acme Widget\",\n      \"price\": 19.99,\n      \"status\": 1,\n      \"visibility\": 4,\n      \"type_id\": \"simple\",\n      \"attribute_set_id\": 4,\n      \"created_at\": \"2026-02-12 18:01:33\",\n      \"updated_at\": \"2026-06-15 11:22:18\",\n      \"weight\": 0.45,\n      \"extension_attributes\": {\n        \"stock_item\": {\n          \"qty\": 128,\n          \"is_in_stock\": true\n        }\n      },\n      \"custom_attributes\": [\n        {\n          \"attribute_code\": \"cost\",\n          \"value\": \"7.50\"\n        },\n        {\n          \"attribute_code\": \"url_key\",\n          \"value\": \"acme-widget\"\n        }\n      ]\n    }\n  }\n}"}]},{"name":"Map Product","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/map","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","map"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]},"description":"Map a single Magento product to a SKU.io Product.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Body:**\n- `sku_product_id` (integer | null) — the SKU.io `products.id` to link to. Pass `null` to clear the mapping (equivalent to `Unmap Product`).\n\nReturns the refreshed `Magento2ProductResource` so the frontend can swap the row.\n\n**Error responses:**\n- `404` — product does not exist OR belongs to a different instance.\n\nThe mapped SKU.io product is returned as a `product` object alongside `subscription_offering` and `revenue_line` — the three things a channel listing can be mapped to. `is_mapped` is true when ANY of them is set, so a listing mapped to a revenue line reports mapped even though it has no product (revenue lines never touch inventory). `mapped_at` is when the mapping was created.","body":{"mode":"raw","raw":"{\n  \"sku_product_id\": 8821\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/map","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","map"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 312,\n    \"integration_instance_id\": 42,\n    \"magento_id\": 1024,\n    \"magento_entity_id\": 1024,\n    \"sku\": \"WIDGET-001\",\n    \"name\": \"Acme Widget\",\n    \"type_id\": \"simple\",\n    \"status\": 1,\n    \"visibility\": 4,\n    \"price\": 19.99,\n    \"cost\": 7.5,\n    \"weight\": 0.45,\n    \"attribute_set_id\": 4,\n    \"is_mapped\": true,\n    \"mapped_at\": \"2026-06-21T12:00:00+00:00\",\n    \"updated_at\": \"2026-06-21T12:00:00+00:00\",\n    \"product_listing_id\": 902,\n    \"product\": {\n      \"id\": 8821,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Acme Widget\",\n      \"listing_id\": 902\n    },\n    \"subscription_offering\": null,\n    \"revenue_line\": null,\n    \"mapped_sku\": \"WIDGET-001\"\n  }\n}"}]},{"name":"Unmap Product","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/unmap","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","unmap"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]},"description":"Clears `sku_product_id` and `sku_product_listing_id` on a single Magento product (no body). Returns the refreshed `Magento2ProductResource`.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Error responses:**\n- `404` — product does not exist OR belongs to a different instance.\n\nThe mapped SKU.io product is returned as a `product` object alongside `subscription_offering` and `revenue_line` — the three things a channel listing can be mapped to. `is_mapped` is true when ANY of them is set, so a listing mapped to a revenue line reports mapped even though it has no product (revenue lines never touch inventory). `mapped_at` is when the mapping was created."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/unmap","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","unmap"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 312,\n    \"integration_instance_id\": 42,\n    \"sku\": \"WIDGET-001\",\n    \"name\": \"Acme Widget\",\n    \"is_mapped\": false,\n    \"mapped_at\": null,\n    \"updated_at\": \"2026-06-21T12:01:00+00:00\",\n    \"product_listing_id\": null,\n    \"product\": null,\n    \"subscription_offering\": null,\n    \"revenue_line\": null,\n    \"mapped_sku\": null\n  }\n}"}]},{"name":"Smart Match SKU Product","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/smart-match","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","smart-match"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]},"description":"Suggest SKU.io products that could be mapped to this Magento 2 product. Returns three buckets:\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n- `exact_match` — the SKU.io product whose `sku` is identical to the Magento product's SKU (or `null` if no match).\n- `barcode_match` — the SKU.io product whose barcode matches the Magento product's barcode (or `null`).\n- `similar_matches` — fuzzy name/SKU matches, capped by `limit`.\n\n**Body (optional):**\n- `query` (string, max 255) — free-text search term. When omitted, the manager seeds the search with the Magento product's `name` (falling back to `sku`).\n- `limit` (integer, 1–50) — max similar matches to return. Defaults to 10.\n\n**Error responses:**\n- `404` — product does not exist OR belongs to a different instance.\n- `422` — validation failed on `query` or `limit`.","body":{"mode":"raw","raw":"{\n  \"query\": \"widget\",\n  \"limit\": 10\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/smart-match","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","smart-match"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"exact_match\": {\n      \"id\": 8821,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Acme Widget\",\n      \"image_url\": \"https://cdn.sku.io/products/widget-001.jpg\",\n      \"match_type\": \"sku\",\n      \"match_score\": 1\n    },\n    \"barcode_match\": null,\n    \"similar_matches\": [\n      {\n        \"id\": 8835,\n        \"sku\": \"WIDGET-002\",\n        \"name\": \"Acme Widget Pro\",\n        \"image_url\": null,\n        \"match_type\": \"fuzzy\",\n        \"match_score\": 0.74\n      },\n      {\n        \"id\": 9112,\n        \"sku\": \"WGT-RED-01\",\n        \"name\": \"Red Widget\",\n        \"image_url\": null,\n        \"match_type\": \"fuzzy\",\n        \"match_score\": 0.52\n      }\n    ]\n  }\n}"}]},{"name":"Create SKU Product From Magento","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/create-sku-product","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","create-sku-product"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]},"description":"Create a brand-new SKU.io product seeded from this Magento 2 product (with optional user overrides + a field-mapping override list), then map the Magento row to the newly created SKU product. The whole operation runs inside a single DB transaction.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Idempotency:** if a SKU.io product with the supplied `sku` already exists, the manager links to that existing product instead of creating a new one. The response then sets `linked_existing: true` and the message becomes `\"Linked to existing SKU product.\"`.\n\n**Pre-condition:** the Magento product MUST be unmapped. Mapped products return `422` and must be unmapped first.\n\n**Body (all fields optional; modal pre-fills from Magento):**\n- `sku` (string, max 255)\n- `name` (string, max 255)\n- `description` (string)\n- `image_url` (string, max 1024)\n- `barcode` (string, max 255)\n- `cost` (number ≥ 0)\n- `price` (number ≥ 0)\n- `weight` (number ≥ 0)\n- `field_mappings[]` — array of `{ magento_field, sku_field }` pairs the manager applies to the Magento payload before persisting. Same shape `GET .../products/field-mappings` returns.\n\n**Error responses:**\n- `404` — product does not exist OR belongs to a different instance.\n- `422` — product is already mapped, or validation failed.\n\nThe mapped SKU.io product is returned as a `product` object alongside `subscription_offering` and `revenue_line` — the three things a channel listing can be mapped to. `is_mapped` is true when ANY of them is set, so a listing mapped to a revenue line reports mapped even though it has no product (revenue lines never touch inventory). `mapped_at` is when the mapping was created.","body":{"mode":"raw","raw":"{\n  \"sku\": \"WIDGET-001\",\n  \"name\": \"Acme Widget\",\n  \"description\": \"Imported from Magento on 2026-06-30.\",\n  \"image_url\": \"https://cdn.example-magento.com/media/catalog/product/w/i/widget.jpg\",\n  \"barcode\": 12345678905,\n  \"cost\": 7.5,\n  \"price\": 19.99,\n  \"weight\": 0.45,\n  \"field_mappings\": [\n    {\n      \"magento_field\": \"sku\",\n      \"sku_field\": \"sku\"\n    },\n    {\n      \"magento_field\": \"name\",\n      \"sku_field\": \"name\"\n    },\n    {\n      \"magento_field\": \"description\",\n      \"sku_field\": \"description\"\n    },\n    {\n      \"magento_field\": \"weight\",\n      \"sku_field\": \"weight\"\n    },\n    {\n      \"magento_field\": \"barcode\",\n      \"sku_field\": \"barcode\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/create-sku-product","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","create-sku-product"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 312,\n    \"integration_instance_id\": 42,\n    \"magento_id\": 1024,\n    \"sku\": \"WIDGET-001\",\n    \"name\": \"Acme Widget\",\n    \"is_mapped\": true,\n    \"mapped_at\": \"2026-06-30T12:00:00+00:00\",\n    \"updated_at\": \"2026-06-30T12:00:00+00:00\",\n    \"product_listing_id\": 902,\n    \"product\": {\n      \"id\": 8821,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Acme Widget\",\n      \"listing_id\": 902\n    },\n    \"subscription_offering\": null,\n    \"revenue_line\": null,\n    \"mapped_sku\": \"WIDGET-001\"\n  },\n  \"linked_existing\": false,\n  \"message\": \"SKU product created and mapped.\"\n}"}]},{"name":"Refresh Single Product","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/refresh-single","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","refresh-single"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]},"description":"**Synchronous** single-product resync. Looks up the product in Magento by its SKU, upserts the local row, and returns the refreshed `Magento2ProductResource` so the frontend can swap the row in place. No `tracked_job_log_id` is returned (unlike the bulk `Refresh Products` endpoint).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**No request body.**\n\n**Error responses:**\n- `404` — product does not exist OR belongs to a different instance.\n- `422` — local product row has no SKU (cannot resolve it against Magento).\n- `502` (or the upstream status, when 4xx) — the Magento API call failed.\n\nThe mapped SKU.io product is returned as a `product` object alongside `subscription_offering` and `revenue_line` — the three things a channel listing can be mapped to. `is_mapped` is true when ANY of them is set, so a listing mapped to a revenue line reports mapped even though it has no product (revenue lines never touch inventory). `mapped_at` is when the mapping was created."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/refresh-single","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","refresh-single"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 312,\n    \"integration_instance_id\": 42,\n    \"magento_id\": 1024,\n    \"magento_entity_id\": 1024,\n    \"sku\": \"WIDGET-001\",\n    \"name\": \"Acme Widget\",\n    \"type_id\": \"simple\",\n    \"status\": 1,\n    \"visibility\": 4,\n    \"price\": 19.99,\n    \"cost\": 7.5,\n    \"weight\": 0.45,\n    \"attribute_set_id\": 4,\n    \"qty\": 128,\n    \"store_id\": 1,\n    \"store_view_code\": \"default\",\n    \"url_key\": \"acme-widget\",\n    \"image_url\": \"https://shop.example.com/pub/media/catalog/product/a/c/acme-widget.jpg\",\n    \"magento_parent_id\": null,\n    \"magento_created_at\": \"2026-02-12T18:01:33+00:00\",\n    \"magento_updated_at\": \"2026-06-21T12:05:00+00:00\",\n    \"created_at\": \"2026-02-12T18:30:00+00:00\",\n    \"updated_at\": \"2026-06-21T12:05:00+00:00\",\n    \"archived_at\": null,\n    \"last_sync_at\": \"2026-06-21T12:05:00+00:00\",\n    \"is_mapped\": true,\n    \"mapped_at\": \"2026-06-21T12:05:00+00:00\",\n    \"magento_admin_url\": \"https://shop.example.com/admin/catalog/product/edit/id/1024\",\n    \"inventory_caches\": [],\n    \"raw_payload\": null,\n    \"product_listing_id\": 902,\n    \"product\": {\n      \"id\": 8821,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Acme Widget\",\n      \"listing_id\": 902\n    },\n    \"subscription_offering\": null,\n    \"revenue_line\": null,\n    \"mapped_sku\": \"WIDGET-001\"\n  },\n  \"message\": \"Product refreshed.\"\n}"}]},{"name":"Archive Product","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/archive","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","archive"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]},"description":"Archive a single product (sets `archived_at = now()`). No body.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Error responses:**\n- `404` — product does not exist OR belongs to a different instance."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/archive","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","archive"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Product archived.\"\n}"}]},{"name":"Unarchive Product","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/unarchive","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","unarchive"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]},"description":"Unarchive a single product (clears `archived_at`). No body.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Error responses:**\n- `404` — product does not exist OR belongs to a different instance."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/products/{product}/unarchive","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","products",":product","unarchive"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"product","value":"312","description":"Internal magento2_products.id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Product unarchived.\"\n}"}]}]},{"name":"Product Import","item":[{"name":"Get Import Fields","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/import/fields","host":["{{baseUrl}}"],"path":["api","v2","products","import","fields"]},"description":"Returns dynamic import fields (currently account-specific attributes) used by the column mapping UI.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRoute: v2.products.import.fields\n\nResponse shape: { data: { attributes: [{ key, label }] } }\n\n**Stock UoM (added 2026-05-30):** `stock_uom_code` is now a supported field. Provide a value matching an existing UoM code (e.g. EACH, KG, BOX). On create, it sets the product's initial stock UoM; on update, it flips the stock UoM to the matching uom_id."}},{"name":"Download Import Template (XLSX)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/import/template","host":["{{baseUrl}}"],"path":["api","v2","products","import","template"]},"description":"Downloads the multi-sheet XLSX import template, prefilled with sample rows and tenant reference data (brands, categories).\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRoute: v2.products.import.template\n\nupdate: the data sheet now includes nine new purple-headed image URL columns:\n- image_url_1 — Primary Image URL\n- image_url_2 .. image_url_9 — Additional gallery image URLs\n\nThe guide sheet documents these columns. Sample rows include picsum.photos placeholder URLs for image_url_1, image_url_2, and image_url_3.\n\nResponse: binary XLSX file (Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet).\n\n**Stock UoM (added 2026-05-30):** `stock_uom_code` is now a supported field. Provide a value matching an existing UoM code (e.g. EACH, KG, BOX). On create, it sets the product's initial stock UoM; on update, it flips the stock UoM to the matching uom_id."}},{"name":"Validate Import","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/import/validate","host":["{{baseUrl}}"],"path":["api","v2","products","import","validate"]},"description":"Validates mapped import rows and returns a structured preview. No side effects — safe to call repeatedly.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRoute: v2.products.import.validate\n\nRequired body field:\n- rows: array (min 1) of row objects with mapped column keys.\n\nadded image URL fields to the accepted row shape:\n- rows[].image_url_1 (string) — Primary image URL (http/https only, max 2048 chars)\n- rows[].image_url_2 .. rows[].image_url_9 (string) — Additional gallery image URLs\n\nValidation rules for each image_url_N:\n- Must start with http:// or https://\n- Must pass FILTER_VALIDATE_URL\n- Must be <= 2048 characters\n- Duplicate URLs (case-insensitive) are skipped with a warning\n- If all image_url_* columns are blank/unmapped, existing product images are left unchanged\n\nResponse shape: { products: [...], summary: {...} }\n\nEach product entry includes a new field:\n- _image_urls: array of { url, is_primary, sort_order, download } | null\n  - null = no image_url_* columns mapped, OR all blank (existing images preserved)\n  - Otherwise: ordered by image_url_1..9 position; image_url_1 becomes is_primary=true; download=true triggers async download/storage on import.\n\nThe summary reports: total_products, total_new, total_updates, total_errors, total_warnings.\n\n**Stock UoM (added 2026-05-30):** `stock_uom_code` is now a supported field. Provide a value matching an existing UoM code (e.g. EACH, KG, BOX). On create, it sets the product's initial stock UoM; on update, it flips the stock UoM to the matching uom_id.","body":{"mode":"raw","raw":"{\n  \"rows\": [\n    {\n      \"sku\": \"SAMPLE-SKU-001\",\n      \"name\": \"Sample Product One\",\n      \"type\": \"standard\",\n      \"barcode\": 12345678901,\n      \"mpn\": \"MFG-001\",\n      \"hts_code\": \"8471.30.0100\",\n      \"country_of_origin\": \"US\",\n      \"brand\": \"Acme Co\",\n      \"category\": \"Widgets\",\n      \"unit_cost\": 9.99,\n      \"weight\": 0.5,\n      \"weight_unit\": \"kg\",\n      \"length\": 10,\n      \"width\": 5,\n      \"height\": 3,\n      \"dimension_unit\": \"cm\",\n      \"tags\": \"sample, import\",\n      \"image_url_1\": \"https://picsum.photos/seed/sku-001-front/600/600\",\n      \"image_url_2\": \"https://picsum.photos/seed/sku-001-back/600/600\",\n      \"image_url_3\": \"https://picsum.photos/seed/sku-001-side/600/600\",\n      \"image_url_4\": \"\",\n      \"image_url_5\": \"\",\n      \"image_url_6\": \"\",\n      \"image_url_7\": \"\",\n      \"image_url_8\": \"\",\n      \"image_url_9\": \"\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}}}]},{"name":"Product Substitutions","item":[{"name":"List Product Substitutions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/product-substitutions","host":["{{baseUrl}}"],"path":["api","product-substitutions"]},"description":"Paginated list of all substitution rules.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFilters use the operator syntax `filter[column.operator]=value` with operators: is, is_not, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, is_one_of. Filterable columns: product_sku, product_name, substitute_sku, substitute_name, substitution_type, mode, active, priority (numeric operators), created_at, updated_at (date operators).\n\nAllowed sorts: id, substitution_type, mode, priority, active, created_at, updated_at (prefix with - for descending).\n\nPagination: page, per_page (default 10).\n\nThe `filter[search]` parameter also matches the exact rule ID, alongside product/substitute SKU and name."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/product-substitutions","host":["{{baseUrl}}"],"path":["api","product-substitutions"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"product\": {\n        \"id\": 101,\n        \"sku\": \"RE61996\",\n        \"name\": \"Rocket Gasket 58mm\"\n      },\n      \"substitute_product\": {\n        \"id\": 102,\n        \"sku\": \"521902\",\n        \"name\": \"Nuova Ricambi Gasket 58mm\"\n      },\n      \"substitution_type\": \"direct\",\n      \"substitution_type_label\": \"Direct equivalent\",\n      \"mode\": \"always\",\n      \"mode_label\": \"Always\",\n      \"allow_backorder_swap\": false,\n      \"include_in_alias_availability\": true,\n      \"priority\": 1,\n      \"active\": true,\n      \"notes\": \"OEM equivalent — direct from Italy\",\n      \"usage_count\": 14,\n      \"substitute_available_quantity\": 184,\n      \"created_by\": \"Jane Doe\",\n      \"created_at\": \"2026-07-27T10:15:00.000000Z\",\n      \"updated_at\": \"2026-07-27T10:15:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"List Substitutions for a Product","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/products/{productId}/substitutions","host":["{{baseUrl}}"],"path":["api","products",":productId","substitutions"],"variable":[{"key":"productId","value":"101","description":"Product ID"}]},"description":"Both substitution directions for one product: rules where it can be replaced by a substitute, and rules where it is the substitute for another product. Includes live availability for each substitute and whether the product is always substituted (a pure sales alias).\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/products/{productId}/substitutions","host":["{{baseUrl}}"],"path":["api","products",":productId","substitutions"],"variable":[{"key":"productId","value":"101","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"substitutions\": [\n      {\n        \"id\": 12,\n        \"product\": {\n          \"id\": 101,\n          \"sku\": \"RE61996\",\n          \"name\": \"Rocket Gasket 58mm\"\n        },\n        \"substitute_product\": {\n          \"id\": 102,\n          \"sku\": \"521902\",\n          \"name\": \"Nuova Ricambi Gasket 58mm\"\n        },\n        \"substitution_type\": \"direct\",\n        \"substitution_type_label\": \"Direct equivalent\",\n        \"mode\": \"always\",\n        \"mode_label\": \"Always\",\n        \"allow_backorder_swap\": false,\n        \"include_in_alias_availability\": true,\n        \"priority\": 1,\n        \"active\": true,\n        \"notes\": \"OEM equivalent — direct from Italy\",\n        \"usage_count\": 14,\n        \"substitute_available_quantity\": 184,\n        \"created_by\": \"Jane Doe\",\n        \"created_at\": \"2026-07-27T10:15:00.000000Z\",\n        \"updated_at\": \"2026-07-27T10:15:00.000000Z\"\n      }\n    ],\n    \"replaces\": [],\n    \"is_always_substituted\": false\n  }\n}"}]},{"name":"Create Product Substitution","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/products/{productId}/substitutions","host":["{{baseUrl}}"],"path":["api","products",":productId","substitutions"],"variable":[{"key":"productId","value":"101","description":"Original product ID"}]},"description":"Create a substitution rule: the route product is the original, the payload names the substitute.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nValidation rejects self-substitution, duplicate pairs, non-stock-tracked substitutes, and `always` rules that would close an automatic swap loop. `create_reverse` also creates the independent reverse rule (substitute → original) with the same mode and substitution_type.\n\nA non-blocking warning is returned when the pair mixes a serialised and a non-serialised product.\n\n`substitution_type` classifies what kind of substitute this is — `direct` (identical to the customer, just a different SKU) or `replacement` (does the same job but is visibly different, so the customer receives something other than what they ordered). It is optional and defaults to `direct`. It is orthogonal to `mode`: `substitution_type` says *what* the substitute is, `mode` says *when* the swap may fire.","body":{"mode":"raw","raw":"{\n  \"substitute_product_id\": 102,\n  \"substitution_type\": \"direct\",\n  \"mode\": \"when_original_unavailable\",\n  \"allow_backorder_swap\": false,\n  \"include_in_alias_availability\": true,\n  \"priority\": 1,\n  \"active\": true,\n  \"notes\": \"OEM equivalent — direct from Italy\",\n  \"create_reverse\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/products/{productId}/substitutions","host":["{{baseUrl}}"],"path":["api","products",":productId","substitutions"],"variable":[{"key":"productId","value":"101","description":"Original product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"product\": {\n        \"id\": 101,\n        \"sku\": \"RE61996\",\n        \"name\": \"Rocket Gasket 58mm\"\n      },\n      \"substitute_product\": {\n        \"id\": 102,\n        \"sku\": \"521902\",\n        \"name\": \"Nuova Ricambi Gasket 58mm\"\n      },\n      \"substitution_type\": \"direct\",\n      \"substitution_type_label\": \"Direct equivalent\",\n      \"mode\": \"always\",\n      \"mode_label\": \"Always\",\n      \"allow_backorder_swap\": false,\n      \"include_in_alias_availability\": true,\n      \"priority\": 1,\n      \"active\": true,\n      \"notes\": \"OEM equivalent — direct from Italy\",\n      \"usage_count\": 14,\n      \"substitute_available_quantity\": 184,\n      \"created_by\": \"Jane Doe\",\n      \"created_at\": \"2026-07-27T10:15:00.000000Z\",\n      \"updated_at\": \"2026-07-27T10:15:00.000000Z\"\n    }\n  ]\n}"}]},{"name":"Update Product Substitution","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-substitutions/{substitutionId}","host":["{{baseUrl}}"],"path":["api","product-substitutions",":substitutionId"],"variable":[{"key":"substitutionId","value":"12","description":"Substitution rule ID"}]},"description":"Update a substitution rule's substitution_type, mode, flags, priority, active state, or notes.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nActivating or switching a rule to `always` re-runs swap-loop detection and is rejected when it would close a loop.\n\n`substitution_type` (`direct` | `replacement`) may be changed here too — it is orthogonal to `mode` and reclassifying a rule never changes when it swaps.","body":{"mode":"raw","raw":"{\n  \"substitution_type\": \"direct\",\n  \"mode\": \"always\",\n  \"allow_backorder_swap\": false,\n  \"include_in_alias_availability\": true,\n  \"priority\": 1,\n  \"active\": true,\n  \"notes\": \"OEM equivalent — direct from Italy\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/product-substitutions/{substitutionId}","host":["{{baseUrl}}"],"path":["api","product-substitutions",":substitutionId"],"variable":[{"key":"substitutionId","value":"12","description":"Substitution rule ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"product\": {\n      \"id\": 101,\n      \"sku\": \"RE61996\",\n      \"name\": \"Rocket Gasket 58mm\"\n    },\n    \"substitute_product\": {\n      \"id\": 102,\n      \"sku\": \"521902\",\n      \"name\": \"Nuova Ricambi Gasket 58mm\"\n    },\n    \"substitution_type\": \"direct\",\n    \"substitution_type_label\": \"Direct equivalent\",\n    \"mode\": \"always\",\n    \"mode_label\": \"Always\",\n    \"allow_backorder_swap\": false,\n    \"include_in_alias_availability\": true,\n    \"priority\": 1,\n    \"active\": true,\n    \"notes\": \"OEM equivalent — direct from Italy\",\n    \"usage_count\": 14,\n    \"substitute_available_quantity\": 184,\n    \"created_by\": \"Jane Doe\",\n    \"created_at\": \"2026-07-27T10:15:00.000000Z\",\n    \"updated_at\": \"2026-07-27T10:15:00.000000Z\"\n  }\n}"}]},{"name":"Delete Product Substitution","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/product-substitutions/{substitutionId}","host":["{{baseUrl}}"],"path":["api","product-substitutions",":substitutionId"],"variable":[{"key":"substitutionId","value":"12","description":"Substitution rule ID"}]},"description":"Delete a substitution rule. Lines already swapped under this rule keep their swap provenance.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/product-substitutions/{substitutionId}","host":["{{baseUrl}}"],"path":["api","product-substitutions",":substitutionId"],"variable":[{"key":"substitutionId","value":"12","description":"Substitution rule ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Substitution deleted.\"\n}"}]},{"name":"Reorder Product Substitutions","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-substitutions/reorder","host":["{{baseUrl}}"],"path":["api","product-substitutions","reorder"]},"description":"Rewrite the priority order of substitution rules for one original product. Pass the rule IDs in the desired order — the first ID becomes priority 1.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ordered_ids\": [\n    14,\n    12,\n    17\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-substitutions/reorder","host":["{{baseUrl}}"],"path":["api","product-substitutions","reorder"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Priorities updated.\"\n}"}]},{"name":"Download Substitution Import Template","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/product-substitutions/import/template","host":["{{baseUrl}}"],"path":["api","product-substitutions","import","template"]},"description":"Download the Excel (XLSX) import template. The workbook has three sheets: 'Import Here' (headers + sample rows; green headers are required), 'Field Guide' (per-column description, valid values, and an example), and 'Reference Data' (the valid substitution_type and mode values, accepted yes/no tokens, and your existing rules).\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRows match on the (product_sku, substitute_sku) pair — an unknown pair creates a rule, an existing pair updates it, so re-importing a corrected file is safe.\n\nAuthentication: Requires Bearer token.\n\nResponse is an .xlsx file download (Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/product-substitutions/import/template","host":["{{baseUrl}}"],"path":["api","product-substitutions","import","template"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary .xlsx workbook — sheets: Import Here, Field Guide, Reference Data)"}]},{"name":"Validate Substitution Import Rows","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-substitutions/import/validate","host":["{{baseUrl}}"],"path":["api","product-substitutions","import","validate"]},"description":"Validate mapped import rows and return a per-row annotated preview — no side effects, safe to call repeatedly.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nEach row is marked `create` (new pair), `update` (pair exists — fields will be updated), or `error` (with a reason). The `mode` column accepts friendly synonyms case-insensitively: \"manual\", \"manual only\", \"out of stock\", \"if out of stock\", \"always\".\n\nThe optional `substitution_type` column accepts \"direct\", \"direct equivalent\", \"equivalent\", \"identical\", \"replacement\", \"functional replacement\", \"functional\", \"alternative\" (case-insensitive); an unrecognised value fails the row. Left blank it defaults a NEW rule to `direct` and leaves an existing rule's classification untouched, so re-uploading a file that predates the column never silently reclassifies a replacement as a direct equivalent.","body":{"mode":"raw","raw":"{\n  \"rows\": [\n    {\n      \"product_sku\": \"RE61996\",\n      \"substitute_sku\": \"521902\",\n      \"substitution_type\": \"direct\",\n      \"mode\": \"always\",\n      \"allow_backorder_swap\": \"no\",\n      \"include_in_alias_availability\": \"yes\",\n      \"priority\": \"1\",\n      \"notes\": \"OEM equivalent\",\n      \"active\": \"yes\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-substitutions/import/validate","host":["{{baseUrl}}"],"path":["api","product-substitutions","import","validate"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"rows\": [\n      {\n        \"status\": \"create\",\n        \"message\": \"New relationship will be created.\",\n        \"row_number\": 1,\n        \"product_id\": 101,\n        \"substitute_product_id\": 102\n      }\n    ],\n    \"summary\": {\n      \"total\": 1,\n      \"create\": 1,\n      \"update\": 0,\n      \"error\": 0\n    }\n  }\n}"}]},{"name":"Import Product Substitutions","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-substitutions/import","host":["{{baseUrl}}"],"path":["api","product-substitutions","import"]},"description":"Execute the import. Files up to 500 rows run inline and return the summary directly; larger files run as a background job (202 response with a job ID to track).\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nThe import is idempotent on the (product_sku, substitute_sku) pair, so re-uploading a corrected file is safe. Set `evaluate_open_orders` to immediately re-evaluate open orders against the imported rules and release any that a swap resolves.\n\nThe optional `substitution_type` column (`direct` | `replacement`, plus the synonyms listed on Validate) is only applied when supplied — a blank value defaults a NEW rule to `direct` and leaves an existing rule's classification untouched.","body":{"mode":"raw","raw":"{\n  \"rows\": [\n    {\n      \"product_sku\": \"RE61996\",\n      \"substitute_sku\": \"521902\",\n      \"substitution_type\": \"direct\",\n      \"mode\": \"always\"\n    }\n  ],\n  \"evaluate_open_orders\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-substitutions/import","host":["{{baseUrl}}"],"path":["api","product-substitutions","import"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"created\": 1,\n    \"updated\": 0,\n    \"failed\": 0,\n    \"errors\": []\n  }\n}"}]},{"name":"Export Product Substitutions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/product-substitutions/export","host":["{{baseUrl}}"],"path":["api","product-substitutions","export"]},"description":"Export substitution rules as XLSX (default) or CSV. Columns: product_sku, substitute_sku, substitution_type, mode, allow_backorder_swap, include_in_alias_availability, priority, notes, active — the same columns the import accepts, so an export can be edited and re-imported.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nScopes: `all` (default) exports every rule; `filtered` honours the same filter query parameters as List Product Substitutions; `selected` exports only the rules named in `ids`.\n\nAuthentication: Requires Bearer token.\n\nResponse is a file download."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/product-substitutions/export","host":["{{baseUrl}}"],"path":["api","product-substitutions","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary .xlsx workbook — one 'Substitutions' sheet with the columns above; pass format=csv for a CSV file instead)"}]}]},{"name":"Organization","item":[{"name":"List Product Brands","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/product-brands?search=nike&sort=name&page=1&limit=25&archived=0","host":["{{baseUrl}}"],"path":["api","product-brands"],"query":[{"key":"search","value":"nike","description":"General text search across searchable columns (name)","disabled":true},{"key":"sort","value":"name","description":"Sort column. Prefix with - for descending (e.g. -name). Sortable: name, created_at, updated_at.","disabled":true},{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"25","description":"Results per page (default: 25)","disabled":true},{"key":"archived","value":"0","description":"0 = active only (default), 1 = archived only","disabled":true}]},"description":"Returns a paginated list of product brands.\n\nAuthentication: Requires Bearer token.\n\nSupports search, sort, pagination, archived filter, and column inclusion/exclusion.\n\nAllowed sort fields: name, created_at, updated_at."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/product-brands?search=nike&sort=name&page=1&limit=25&archived=0","host":["{{baseUrl}}"],"path":["api","product-brands"],"query":[{"key":"search","value":"nike","description":"General text search across searchable columns (name)","disabled":true},{"key":"sort","value":"name","description":"Sort column. Prefix with - for descending (e.g. -name). Sortable: name, created_at, updated_at.","disabled":true},{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"25","description":"Results per page (default: 25)","disabled":true},{"key":"archived","value":"0","description":"0 = active only (default), 1 = archived only","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"sort_order\": 3,\n      \"name\": \"Nike\",\n      \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2024-03-20T14:30:00.000000Z\",\n      \"archived_at\": null\n    },\n    {\n      \"id\": 2,\n      \"sort_order\": 3,\n      \"name\": \"Adidas\",\n      \"created_at\": \"2024-01-16T09:00:00.000000Z\",\n      \"updated_at\": \"2024-03-21T11:00:00.000000Z\",\n      \"archived_at\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 5,\n  \"per_page\": 25,\n  \"total\": 112,\n  \"from\": 1,\n  \"to\": 25\n}"}]},{"name":"Create Product Brand","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-brands","host":["{{baseUrl}}"],"path":["api","product-brands"]},"description":"Creates a new product brand.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nValidation:\n- `name` (required, max 255, unique across product_brands)","body":{"mode":"raw","raw":"{\n  \"name\": \"Nike\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-brands","host":["{{baseUrl}}"],"path":["api","product-brands"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"sort_order\": 3,\n    \"name\": \"Nike\",\n    \"created_at\": \"2024-04-01T12:00:00.000000Z\",\n    \"updated_at\": \"2024-04-01T12:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"Product brand has been created successfully\"\n}"}]},{"name":"Bulk Delete Product Brands","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/product-brands","host":["{{baseUrl}}"],"path":["api","product-brands"]},"description":"Bulk deletes multiple product brands by ID.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nValidation:\n- `ids` (required, array of integers)"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/product-brands","host":["{{baseUrl}}"],"path":["api","product-brands"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 product brands have been deleted successfully\"\n}"}]},{"name":"Get Product Brand","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/product-brands/{id}","host":["{{baseUrl}}"],"path":["api","product-brands",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns a single product brand by ID.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/product-brands/{id}","host":["{{baseUrl}}"],"path":["api","product-brands",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"sort_order\": 3,\n    \"name\": \"Nike\",\n    \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2024-03-20T14:30:00.000000Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Update Product Brand","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-brands/{id}","host":["{{baseUrl}}"],"path":["api","product-brands",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Updates a product brand's name.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nValidation:\n- `name` (required, max 255, unique ignoring current record)","body":{"mode":"raw","raw":"{\n  \"name\": \"Nike Inc.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/product-brands/{id}","host":["{{baseUrl}}"],"path":["api","product-brands",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"sort_order\": 3,\n    \"name\": \"Nike Inc.\",\n    \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2024-04-01T12:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"Product brand Nike Inc. has been updated successfully\"\n}"}]},{"name":"Delete Product Brand","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/product-brands/{id}","host":["{{baseUrl}}"],"path":["api","product-brands",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Deletes a product brand. Fails if any products are still linked to the brand.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/product-brands/{id}","host":["{{baseUrl}}"],"path":["api","product-brands",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Product brand Nike has been deleted successfully\"\n}"}]},{"name":"Archive Product Brand","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/product-brands/{id}/archive","host":["{{baseUrl}}"],"path":["api","product-brands",":id","archive"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Archives a product brand. Sets archived_at timestamp. Returns a warning if already archived.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/product-brands/{id}/archive","host":["{{baseUrl}}"],"path":["api","product-brands",":id","archive"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"sort_order\": 3,\n    \"name\": \"Nike\",\n    \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2024-04-01T12:00:00.000000Z\",\n    \"archived_at\": \"2024-04-01T12:00:00.000000Z\"\n  },\n  \"message\": \"Product brand Nike has been archived successfully\"\n}"}]},{"name":"Unarchive Product Brand","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/product-brands/{id}/unarchived","host":["{{baseUrl}}"],"path":["api","product-brands",":id","unarchived"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Unarchives a product brand. Clears archived_at. Returns a warning if already active.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/product-brands/{id}/unarchived","host":["{{baseUrl}}"],"path":["api","product-brands",":id","unarchived"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"sort_order\": 3,\n    \"name\": \"Nike\",\n    \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2024-04-01T12:30:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"Product brand Nike has been unarchived successfully\"\n}"}]},{"name":"Bulk Archive Product Brands","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-brands/archive","host":["{{baseUrl}}"],"path":["api","product-brands","archive"]},"description":"Bulk archives multiple product brands.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    3,\n    4,\n    5\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/product-brands/archive","host":["{{baseUrl}}"],"path":["api","product-brands","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 product brands have been archived successfully\"\n}"}]},{"name":"Bulk Unarchive Product Brands","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-brands/unarchive","host":["{{baseUrl}}"],"path":["api","product-brands","unarchive"]},"description":"Bulk unarchives multiple product brands.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    3,\n    4,\n    5\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/product-brands/unarchive","host":["{{baseUrl}}"],"path":["api","product-brands","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 product brands have been unarchived successfully\"\n}"}]},{"name":"Check Product Brands Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-brands/deletable","host":["{{baseUrl}}"],"path":["api","product-brands","deletable"]},"description":"Checks whether each of the given product brand IDs can be safely deleted. A brand is not deletable if products are linked to it.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nValidation:\n- `ids` (required, array of integers, each must exist in product_brands table)\n- `ids.*` (integer)","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-brands/deletable","host":["{{baseUrl}}"],"path":["api","product-brands","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Nike\",\n      \"deletable\": false,\n      \"reason\": {\n        \"Products\": \"Product brand(Nike) is currently used by 12 product(s)\"\n      }\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Generic\",\n      \"deletable\": true,\n      \"reason\": null\n    },\n    {\n      \"id\": 3,\n      \"name\": \"Unused Brand\",\n      \"deletable\": true,\n      \"reason\": null\n    }\n  ]\n}"}]},{"name":"Set Brand Pick Order","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-brands/reorder","host":["{{baseUrl}}"],"path":["api","product-brands","reorder"]},"description":"Sets the warehouse pick order for brands — the order a picker walks the floor, front of store first. The `ids` array is the COMPLETE ranked set, in front-to-back order: each listed brand gets its 1-based position, and every other brand is cleared to unranked (picked last). Send an empty array to clear all ranks. Pick lists on warehouses without bin locations sequence their items by this order so the team picks in one pass.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody:\n- `ids` (present, array of integers) — the complete ranked brand id list, front-to-back. Each id must exist and appear once. Empty clears all ranks.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    3,\n    1,\n    2\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-brands/reorder","host":["{{baseUrl}}"],"path":["api","product-brands","reorder"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"The product brand pick order has been updated.\",\n  \"data\": {\n    \"ids\": [\n      3,\n      1,\n      2\n    ]\n  }\n}"}]},{"name":"Search Product Brands","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/product-brands/search?search=nike","host":["{{baseUrl}}"],"path":["api","product-brands","search"],"query":[{"key":"search","value":"nike","description":"Optional. Partial name match (LIKE %search%). Returns up to 50 results ordered by name.","disabled":true}]},"description":"Simple search endpoint returning up to 50 product brands matching the name. Returns product brand object collection.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/product-brands/search?search=nike","host":["{{baseUrl}}"],"path":["api","product-brands","search"],"query":[{"key":"search","value":"nike","description":"Optional. Partial name match (LIKE %search%). Returns up to 50 results ordered by name.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"sort_order\": 3,\n      \"name\": \"Nike\",\n      \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2024-03-20T14:30:00.000000Z\",\n      \"archived_at\": null\n    },\n    {\n      \"id\": 8,\n      \"sort_order\": 3,\n      \"name\": \"Nike Sport\",\n      \"created_at\": \"2024-02-01T09:00:00.000000Z\",\n      \"updated_at\": \"2024-02-01T09:00:00.000000Z\",\n      \"archived_at\": null\n    }\n  ]\n}"}]},{"name":"Preview Product Brands Import","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/product-brands/import/preview","host":["{{baseUrl}}"],"path":["api","product-brands","import","preview"]},"description":"Preview the result of importing product brands from a CSV/Excel file without committing the import.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-brands/import/preview","host":["{{baseUrl}}"],"path":["api","product-brands","import","preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"headers\": [\n      \"name\"\n    ],\n    \"rows\": [\n      {\n        \"name\": \"New Brand A\",\n        \"status\": \"create\"\n      },\n      {\n        \"name\": \"Nike\",\n        \"status\": \"update\"\n      }\n    ],\n    \"total\": 2,\n    \"errors\": []\n  }\n}"}]},{"name":"Import Product Brands","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/product-brands/import","host":["{{baseUrl}}"],"path":["api","product-brands","import"]},"description":"Imports product brands from a CSV or Excel file. Each row must have a `name` column.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-brands/import","host":["{{baseUrl}}"],"path":["api","product-brands","import"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Import completed: 10 created, 2 updated, 0 errors\"\n}"}]},{"name":"Get Brand Detail Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand"],"variable":[{"key":"brand","value":"1","description":"Brand ID (archived brands are resolvable)"}]},"description":"Returns a single brand with lightweight aggregate stats for the Brand detail page header. Includes archived brands (a deep-link to an archived brand still resolves).\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nResponse fields: id, name, archived_at, created_at, updated_at, products_count (count of products assigned to the brand).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand"],"variable":[{"key":"brand","value":"1","description":"Brand ID (archived brands are resolvable)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"sort_order\": 3,\n    \"name\": \"Nike\",\n    \"archived_at\": null,\n    \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2024-03-20T14:30:00.000000Z\",\n    \"products_count\": 128\n  }\n}"}]},{"name":"Get Brand Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/activity-log","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","activity-log"],"variable":[{"key":"brand","value":"1","description":"Brand ID (archived brands are resolvable)"}]},"description":"Paginated, filterable activity log for a brand. Surfaces two signals: the brand's own events (rename, archive, restore) and product brand-assignment changes (a product's brand being set to or cleared from this brand).\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach entry: id, description, event, subject_type (ProductBrand or Product), subject_id, properties (attributes/old change diff), causer_name, created_at."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/brands/{brand}/activity-log","host":["{{baseUrl}}"],"path":["api","v2","brands",":brand","activity-log"],"variable":[{"key":"brand","value":"1","description":"Brand ID (archived brands are resolvable)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5012,\n      \"description\": null,\n      \"event\": \"updated\",\n      \"subject_type\": \"ProductBrand\",\n      \"subject_id\": 1,\n      \"properties\": {\n        \"attributes\": {\n          \"name\": \"Nike\"\n        },\n        \"old\": {\n          \"name\": \"Nike Inc\"\n        }\n      },\n      \"causer_name\": \"Jane Doe\",\n      \"created_at\": \"2024-03-20T14:30:00.000000Z\"\n    },\n    {\n      \"id\": 5008,\n      \"description\": null,\n      \"event\": \"created\",\n      \"subject_type\": \"Product\",\n      \"subject_id\": 44,\n      \"properties\": {\n        \"attributes\": {\n          \"brand_id\": 1,\n          \"sku\": \"AIRMAX-90\"\n        }\n      },\n      \"causer_name\": \"System\",\n      \"created_at\": \"2024-03-19T09:15:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 2,\n  \"from\": 1,\n  \"to\": 2\n}"}]},{"name":"Get Category","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/categories/{category}","host":["{{baseUrl}}"],"path":["api","categories",":category"],"variable":[{"key":"category","value":"1","description":"Category ID"}]},"description":"Show a single product category with full detail including children, attribute groups, attributes, parent, and root info.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nResponse includes:\n- `products_count` — total products assigned to this category and all sub-categories\n- `products_ids` — first 10 product IDs assigned\n- `subcategories_count` — count of all subcategories\n- `attribute_groups` / `attributes` — linked attribute groups and attributes"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/categories/{category}","host":["{{baseUrl}}"],"path":["api","categories",":category"],"variable":[{"key":"category","value":"1","description":"Category ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Electronics\",\n    \"parent_id\": null,\n    \"parent_name\": null,\n    \"category_tree\": null,\n    \"is_leaf\": false,\n    \"level\": 0,\n    \"products_count\": 128,\n    \"products_ids\": [\n      10,\n      11,\n      12,\n      13,\n      14,\n      15,\n      16,\n      17,\n      18,\n      19\n    ],\n    \"subcategories_count\": 4,\n    \"attribute_groups_count\": 2,\n    \"attribute_groups\": [\n      {\n        \"id\": 1,\n        \"name\": \"Technical Specs\"\n      }\n    ],\n    \"attributes_count\": 5,\n    \"attributes\": [\n      {\n        \"id\": 3,\n        \"name\": \"Color\",\n        \"type\": \"text\"\n      },\n      {\n        \"id\": 4,\n        \"name\": \"Weight\",\n        \"type\": \"number\"\n      }\n    ],\n    \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2024-01-20T08:30:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Update Category","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/categories/{category}","host":["{{baseUrl}}"],"path":["api","categories",":category"],"variable":[{"key":"category","value":"5","description":"Category ID"}]},"description":"Update an existing product category. Attribute groups and attributes are synced (replaced entirely if provided).\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body fields:\n- `name` (optional) — category name; must be unique within the same parent\n- `parent_id` (optional) — parent category ID\n- `attribute_groups` (optional) — array of attribute group IDs to sync\n- `attributes` (optional) — array of attribute IDs to sync\n\n**Requires permission:** `categories.update`","body":{"mode":"raw","raw":"{\n  \"name\": \"Gaming Laptops\",\n  \"parent_id\": 1,\n  \"attribute_groups\": [\n    1\n  ],\n  \"attributes\": [\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/categories/{category}","host":["{{baseUrl}}"],"path":["api","categories",":category"],"variable":[{"key":"category","value":"5","description":"Category ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Gaming Laptops\",\n    \"parent_id\": 1,\n    \"parent_name\": null,\n    \"is_leaf\": true,\n    \"level\": 1,\n    \"created_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"updated_at\": \"2024-03-02T09:15:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"category Gaming Laptops was updated successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Delete Category","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/categories/{category}","host":["{{baseUrl}}"],"path":["api","categories",":category"],"variable":[{"key":"category","value":"5","description":"Category ID"}]},"description":"Delete a product category. Only leaf categories (is_leaf = true) can be deleted.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 400 if the category is not a leaf node (has children).\n\n**Requires permission:** `categories.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/categories/{category}","host":["{{baseUrl}}"],"path":["api","categories",":category"],"variable":[{"key":"category","value":"5","description":"Category ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"category and its associations Gaming Laptops was deleted successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"List Categories","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/categories?page=1&limit=15","host":["{{baseUrl}}"],"path":["api","categories"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"15","description":"Records per page (default: 15)","disabled":true}]},"description":"List product categories. Supports search, sort, pagination, and archiving filters.\n\nAuthentication: Requires Bearer token.\n\nQuery parameters:\n- `page` / `limit` — pagination\n- `search` — full-text search on name\n- `sort` — sortable columns: id, name, created_at, updated_at, archived_at\n- `archived` — 0 (active), 1 (archived)\n- `filter[name]` — filter by name\n- `included[]` / `excluded[]` — force-include or exclude specific IDs"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/categories?page=1&limit=15","host":["{{baseUrl}}"],"path":["api","categories"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"15","description":"Records per page (default: 15)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Electronics\",\n      \"parent_id\": null,\n      \"parent_name\": null,\n      \"is_leaf\": false,\n      \"level\": 0,\n      \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2024-01-20T08:30:00.000000Z\",\n      \"archived_at\": null\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Smartphones\",\n      \"parent_id\": 1,\n      \"parent_name\": \"Electronics\",\n      \"is_leaf\": true,\n      \"level\": 1,\n      \"created_at\": \"2024-01-16T09:00:00.000000Z\",\n      \"updated_at\": \"2024-01-21T12:00:00.000000Z\",\n      \"archived_at\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 15,\n  \"total\": 42,\n  \"status\": \"success\"\n}"}]},{"name":"Create Category","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/categories","host":["{{baseUrl}}"],"path":["api","categories"]},"description":"Create a new product category.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body fields:\n- `name` (required) — category name; must be unique within the same parent\n- `parent_id` (optional) — parent category ID; null creates a root category\n- `attribute_groups` (optional) — array of attribute group IDs to link\n- `attributes` (optional) — array of attribute IDs to link\n\n**Requires permission:** `categories.create`","body":{"mode":"raw","raw":"{\n  \"name\": \"Laptops\",\n  \"parent_id\": 1,\n  \"attribute_groups\": [\n    1,\n    2\n  ],\n  \"attributes\": [\n    3,\n    4,\n    5\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/categories","host":["{{baseUrl}}"],"path":["api","categories"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Laptops\",\n    \"parent_id\": 1,\n    \"parent_name\": null,\n    \"is_leaf\": true,\n    \"level\": 1,\n    \"created_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"updated_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"category was created successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Delete Categories","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/categories","host":["{{baseUrl}}"],"path":["api","categories"]},"description":"Bulk delete multiple product categories by their IDs. Only leaf categories can be deleted.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- `ids` (required) — array of category IDs to delete\n\n**Requires permission:** `categories.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/categories","host":["{{baseUrl}}"],"path":["api","categories"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 categories deleted successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Category Tree","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/categories/tree","host":["{{baseUrl}}"],"path":["api","categories","tree"]},"description":"Returns all categories as a nested tree structure for hierarchical display.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nEach node includes: id, name, parent_id, is_leaf, subcategories_count, children[]"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/categories/tree","host":["{{baseUrl}}"],"path":["api","categories","tree"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Electronics\",\n      \"parent_id\": null,\n      \"is_leaf\": false,\n      \"subcategories_count\": 2,\n      \"children\": [\n        {\n          \"id\": 2,\n          \"name\": \"Smartphones\",\n          \"parent_id\": 1,\n          \"is_leaf\": true,\n          \"subcategories_count\": 0,\n          \"children\": []\n        },\n        {\n          \"id\": 3,\n          \"name\": \"Laptops\",\n          \"parent_id\": 1,\n          \"is_leaf\": true,\n          \"subcategories_count\": 0,\n          \"children\": []\n        }\n      ]\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"List Categories (Management View)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/categories/for-manage","host":["{{baseUrl}}"],"path":["api","categories","for-manage"]},"description":"Returns categories at a specific level for the product category assignment/manage UI. Includes counts for products, attributes, and attribute groups.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nQuery parameters:\n- `parent_id` (optional) — limit to children of this parent; omit for root-level categories"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/categories/for-manage","host":["{{baseUrl}}"],"path":["api","categories","for-manage"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Electronics\",\n      \"parent_id\": null,\n      \"children_count\": 3,\n      \"is_leaf\": false,\n      \"level\": 0,\n      \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2024-01-20T08:30:00.000000Z\",\n      \"archived_at\": null\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Assign Category to Product","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/categories/assign-category-to-product","host":["{{baseUrl}}"],"path":["api","categories","assign-category-to-product"]},"description":"Assign a category to a product and optionally set it as the primary category. Uses updateOrCreate so re-posting changes is_primary.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body fields:\n- `category_id` (required) — must be a leaf category\n- `product_id` (required) — must exist in products table\n- `is_primary` (optional, boolean) — if true, unsets primary from all other categories for this product\n\nReturns 201 on creation, 200 on update.\n\n**Requires permission:** `categories.update`","body":{"mode":"raw","raw":"{\n  \"category_id\": 2,\n  \"product_id\": 100,\n  \"is_primary\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/categories/assign-category-to-product","host":["{{baseUrl}}"],"path":["api","categories","assign-category-to-product"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"category was assigned to product, and set as primary category\",\n  \"status\": \"success\"\n}"}]},{"name":"Reassign Category to Products","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/categories/reassign-to-products","host":["{{baseUrl}}"],"path":["api","categories","reassign-to-products"]},"description":"Bulk reassign all products from one leaf category to another. Products already in the new category are detached from the old one.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body fields:\n- `old_category_id` (required) — must be an existing leaf category\n- `new_category_id` (required) — must be an existing leaf category; different from old_category_id\n\n**Requires permission:** `categories.update`","body":{"mode":"raw","raw":"{\n  \"old_category_id\": 3,\n  \"new_category_id\": 4\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/categories/reassign-to-products","host":["{{baseUrl}}"],"path":["api","categories","reassign-to-products"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"affected_records_count\": 47\n  },\n  \"message\": \"Products have been reassigned to the new category.\",\n  \"status\": \"success\"\n}"}]},{"name":"Archive Category","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/categories/{productCategoryId}/archive","host":["{{baseUrl}}"],"path":["api","categories",":productCategoryId","archive"],"variable":[{"key":"productCategoryId","value":"5","description":"Category ID"}]},"description":"Archive a single product category.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `categories.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/categories/{productCategoryId}/archive","host":["{{baseUrl}}"],"path":["api","categories",":productCategoryId","archive"],"variable":[{"key":"productCategoryId","value":"5","description":"Category ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Laptops\",\n    \"archived_at\": \"2024-03-10T11:00:00.000000Z\"\n  },\n  \"message\": \"category Laptops was archived successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Unarchive Category","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/categories/{productCategoryId}/unarchived","host":["{{baseUrl}}"],"path":["api","categories",":productCategoryId","unarchived"],"variable":[{"key":"productCategoryId","value":"5","description":"Category ID"}]},"description":"Unarchive a single product category.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `categories.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/categories/{productCategoryId}/unarchived","host":["{{baseUrl}}"],"path":["api","categories",":productCategoryId","unarchived"],"variable":[{"key":"productCategoryId","value":"5","description":"Category ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Laptops\",\n    \"archived_at\": null\n  },\n  \"message\": \"category Laptops was unarchived successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Archive Categories","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/categories/archive","host":["{{baseUrl}}"],"path":["api","categories","archive"]},"description":"Bulk archive multiple product categories by their IDs.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- `ids` (required) — array of category IDs to archive\n\n**Requires permission:** `categories.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    5,\n    6,\n    7\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/categories/archive","host":["{{baseUrl}}"],"path":["api","categories","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 categories archived successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Unarchive Categories","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/categories/unarchive","host":["{{baseUrl}}"],"path":["api","categories","unarchive"]},"description":"Bulk unarchive multiple product categories by their IDs.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- `ids` (required) — array of category IDs to unarchive\n\n**Requires permission:** `categories.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    5,\n    6,\n    7\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/categories/unarchive","host":["{{baseUrl}}"],"path":["api","categories","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 categories unarchived successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Check Categories Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/categories/deletable","host":["{{baseUrl}}"],"path":["api","categories","deletable"]},"description":"Check which categories can be deleted. Categories with children (is_leaf = false) cannot be deleted.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- `ids` (required) — array of category IDs to check\n\n**Requires permission:** `categories.delete`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    5\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/categories/deletable","host":["{{baseUrl}}"],"path":["api","categories","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Electronics\",\n      \"deletable\": false,\n      \"reason\": {\n        \"productCategory\": \"Category cannot be deleted as it has sub-categories.\"\n      }\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Smartphones\",\n      \"deletable\": true,\n      \"reason\": null\n    },\n    {\n      \"id\": 5,\n      \"name\": \"Laptops\",\n      \"deletable\": true,\n      \"reason\": null\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Preview Categories Import","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/categories/import/preview","host":["{{baseUrl}}"],"path":["api","categories","import","preview"]},"description":"Preview a category import file before committing. Returns parsed rows and any validation errors.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data\n- `file` (required) — CSV or Excel file\n\n**Requires permission:** `categories.import`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/categories/import/preview","host":["{{baseUrl}}"],"path":["api","categories","import","preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"rows\": [\n      {\n        \"name\": \"Tablets\",\n        \"parent_id\": 1,\n        \"valid\": true,\n        \"errors\": []\n      }\n    ],\n    \"total\": 1,\n    \"valid\": 1,\n    \"invalid\": 0\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Import Categories","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/categories/import","host":["{{baseUrl}}"],"path":["api","categories","import"]},"description":"Import categories from a CSV or Excel file.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data\n- `file` (required) — CSV or Excel file\n\n**Requires permission:** `categories.import`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/categories/import","host":["{{baseUrl}}"],"path":["api","categories","import"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"imported\": 5,\n    \"skipped\": 0,\n    \"errors\": []\n  },\n  \"message\": \"Import completed successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Get Attribute","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/attributes/{id}","host":["{{baseUrl}}"],"path":["api","attributes",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns a single attribute with its group and option values.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/attributes/{id}","host":["{{baseUrl}}"],"path":["api","attributes",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Color\",\n    \"type\": \"string\",\n    \"attribute_group_id\": 1,\n    \"attribute_group\": {\n      \"id\": 1,\n      \"name\": \"Physical Attributes\"\n    },\n    \"display_options\": {\n      \"placeholder\": \"Enter color\"\n    },\n    \"validation\": null,\n    \"available_for_templates\": true,\n    \"option_values\": [\n      {\n        \"value\": \"Red\",\n        \"sort_order\": 1\n      },\n      {\n        \"value\": \"Blue\",\n        \"sort_order\": 2\n      }\n    ],\n    \"notes\": \"Primary product color\",\n    \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2024-06-01T14:30:00.000000Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Update Attribute","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/attributes/{id}","host":["{{baseUrl}}"],"path":["api","attributes",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Updates an existing attribute. All fields are optional (PATCH semantics). Providing option_values syncs them — values not included will be deleted.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nEach entry in `option_values` may carry an optional variant SWATCH — `swatch_hex` (a hex colour, max 20) and/or `swatch_image_url` (an image URL, max 500). When the attribute is used as a variant axis (e.g. Color), channels that support swatches (Walmart) render this chip for every variant that selects the value. Send null to clear a swatch.\n\nReturns **422** with code `IsLinked` if the update would remove an option value that is still assigned to a product — the response lists the in-use values under `errors[].data.in_use`. Add those values back, or reassign/clear them on the affected products, before removing the option.","body":{"mode":"raw","raw":"{\n  \"name\": \"Product Color\",\n  \"type\": \"string\",\n  \"option_values\": [\n    {\n      \"value\": \"Red\",\n      \"sort_order\": 1,\n      \"swatch_hex\": \"#FF0000\",\n      \"swatch_image_url\": null\n    },\n    {\n      \"value\": \"Blue\",\n      \"sort_order\": 2,\n      \"swatch_hex\": \"#0000FF\",\n      \"swatch_image_url\": null\n    },\n    {\n      \"value\": \"Green\",\n      \"sort_order\": 3,\n      \"swatch_hex\": \"#00A651\",\n      \"swatch_image_url\": \"https://cdn.example.com/swatches/green.jpg\"\n    }\n  ],\n  \"available_for_templates\": true,\n  \"notes\": \"Updated color attribute\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/attributes/{id}","host":["{{baseUrl}}"],"path":["api","attributes",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"attribute 'Product Color' updated successfully\",\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Product Color\",\n    \"type\": \"string\",\n    \"attribute_group_id\": 1,\n    \"display_options\": null,\n    \"option_values\": [\n      {\n        \"value\": \"Red\",\n        \"sort_order\": 1\n      },\n      {\n        \"value\": \"Blue\",\n        \"sort_order\": 2\n      },\n      {\n        \"value\": \"Green\",\n        \"sort_order\": 3\n      }\n    ],\n    \"notes\": \"Updated color attribute\",\n    \"updated_at\": \"2024-06-15T14:00:00.000000Z\"\n  }\n}"}]},{"name":"Delete Attribute","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/attributes/{id}?ignore_relations=0","host":["{{baseUrl}}"],"path":["api","attributes",":id"],"query":[{"key":"ignore_relations","value":"0","description":"Set to 1 to force delete even if used by products","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Deletes an attribute along with its values and product attribute assignments. Returns an error if the attribute is used by products unless ignore_relations=1.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nQuery params:\n- ignore_relations (optional): 0 (default) = block if used, 1 = force delete"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/attributes/{id}?ignore_relations=0","host":["{{baseUrl}}"],"path":["api","attributes",":id"],"query":[{"key":"ignore_relations","value":"0","description":"Set to 1 to force delete even if used by products","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"attribute 'Color' deleted successfully\"\n}"}]},{"name":"Get Attributes Grouped","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/attributes/grouped","host":["{{baseUrl}}"],"path":["api","attributes","grouped"]},"description":"Returns all attributes organized by their attribute group, plus a list of ungrouped attributes.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/attributes/grouped","host":["{{baseUrl}}"],"path":["api","attributes","grouped"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"grouped\": [\n    {\n      \"id\": 1,\n      \"name\": \"Physical Attributes\",\n      \"sort_order\": 1,\n      \"attributes\": [\n        {\n          \"id\": 1,\n          \"name\": \"Color\",\n          \"type\": \"string\",\n          \"sort_order\": 1,\n          \"attribute_id\": 1,\n          \"display_options\": null,\n          \"validation\": null\n        }\n      ]\n    }\n  ],\n  \"ungrouped\": [\n    {\n      \"id\": 10,\n      \"name\": \"Internal Notes\"\n    }\n  ]\n}"}]},{"name":"List Attributes","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/attributes?page=1&limit=15&sort=name&search=","host":["{{baseUrl}}"],"path":["api","attributes"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"limit","value":"15","description":"Records per page","disabled":true},{"key":"sort","value":"name","description":"Sort field (prefix with - for descending)","disabled":true},{"key":"search","value":"","description":"Search by attribute name","disabled":true}]},"description":"Returns a paginated list of attributes.\n\nAuthentication: Requires Bearer token.\n\nSort fields: name, type, created_at, updated_at, archived_at"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/attributes?page=1&limit=15&sort=name&search=","host":["{{baseUrl}}"],"path":["api","attributes"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"limit","value":"15","description":"Records per page","disabled":true},{"key":"sort","value":"name","description":"Sort field (prefix with - for descending)","disabled":true},{"key":"search","value":"","description":"Search by attribute name","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Color\",\n      \"type\": \"string\",\n      \"attribute_group_id\": 1,\n      \"notes\": null,\n      \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2024-06-01T14:30:00.000000Z\",\n      \"archived_at\": null,\n      \"display_options\": null,\n      \"validation\": null,\n      \"available_for_templates\": false\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 15,\n  \"total\": 38\n}"}]},{"name":"Create Attribute","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/attributes","host":["{{baseUrl}}"],"path":["api","attributes"]},"description":"Creates a new attribute.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- name (required): Unique name, max 255 chars\n- type (required): One of: string, longtext, date, datetime, numeric, integer, checkbox\n- display_options (optional): JSON object with display configuration\n- option_values (optional): Array of selectable options for dropdown-type attributes\n  - option_values.*.value (optional): Option value string, max 255 chars\n  - option_values.*.sort_order (optional): Sort order integer\n- validation (optional): JSON validation rules\n- available_for_templates (optional): Boolean — makes attribute available in email templates\n- attribute_group_id (optional): ID of the group to assign this attribute to\n- notes (optional): Internal notes, max 255 chars\n\n**Requires permission:** `attributes.create`\n\nEach entry in `option_values` may carry an optional variant SWATCH — `swatch_hex` (a hex colour, max 20) and/or `swatch_image_url` (an image URL, max 500). When the attribute is used as a variant axis (e.g. Color), channels that support swatches (Walmart) render this chip for every variant that selects the value. Send null to clear a swatch.","body":{"mode":"raw","raw":"{\n  \"name\": \"Color\",\n  \"type\": \"string\",\n  \"display_options\": {\n    \"has_options\": true,\n    \"type\": \"Multi\",\n    \"placeholder\": \"Enter color\"\n  },\n  \"option_values\": [\n    {\n      \"value\": \"Red\",\n      \"sort_order\": 1,\n      \"swatch_hex\": \"#FF0000\",\n      \"swatch_image_url\": \"https://cdn.example.com/swatches/red.jpg\"\n    },\n    {\n      \"value\": \"Blue\",\n      \"sort_order\": 2,\n      \"swatch_hex\": \"#0000FF\",\n      \"swatch_image_url\": null\n    }\n  ],\n  \"validation\": null,\n  \"available_for_templates\": true,\n  \"attribute_group_id\": 1,\n  \"notes\": \"Primary product color\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/attributes","host":["{{baseUrl}}"],"path":["api","attributes"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"attribute created successfully\",\n  \"data\": {\n    \"id\": 15,\n    \"name\": \"Color\",\n    \"type\": \"string\",\n    \"attribute_group_id\": 1,\n    \"display_options\": {\n      \"placeholder\": \"Enter color\"\n    },\n    \"validation\": null,\n    \"available_for_templates\": true,\n    \"option_values\": [\n      {\n        \"value\": \"Red\",\n        \"sort_order\": 1\n      },\n      {\n        \"value\": \"Blue\",\n        \"sort_order\": 2\n      }\n    ],\n    \"notes\": \"Primary product color\",\n    \"created_at\": \"2024-06-15T12:00:00.000000Z\",\n    \"updated_at\": \"2024-06-15T12:00:00.000000Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Bulk Delete Attributes","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/attributes","host":["{{baseUrl}}"],"path":["api","attributes"]},"description":"Bulk deletes multiple attributes by ID array or current list view filters.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `attributes.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/attributes","host":["{{baseUrl}}"],"path":["api","attributes"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Attributes deleted successfully\"\n}"}]},{"name":"Set Attribute Option Swatch","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/attributes/{attributeId}/option-swatch","host":["{{baseUrl}}"],"path":["api","attributes",":attributeId","option-swatch"],"variable":[{"key":"attributeId","value":"1","description":"ID of the attribute (e.g. Color) that owns the option value."}]},"description":"Sets the swatch (image URL and/or colour hex) on a single option value of an attribute, creating the option value if it does not exist yet. The swatch is shared by every product variant that selects this value.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest body:\n- value (string, required): the option value to attach the swatch to, e.g. \"Blue\".\n- swatch_image_url (string, optional): public image URL for the swatch (max 500 chars).\n- swatch_hex (string, optional): colour hex for the swatch (max 20 chars).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"value\": \"Blue\",\n  \"swatch_image_url\": \"https://cdn.example.com/swatches/blue.jpg\",\n  \"swatch_hex\": \"#0000FF\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/attributes/{attributeId}/option-swatch","host":["{{baseUrl}}"],"path":["api","attributes",":attributeId","option-swatch"],"variable":[{"key":"attributeId","value":"1","description":"ID of the attribute (e.g. Color) that owns the option value."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"attribute 'Color' updated successfully\",\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Color\",\n    \"type\": \"string\",\n    \"values\": [\n      {\n        \"id\": 10,\n        \"value\": \"Blue\",\n        \"swatch_hex\": \"#0000FF\",\n        \"swatch_image_url\": \"https://cdn.example.com/swatches/blue.jpg\",\n        \"sort_order\": 0\n      }\n    ]\n  }\n}"}]},{"name":"Archive Attribute","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/attributes/{id}/archive","host":["{{baseUrl}}"],"path":["api","attributes",":id","archive"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Archives a single attribute. Idempotent — returns a warning if already archived.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/attributes/{id}/archive","host":["{{baseUrl}}"],"path":["api","attributes",":id","archive"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"attribute 'Color' archived successfully\",\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Color\",\n    \"archived_at\": \"2024-06-15T10:00:00.000000Z\"\n  }\n}"}]},{"name":"Unarchive Attribute","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/attributes/{id}/unarchived","host":["{{baseUrl}}"],"path":["api","attributes",":id","unarchived"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Unarchives a single attribute.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/attributes/{id}/unarchived","host":["{{baseUrl}}"],"path":["api","attributes",":id","unarchived"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"attribute 'Color' unarchived successfully\",\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Color\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Bulk Archive Attributes","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/attributes/archive","host":["{{baseUrl}}"],"path":["api","attributes","archive"]},"description":"Bulk archives multiple attributes by ID array or current list view filters.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `attributes.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/attributes/archive","host":["{{baseUrl}}"],"path":["api","attributes","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Attributes archived successfully\"\n}"}]},{"name":"Bulk Unarchive Attributes","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/attributes/unarchive","host":["{{baseUrl}}"],"path":["api","attributes","unarchive"]},"description":"Bulk unarchives multiple attributes by ID array or current list view filters.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `attributes.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/attributes/unarchive","host":["{{baseUrl}}"],"path":["api","attributes","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Attributes unarchived successfully\"\n}"}]},{"name":"Check Attributes Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/attributes/deletable","host":["{{baseUrl}}"],"path":["api","attributes","deletable"]},"description":"Checks whether each provided attribute ID can be safely deleted.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- ids (required): Array of attribute IDs to check (integers that exist in attributes table)\n\n**Requires permission:** `attributes.delete`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/attributes/deletable","host":["{{baseUrl}}"],"path":["api","attributes","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Color\",\n      \"deletable\": false,\n      \"reason\": \"Used by 5 products\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Size\",\n      \"deletable\": true,\n      \"reason\": null\n    }\n  ]\n}"}]},{"name":"Import Attributes","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/attributes/import","host":["{{baseUrl}}"],"path":["api","attributes","import"]},"description":"Imports attributes from a CSV or Excel file.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data\n\n**Requires permission:** `attributes.import`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/attributes/import","host":["{{baseUrl}}"],"path":["api","attributes","import"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Import completed\",\n  \"data\": {\n    \"imported\": 10,\n    \"errors\": []\n  }\n}"}]},{"name":"Preview Attributes Import","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/attributes/import/preview","host":["{{baseUrl}}"],"path":["api","attributes","import","preview"]},"description":"Previews attribute import data from a file without actually importing.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data\n\n**Requires permission:** `attributes.import`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/attributes/import/preview","host":["{{baseUrl}}"],"path":["api","attributes","import","preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": {\n    \"rows\": [\n      {\n        \"name\": \"Material\",\n        \"type\": \"string\",\n        \"valid\": true,\n        \"errors\": []\n      }\n    ],\n    \"total\": 1,\n    \"valid\": 1,\n    \"invalid\": 0\n  }\n}"}]},{"name":"List Attribute Groups (Management View)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/attribute-groups/for-manage","host":["{{baseUrl}}"],"path":["api","attribute-groups","for-manage"]},"description":"Returns all attribute groups ordered by sort_order, including nested attributes, product counts, and category counts. Used for the attribute group management page.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/attribute-groups/for-manage","host":["{{baseUrl}}"],"path":["api","attribute-groups","for-manage"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Physical Attributes\",\n      \"sort_order\": 1,\n      \"num_attributes\": 2,\n      \"num_categories\": 1,\n      \"num_products\": 15,\n      \"attributes\": [\n        {\n          \"id\": 1,\n          \"name\": \"Color\",\n          \"type\": \"string\",\n          \"sort_order\": 1,\n          \"attribute_id\": 1,\n          \"display_options\": null,\n          \"validation\": null\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"Get Attribute Group","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/attribute-groups/{id}","host":["{{baseUrl}}"],"path":["api","attribute-groups",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns a single attribute group with full details including products, categories, and attributes.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/attribute-groups/{id}","host":["{{baseUrl}}"],"path":["api","attribute-groups",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Physical Attributes\",\n    \"sort_order\": 1,\n    \"products_count_directly\": 10,\n    \"products_ids_directly\": [\n      101,\n      102,\n      103\n    ],\n    \"products_count_through_categories\": 25,\n    \"products_ids_through_categories\": [\n      104,\n      105,\n      106\n    ],\n    \"attributes_count\": 2,\n    \"attributes\": [\n      {\n        \"id\": 1,\n        \"name\": \"Color\",\n        \"type\": \"string\",\n        \"sort_order\": 1,\n        \"attribute_id\": 1,\n        \"display_options\": null,\n        \"validation\": null\n      }\n    ],\n    \"categories_count\": 1,\n    \"categories\": [\n      {\n        \"id\": 5,\n        \"name\": \"Apparel\"\n      }\n    ]\n  }\n}"}]},{"name":"Update Attribute Group","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/attribute-groups/{id}","host":["{{baseUrl}}"],"path":["api","attribute-groups",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Updates an existing attribute group. All fields are optional (PATCH semantics on PUT). Providing the attributes array syncs assigned attributes — attributes not included will be removed from the group.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"name\": \"Updated Physical Attributes\",\n  \"sort_order\": 2,\n  \"attributes\": [\n    {\n      \"id\": 1,\n      \"sort_order\": 1\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/attribute-groups/{id}","host":["{{baseUrl}}"],"path":["api","attribute-groups",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Attribute group 'Updated Physical Attributes' updated successfully\",\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Updated Physical Attributes\",\n    \"sort_order\": 2,\n    \"attributes_count\": 1,\n    \"attributes\": [\n      {\n        \"id\": 1,\n        \"name\": \"Color\",\n        \"type\": \"string\",\n        \"sort_order\": 1,\n        \"attribute_id\": 1,\n        \"display_options\": null,\n        \"validation\": null\n      }\n    ]\n  }\n}"}]},{"name":"Delete Attribute Group","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/attribute-groups/{id}","host":["{{baseUrl}}"],"path":["api","attribute-groups",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Deletes a single attribute group. Returns an error if the group is linked to categories or products.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/attribute-groups/{id}","host":["{{baseUrl}}"],"path":["api","attribute-groups",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Attribute group 'Physical Attributes' deleted successfully\"\n}"}]},{"name":"List Attribute Groups","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/attribute-groups?page=1&limit=15&sort=name&search=","host":["{{baseUrl}}"],"path":["api","attribute-groups"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"limit","value":"15","description":"Records per page","disabled":true},{"key":"sort","value":"name","description":"Sort field (prefix with - for descending, e.g. -name)","disabled":true},{"key":"search","value":"","description":"Search by attribute group name","disabled":true}]},"description":"Returns a paginated list of attribute groups. Supports search, sort, and archive filtering.\n\nAuthentication: Requires Bearer token.\n\nSort fields: name, sort_order, created_at, updated_at, archived_at"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/attribute-groups?page=1&limit=15&sort=name&search=","host":["{{baseUrl}}"],"path":["api","attribute-groups"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"limit","value":"15","description":"Records per page","disabled":true},{"key":"sort","value":"name","description":"Sort field (prefix with - for descending, e.g. -name)","disabled":true},{"key":"search","value":"","description":"Search by attribute group name","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Physical Attributes\",\n      \"sort_order\": 1,\n      \"num_attributes\": 5,\n      \"num_categories\": 3,\n      \"num_products\": 42,\n      \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2024-06-01T14:30:00.000000Z\",\n      \"archived_at\": null,\n      \"attributes\": [\n        {\n          \"id\": 1,\n          \"name\": \"Color\",\n          \"type\": \"string\",\n          \"sort_order\": 1,\n          \"attribute_id\": 1,\n          \"display_options\": null,\n          \"validation\": null\n        },\n        {\n          \"id\": 2,\n          \"name\": \"Weight\",\n          \"type\": \"numeric\",\n          \"sort_order\": 2,\n          \"attribute_id\": 2,\n          \"display_options\": null,\n          \"validation\": null\n        }\n      ]\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1\n}"}]},{"name":"Create Attribute Group","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/attribute-groups","host":["{{baseUrl}}"],"path":["api","attribute-groups"]},"description":"Creates a new attribute group. Optionally assigns existing attributes to the group.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFields:\n- name (required): Unique name for the group, max 255 chars\n- sort_order (optional): Integer sort position\n- attributes (optional): Array of attribute objects to assign\n  - attributes.*.id (required): ID of an existing attribute\n  - attributes.*.sort_order (optional): Sort order within the group\n\nNote: An attribute can only belong to one group — assigning it to a new group removes it from the previous one.\n\n**Requires permission:** `attribute_groups.create`","body":{"mode":"raw","raw":"{\n  \"name\": \"Physical Attributes\",\n  \"sort_order\": 1,\n  \"attributes\": [\n    {\n      \"id\": 1,\n      \"sort_order\": 1\n    },\n    {\n      \"id\": 2,\n      \"sort_order\": 2\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/attribute-groups","host":["{{baseUrl}}"],"path":["api","attribute-groups"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Attribute group created successfully\",\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Physical Attributes\",\n    \"sort_order\": 1,\n    \"attributes_count\": 2,\n    \"attributes\": [\n      {\n        \"id\": 1,\n        \"name\": \"Color\",\n        \"type\": \"string\",\n        \"sort_order\": 1,\n        \"attribute_id\": 1,\n        \"display_options\": null,\n        \"validation\": null\n      }\n    ]\n  }\n}"}]},{"name":"Bulk Delete Attribute Groups","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/attribute-groups","host":["{{baseUrl}}"],"path":["api","attribute-groups"]},"description":"Bulk deletes multiple attribute groups by ID array or by current list view filters.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `attribute_groups.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/attribute-groups","host":["{{baseUrl}}"],"path":["api","attribute-groups"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Attribute groups deleted successfully\"\n}"}]},{"name":"Archive Attribute Group","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/attribute-groups/{id}/archive","host":["{{baseUrl}}"],"path":["api","attribute-groups",":id","archive"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Archives a single attribute group by ID. Idempotent — returns a warning if already archived.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/attribute-groups/{id}/archive","host":["{{baseUrl}}"],"path":["api","attribute-groups",":id","archive"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Attribute group 'Physical Attributes' archived successfully\",\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Physical Attributes\",\n    \"archived_at\": \"2024-06-15T10:00:00.000000Z\"\n  }\n}"}]},{"name":"Unarchive Attribute Group","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/attribute-groups/{id}/unarchived","host":["{{baseUrl}}"],"path":["api","attribute-groups",":id","unarchived"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Unarchives a single attribute group by ID.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/attribute-groups/{id}/unarchived","host":["{{baseUrl}}"],"path":["api","attribute-groups",":id","unarchived"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Attribute group 'Physical Attributes' unarchived successfully\",\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Physical Attributes\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Bulk Archive Attribute Groups","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/attribute-groups/archive","host":["{{baseUrl}}"],"path":["api","attribute-groups","archive"]},"description":"Bulk archives multiple attribute groups by ID array or by current list view filters.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `attribute_groups.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/attribute-groups/archive","host":["{{baseUrl}}"],"path":["api","attribute-groups","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Attribute groups archived successfully\"\n}"}]},{"name":"Bulk Unarchive Attribute Groups","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/attribute-groups/unarchive","host":["{{baseUrl}}"],"path":["api","attribute-groups","unarchive"]},"description":"Bulk unarchives multiple attribute groups by ID array or by current list view filters.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `attribute_groups.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/attribute-groups/unarchive","host":["{{baseUrl}}"],"path":["api","attribute-groups","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Attribute groups unarchived successfully\"\n}"}]},{"name":"Check Attribute Groups Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/attribute-groups/deletable","host":["{{baseUrl}}"],"path":["api","attribute-groups","deletable"]},"description":"Checks whether each provided attribute group ID can be safely deleted (not linked to categories or products).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nFields:\n- ids (required): Array of attribute group IDs to check\n\n**Requires permission:** `attribute_groups.delete`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/attribute-groups/deletable","host":["{{baseUrl}}"],"path":["api","attribute-groups","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Physical Attributes\",\n      \"deletable\": true,\n      \"reason\": null\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Dimensions\",\n      \"deletable\": false,\n      \"reason\": \"Linked to categories\"\n    }\n  ]\n}"}]},{"name":"Import Attribute Groups","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/attribute-groups/import","host":["{{baseUrl}}"],"path":["api","attribute-groups","import"]},"description":"Imports attribute groups from a CSV or Excel file.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data\n\n**Requires permission:** `attribute_groups.import`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/attribute-groups/import","host":["{{baseUrl}}"],"path":["api","attribute-groups","import"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Import completed successfully\",\n  \"data\": {\n    \"imported\": 5,\n    \"errors\": []\n  }\n}"}]},{"name":"Preview Attribute Groups Import","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/attribute-groups/import/preview","host":["{{baseUrl}}"],"path":["api","attribute-groups","import","preview"]},"description":"Previews attribute group import data from a file without actually importing. Returns rows with validation status.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data\n\n**Requires permission:** `attribute_groups.import`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/attribute-groups/import/preview","host":["{{baseUrl}}"],"path":["api","attribute-groups","import","preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": {\n    \"rows\": [\n      {\n        \"name\": \"New Group\",\n        \"sort_order\": 1,\n        \"valid\": true,\n        \"errors\": []\n      }\n    ],\n    \"total\": 1,\n    \"valid\": 1,\n    \"invalid\": 0\n  }\n}"}]},{"name":"List Product Types","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/product-types","host":["{{baseUrl}}"],"path":["api","v2","product-types"]},"description":"List product types as a flat, path-ordered set — parents come before their children — with the number of active products each carries.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPagination: `page`, `per_page` (default 10). Pass `per_page=-1` to receive every type on one page, which is the usual way to load the whole tree for a picker.\n\nFilters:\n- `filter[search]` — matches the id exactly, or any part of the name or path\n- `filter[archived]` — omit for active types only; `only` for archived types; `all` for both\n- `filter[parent_id]` — direct children of one type; `root` for top-level types\n- `filter[is_leaf]` — `true` for leaf types (the only ones products and channel category maps can use), `false` for parent types\n- Operator filters on `id`, `name`, `path`, `created_at` and `updated_at` — `filter[name][contains]=diaper`, `filter[id][is_one_of]=3,7`, `filter[created_at][after]=2026-01-01`. Text operators: contains, does_not_contain, is, is_not, is_one_of, is_not_one_of, starts_with, does_not_start_with, ends_with, does_not_end_with, is_empty, is_not_empty. Numeric operators: is, is_not, is_one_of, is_not_one_of, greater_than, less_than, greater_than_or_equal, less_than_or_equal, between, is_empty, is_not_empty. Date operators: is, is_not, before, after, on_or_before, on_or_after, between, is_empty, is_not_empty, today, yesterday, tomorrow.\n- `filter_groups` — a base64-encoded JSON tree of AND/OR groups over the same columns, for compound conditions.\n\nAllowed sorts: id, name, path, level, sort_order, products_count, created_at, updated_at (prefix with `-` for descending). Default: `path`.\n\nSparse fieldsets: `included` / `excluded` (JSON arrays of field names) trim each row to the fields you need; `id` is always returned.\n\nEach row: `id`, `name`, `path` (the full `Parent > Child` path), `parent_id`, `level` (0 = top level; at most 3 levels), `is_leaf`, `sort_order`, `products_count` (active products carrying the type), `created_at`, `updated_at`, `archived_at`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/product-types","host":["{{baseUrl}}"],"path":["api","v2","product-types"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 3,\n      \"name\": \"Diapering\",\n      \"path\": \"Diapering\",\n      \"parent_id\": null,\n      \"level\": 0,\n      \"is_leaf\": false,\n      \"sort_order\": 0,\n      \"products_count\": 0,\n      \"created_at\": \"2026-09-01T09:12:00.000000Z\",\n      \"updated_at\": \"2026-09-01T09:12:00.000000Z\",\n      \"archived_at\": null\n    },\n    {\n      \"id\": 7,\n      \"name\": \"Cloth diapers\",\n      \"path\": \"Diapering > Cloth diapers\",\n      \"parent_id\": 3,\n      \"level\": 1,\n      \"is_leaf\": true,\n      \"sort_order\": 0,\n      \"products_count\": 48,\n      \"created_at\": \"2026-09-01T09:12:00.000000Z\",\n      \"updated_at\": \"2026-09-01T09:12:00.000000Z\",\n      \"archived_at\": null\n    },\n    {\n      \"id\": 8,\n      \"name\": \"Swim diapers\",\n      \"path\": \"Diapering > Swim diapers\",\n      \"parent_id\": 3,\n      \"level\": 1,\n      \"is_leaf\": true,\n      \"sort_order\": 1,\n      \"products_count\": 12,\n      \"created_at\": \"2026-09-01T09:12:00.000000Z\",\n      \"updated_at\": \"2026-09-01T09:12:00.000000Z\",\n      \"archived_at\": null\n    },\n    {\n      \"id\": 12,\n      \"name\": \"Wet bags\",\n      \"path\": \"Wet bags\",\n      \"parent_id\": null,\n      \"level\": 0,\n      \"is_leaf\": true,\n      \"sort_order\": 1,\n      \"products_count\": 9,\n      \"created_at\": \"2026-09-01T09:12:00.000000Z\",\n      \"updated_at\": \"2026-09-01T09:12:00.000000Z\",\n      \"archived_at\": null\n    }\n  ],\n  \"first_page_url\": \"https://sku2.test/api/v2/product-types?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://sku2.test/api/v2/product-types?page=1\",\n  \"links\": [\n    {\n      \"url\": null,\n      \"label\": \"&laquo; Previous\",\n      \"active\": false\n    },\n    {\n      \"url\": \"https://sku2.test/api/v2/product-types?page=1\",\n      \"label\": \"1\",\n      \"active\": true\n    },\n    {\n      \"url\": null,\n      \"label\": \"Next &raquo;\",\n      \"active\": false\n    }\n  ],\n  \"next_page_url\": null,\n  \"path\": \"https://sku2.test/api/v2/product-types\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 4,\n  \"total\": 4\n}"}]},{"name":"Create Product Type","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/product-types","host":["{{baseUrl}}"],"path":["api","v2","product-types"]},"description":"Create a product type. A new type is always a leaf; adding a child under an existing type turns that parent into a non-leaf. Products can only be assigned to leaf types.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- `name` (required, string, max 120) — runs of whitespace are collapsed; cannot contain ` > ` (the path separator); must be unique among its siblings, case-insensitively. An archived sibling with the same name blocks creation — restore it instead.\n- `parent_id` (nullable, integer) — the parent type; omit or null for a top-level type. The parent must be active, and the tree is at most 3 levels deep, so a type on the third level cannot take children.\n- `sort_order` (nullable, integer, min 0) — position among siblings.\n\n**Requires permission:** `product_types.create`","body":{"mode":"raw","raw":"{\n  \"name\": \"Cloth diapers\",\n  \"parent_id\": 3,\n  \"sort_order\": 0\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/product-types","host":["{{baseUrl}}"],"path":["api","v2","product-types"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 7,\n    \"name\": \"Cloth diapers\",\n    \"path\": \"Diapering > Cloth diapers\",\n    \"parent_id\": 3,\n    \"level\": 1,\n    \"is_leaf\": true,\n    \"sort_order\": 0,\n    \"products_count\": 0,\n    \"created_at\": \"2026-09-01T09:12:00.000000Z\",\n    \"updated_at\": \"2026-09-01T09:12:00.000000Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Get Product Type Tree","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/product-types/tree","host":["{{baseUrl}}"],"path":["api","v2","product-types","tree"]},"description":"Every active product type in one call, ordered by path so each parent precedes its children, with the active-product count per type. No pagination or filters — use it to render a picker or the full hierarchy. Archived types are not included.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPermission: `product_types.index` or `products.index` — users who can view products can read the tree without managing product types. Personal access tokens need the `products:read` scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/product-types/tree","host":["{{baseUrl}}"],"path":["api","v2","product-types","tree"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3,\n      \"name\": \"Diapering\",\n      \"path\": \"Diapering\",\n      \"parent_id\": null,\n      \"level\": 0,\n      \"is_leaf\": false,\n      \"sort_order\": 0,\n      \"products_count\": 0,\n      \"created_at\": \"2026-09-01T09:12:00.000000Z\",\n      \"updated_at\": \"2026-09-01T09:12:00.000000Z\",\n      \"archived_at\": null\n    },\n    {\n      \"id\": 7,\n      \"name\": \"Cloth diapers\",\n      \"path\": \"Diapering > Cloth diapers\",\n      \"parent_id\": 3,\n      \"level\": 1,\n      \"is_leaf\": true,\n      \"sort_order\": 0,\n      \"products_count\": 48,\n      \"created_at\": \"2026-09-01T09:12:00.000000Z\",\n      \"updated_at\": \"2026-09-01T09:12:00.000000Z\",\n      \"archived_at\": null\n    },\n    {\n      \"id\": 8,\n      \"name\": \"Swim diapers\",\n      \"path\": \"Diapering > Swim diapers\",\n      \"parent_id\": 3,\n      \"level\": 1,\n      \"is_leaf\": true,\n      \"sort_order\": 1,\n      \"products_count\": 12,\n      \"created_at\": \"2026-09-01T09:12:00.000000Z\",\n      \"updated_at\": \"2026-09-01T09:12:00.000000Z\",\n      \"archived_at\": null\n    },\n    {\n      \"id\": 12,\n      \"name\": \"Wet bags\",\n      \"path\": \"Wet bags\",\n      \"parent_id\": null,\n      \"level\": 0,\n      \"is_leaf\": true,\n      \"sort_order\": 1,\n      \"products_count\": 9,\n      \"created_at\": \"2026-09-01T09:12:00.000000Z\",\n      \"updated_at\": \"2026-09-01T09:12:00.000000Z\",\n      \"archived_at\": null\n    }\n  ]\n}"}]},{"name":"Get Product Type","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/product-types/{type}","host":["{{baseUrl}}"],"path":["api","v2","product-types",":type"],"variable":[{"key":"type","value":"7","description":"Product type id"}]},"description":"Get one product type by id. Archived types are returned too — a product may still reference one — with `archived_at` set.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/product-types/{type}","host":["{{baseUrl}}"],"path":["api","v2","product-types",":type"],"variable":[{"key":"type","value":"7","description":"Product type id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 7,\n    \"name\": \"Cloth diapers\",\n    \"path\": \"Diapering > Cloth diapers\",\n    \"parent_id\": 3,\n    \"level\": 1,\n    \"is_leaf\": true,\n    \"sort_order\": 0,\n    \"products_count\": 48,\n    \"created_at\": \"2026-09-01T09:12:00.000000Z\",\n    \"updated_at\": \"2026-09-01T09:12:00.000000Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Update Product Type","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/product-types/{type}","host":["{{baseUrl}}"],"path":["api","v2","product-types",":type"],"variable":[{"key":"type","value":"7","description":"Product type id"}]},"description":"Rename and/or move a product type. Fields you omit keep their current value. Renaming rewrites `path` for the type and its whole subtree; moving it re-parents the subtree and keeps the old and new parents' `is_leaf` flags truthful (a parent left with no children becomes a leaf again).\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- `name` (optional, string, max 120) — same rules as on create (no ` > `, unique among siblings, case-insensitive).\n- `parent_id` (optional, integer or null) — the new parent; null moves the type to the top level. A type cannot be moved under itself or one of its descendants, the target parent must be active, and the moved subtree must still fit within 3 levels.\n- `sort_order` (optional, integer or null, min 0).\n\n**Requires permission:** `product_types.update`","body":{"mode":"raw","raw":"{\n  \"name\": \"Cloth nappies\",\n  \"parent_id\": 3,\n  \"sort_order\": 0\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/product-types/{type}","host":["{{baseUrl}}"],"path":["api","v2","product-types",":type"],"variable":[{"key":"type","value":"7","description":"Product type id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 7,\n    \"name\": \"Cloth nappies\",\n    \"path\": \"Diapering > Cloth nappies\",\n    \"parent_id\": 3,\n    \"level\": 1,\n    \"is_leaf\": true,\n    \"sort_order\": 0,\n    \"products_count\": 48,\n    \"created_at\": \"2026-09-01T09:12:00.000000Z\",\n    \"updated_at\": \"2026-09-01T09:12:00.000000Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Archive Product Type","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/product-types/{type}/archive","host":["{{baseUrl}}"],"path":["api","v2","product-types",":type","archive"],"variable":[{"key":"type","value":"7","description":"Product type id"}]},"description":"Archive a product type so it disappears from lists, pickers and the channel category maps. A type with active child types cannot be archived — archive or move the children first. A type that still carries products needs `move_to`: its products and its per-channel category-map rows are merged into that leaf type first (the same rules as Merge Product Type), then the type is archived. Archiving an already archived type is a no-op.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- `move_to` (nullable, integer) — the active leaf type that takes over this type's products. Required when the type has products; the response tells you how many.\n\n**Requires permission:** `product_types.archive`","body":{"mode":"raw","raw":"{\n  \"move_to\": 8\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/product-types/{type}/archive","host":["{{baseUrl}}"],"path":["api","v2","product-types",":type","archive"],"variable":[{"key":"type","value":"7","description":"Product type id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 7,\n    \"name\": \"Cloth diapers\",\n    \"path\": \"Diapering > Cloth diapers\",\n    \"parent_id\": 3,\n    \"level\": 1,\n    \"is_leaf\": true,\n    \"sort_order\": 0,\n    \"products_count\": 0,\n    \"created_at\": \"2026-09-01T09:12:00.000000Z\",\n    \"updated_at\": \"2026-09-01T09:12:00.000000Z\",\n    \"archived_at\": \"2026-09-16T10:41:00.000000Z\"\n  }\n}"}]},{"name":"Unarchive Product Type","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/v2/product-types/{type}/unarchive","host":["{{baseUrl}}"],"path":["api","v2","product-types",":type","unarchive"],"variable":[{"key":"type","value":"7","description":"Product type id"}]},"description":"Restore an archived product type to its previous place in the tree. Its parent must be active — restore the parent first. Restoring an active type is a no-op.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `product_types.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/product-types/{type}/unarchive","host":["{{baseUrl}}"],"path":["api","v2","product-types",":type","unarchive"],"variable":[{"key":"type","value":"7","description":"Product type id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 7,\n    \"name\": \"Cloth diapers\",\n    \"path\": \"Diapering > Cloth diapers\",\n    \"parent_id\": 3,\n    \"level\": 1,\n    \"is_leaf\": true,\n    \"sort_order\": 0,\n    \"products_count\": 48,\n    \"created_at\": \"2026-09-01T09:12:00.000000Z\",\n    \"updated_at\": \"2026-09-01T09:12:00.000000Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Merge Product Type","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/product-types/{type}/merge","host":["{{baseUrl}}"],"path":["api","v2","product-types",":type","merge"],"variable":[{"key":"type","value":"7","description":"Product type id"}]},"description":"Move every product (archived products included) and every per-channel category-map row from this type onto another leaf type, then archive this one. Both types must be leaves and the target must be active. Where the target already has a map row on a channel, the target's row is kept and this type's row is dropped; those channels are listed in `meta.conflicting_channel_ids` so their mappings can be checked. Responds with the target type.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- `into` (required, integer) — the active leaf type to merge into.\n\nResponse `meta`: `moved_products`, `moved_map_rows`, `conflicting_channel_ids` (sales channel ids whose existing mapping on the target won).\n\n**Requires permission:** `product_types.update`","body":{"mode":"raw","raw":"{\n  \"into\": 8\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/product-types/{type}/merge","host":["{{baseUrl}}"],"path":["api","v2","product-types",":type","merge"],"variable":[{"key":"type","value":"7","description":"Product type id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 8,\n    \"name\": \"Swim diapers\",\n    \"path\": \"Diapering > Swim diapers\",\n    \"parent_id\": 3,\n    \"level\": 1,\n    \"is_leaf\": true,\n    \"sort_order\": 1,\n    \"products_count\": 60,\n    \"created_at\": \"2026-09-01T09:12:00.000000Z\",\n    \"updated_at\": \"2026-09-01T09:12:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"meta\": {\n    \"moved_products\": 48,\n    \"moved_map_rows\": 2,\n    \"conflicting_channel_ids\": [\n      30\n    ]\n  }\n}"}]},{"name":"Propose Product Types","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/product-types/propose","host":["{{baseUrl}}"],"path":["api","v2","product-types","propose"]},"description":"Queue a background run that proposes a product type for up to 500 products from their names, brands and the existing type tree. Products that already carry a type are skipped unless `include_typed` is `true`. Nothing is written — poll `poll_url` (Get Product Type Proposal Run) until `status` is `completed`, review the proposals, then send the accepted ones to Apply Product Type Proposals.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- `product_ids` (required, array of integers, 1–500)\n- `include_typed` (nullable, boolean, default false) — also propose for products that already have a type\n\nReturns 422 when AI features are disabled or the configured provider has no API key. Rate limited per user — 429 when exceeded.\n\n**Requires permission:** `product_types.update`","body":{"mode":"raw","raw":"{\n  \"product_ids\": [\n    88,\n    89,\n    90,\n    91\n  ],\n  \"include_typed\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Accepted","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/product-types/propose","host":["{{baseUrl}}"],"path":["api","v2","product-types","propose"]}},"status":"Accepted","code":202,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 8812,\n    \"tracked_job_log_id\": 8812,\n    \"status\": \"pending\",\n    \"poll_url\": \"/api/v2/product-types/proposals/8812\"\n  }\n}"}]},{"name":"Get Product Type Proposal Run","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/product-types/proposals/{trackedJobLog}","host":["{{baseUrl}}"],"path":["api","v2","product-types","proposals",":trackedJobLog"],"variable":[{"key":"trackedJobLog","value":"8812","description":"Proposal run id, as returned by Propose Product Types"}]},"description":"The status of a proposal run and, once `status` is `completed`, its proposals. Only the user who started the run can read it — any other run id answers 404.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nResponse `data`:\n- `id`, `status` (`pending` | `processing` | `completed` | `failed` | `cancelled`), `progress_percentage`\n- when completed: `proposals[]`, `provider`, `tokens` (LLM tokens used), `product_count` (products classified), `applied_at` (set once decisions were applied)\n- when failed: `error`\n\nEach proposal: `product_id`, `sku`, `name`, `brand_name`, `current_product_type_id` / `current_product_type_path` (what the product carries today), `product_type_id` / `product_type_path` (an existing leaf type that fits, else null), `new_type_path` (a `Parent > Leaf` path to create when no existing type fits, else null), `is_new`, `confidence` (0–1), `reason`.\n\n**Requires permission:** `product_types.index`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/product-types/proposals/{trackedJobLog}","host":["{{baseUrl}}"],"path":["api","v2","product-types","proposals",":trackedJobLog"],"variable":[{"key":"trackedJobLog","value":"8812","description":"Proposal run id, as returned by Propose Product Types"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 8812,\n    \"status\": \"completed\",\n    \"progress_percentage\": 100,\n    \"proposals\": [\n      {\n        \"product_id\": 88,\n        \"sku\": \"CD-OS-SAGE\",\n        \"name\": \"One-size cloth diaper — Sage\",\n        \"brand_name\": \"Tiny Threads\",\n        \"current_product_type_id\": null,\n        \"current_product_type_path\": null,\n        \"product_type_id\": 7,\n        \"product_type_path\": \"Diapering > Cloth diapers\",\n        \"new_type_path\": null,\n        \"is_new\": false,\n        \"confidence\": 0.94,\n        \"reason\": \"A reusable cloth diaper; matches the existing Cloth diapers type.\"\n      },\n      {\n        \"product_id\": 91,\n        \"sku\": \"WB-LG-NAVY\",\n        \"name\": \"Large wet/dry bag — Navy\",\n        \"brand_name\": \"Tiny Threads\",\n        \"current_product_type_id\": null,\n        \"current_product_type_path\": null,\n        \"product_type_id\": null,\n        \"product_type_path\": null,\n        \"new_type_path\": \"Diapering > Wet bags\",\n        \"is_new\": true,\n        \"confidence\": 0.81,\n        \"reason\": \"A storage bag for used diapers; no existing type fits.\"\n      }\n    ],\n    \"provider\": \"openai\",\n    \"tokens\": 4821,\n    \"product_count\": 2,\n    \"applied_at\": null\n  }\n}"}]},{"name":"Apply Product Type Proposals","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/product-types/proposals/{trackedJobLog}/apply","host":["{{baseUrl}}"],"path":["api","v2","product-types","proposals",":trackedJobLog","apply"],"variable":[{"key":"trackedJobLog","value":"8812","description":"Proposal run id, as returned by Propose Product Types"}]},"description":"Write the decisions taken on a proposal run. Per product, send one of: an existing active leaf `product_type_id`; a `new_type_path` to create (`Parent > Leaf`, 1–3 segments — segments that already exist are reused case-insensitively, an archived match is restored, and the leaf becomes the product's type); or neither, to clear the product's type. Leave out the products whose proposal you rejected. Records `applied_at` on the run.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- `decisions` (required, array, 1–500)\n- `decisions[].product_id` (required, integer)\n- `decisions[].product_type_id` (nullable, integer) — an active leaf type\n- `decisions[].new_type_path` (nullable, string, max 400) — takes precedence over `product_type_id` when both are given\n\nResponse `data`: `assigned` (products given a type), `cleared` (products whose type was removed), `created_types[]` (`{id, path}` of every type created by this call).\n\n**Requires permission:** `product_types.update`","body":{"mode":"raw","raw":"{\n  \"decisions\": [\n    {\n      \"product_id\": 88,\n      \"product_type_id\": 7\n    },\n    {\n      \"product_id\": 91,\n      \"new_type_path\": \"Diapering > Wet bags\"\n    },\n    {\n      \"product_id\": 90,\n      \"product_type_id\": null\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/product-types/proposals/{trackedJobLog}/apply","host":["{{baseUrl}}"],"path":["api","v2","product-types","proposals",":trackedJobLog","apply"],"variable":[{"key":"trackedJobLog","value":"8812","description":"Proposal run id, as returned by Propose Product Types"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"assigned\": 2,\n    \"cleared\": 1,\n    \"created_types\": [\n      {\n        \"id\": 14,\n        \"path\": \"Diapering > Wet bags\"\n      }\n    ]\n  }\n}"}]}]},{"name":"Pricing","item":[{"name":"Get Pricing Tier","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/{product_pricing_tier}","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers",":product_pricing_tier"],"variable":[{"key":"product_pricing_tier","value":"1","description":"Pricing tier ID"}]},"description":"Show a single product pricing tier.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/{product_pricing_tier}","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers",":product_pricing_tier"],"variable":[{"key":"product_pricing_tier","value":"1","description":"Pricing tier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Standard\",\n    \"pricing_type\": \"absolute\",\n    \"base_pricing_tier_id\": null,\n    \"base_pricing_tier_name\": null,\n    \"relative_basis\": null,\n    \"base_supplier_pricing_tier_id\": null,\n    \"base_supplier_pricing_tier_name\": null,\n    \"base_attribute_id\": null,\n    \"base_attribute_name\": null,\n    \"attr_rate\": null,\n    \"attr_offset\": null,\n    \"adjustment_percentage\": null,\n    \"adjustment_fixed_amount\": null,\n    \"rounding_value\": null,\n    \"adjustment_mode\": \"markup\",\n    \"floor_pricing_tier_id\": null,\n    \"floor_pricing_tier_name\": null,\n    \"ceiling_pricing_tier_id\": null,\n    \"ceiling_pricing_tier_name\": null,\n    \"currency_code\": \"USD\",\n    \"is_default\": true,\n    \"dependent_tier_count\": 1,\n    \"chain_depth\": 0,\n    \"created_at\": \"2024-01-01T00:00:00.000000Z\",\n    \"updated_at\": \"2024-01-10T09:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Update Pricing Tier","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/{product_pricing_tier}","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers",":product_pricing_tier"],"variable":[{"key":"product_pricing_tier","value":"2","description":"Pricing tier ID"}]},"description":"Update an existing product pricing tier. currency_code cannot be changed on update.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body fields (all optional on update):\n- `name` (string, max:255, unique)\n- `pricing_type` — `absolute` or `relative`\n- `base_pricing_tier_id` (nullable integer) — required if pricing_type=relative\n- `adjustment_percentage` (nullable numeric, -99.9999 to 999.9999) — required if pricing_type=relative\n- `adjustment_fixed_amount` (nullable numeric)\n- `rounding_value` (nullable numeric, gt:0)\n\n**Relative tiers** (`pricing_type=relative`) require `relative_basis`, one of:\n- `pricing_tier` — derive from another tier's price. Requires `base_pricing_tier_id` + `adjustment_percentage`.\n- `supplier_pricing_tier` — cost-plus from the product's primary supplier cost. Optional `base_supplier_pricing_tier_id` (defaults to the default supplier tier) + `adjustment_percentage`.\n- `attribute` — derive from a numeric/integer product attribute: `value × attr_rate + attr_offset`. Requires `base_attribute_id` + `attr_rate` (optional `attr_offset`).\n- `average_cost` — cost-plus from the product's average cost: the landed figure SKU.io maintains from received stock, so inbound freight, duty and customs are included once recorded. Requires `adjustment_percentage`; no base field. Products with no average cost yet get no price, and a cost that falls to zero (sold out) leaves the last price standing.\n\n`rounding_value` and `adjustment_fixed_amount` apply to every basis. Supplier/pricing-tier bases inherit currency from their source, the average-cost basis uses the default currency; attribute basis uses `currency_code`.\n\n**Adjustment mode** (`adjustment_mode`, optional, `markup` | `margin`, default `markup`) — how `adjustment_percentage` is applied on the percentage bases. `markup`: price = base × (1 + p%). `margin`: price = base ÷ (1 − p%), so p% of the sell price is margin over the base (the repricer's target-margin strategy); p must be below 100.\n\n**Price bounds** (`floor_pricing_tier_id`, `ceiling_pricing_tier_id`, optional, relative tiers only) — clamp the computed price to another tier's price for the same product, e.g. never below a Cost tier, never above a MAP tier. Applied after rounding and the fixed amount; if the two cross the ceiling wins. A bound tier may not be this tier or anything that derives from it (circular reference), and a tier used as a bound cannot be deleted while tiers clamp to it. Bound changes re-price the tier, and a price change on a bound tier re-clamps every tier bounded by it.\n\n**Requires permission:** `pricing_tiers.update`","body":{"mode":"raw","raw":"{\n  \"name\": \"Trade Wholesale\",\n  \"pricing_type\": \"relative\",\n  \"relative_basis\": \"pricing_tier\",\n  \"base_pricing_tier_id\": 1,\n  \"adjustment_percentage\": -20,\n  \"rounding_value\": 0.1\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/{product_pricing_tier}","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers",":product_pricing_tier"],"variable":[{"key":"product_pricing_tier","value":"2","description":"Pricing tier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 2,\n    \"name\": \"Trade Wholesale\",\n    \"pricing_type\": \"relative\",\n    \"base_pricing_tier_id\": 1,\n    \"base_pricing_tier_name\": \"Standard\",\n    \"relative_basis\": \"pricing_tier\",\n    \"base_supplier_pricing_tier_id\": null,\n    \"base_supplier_pricing_tier_name\": null,\n    \"base_attribute_id\": null,\n    \"base_attribute_name\": null,\n    \"attr_rate\": null,\n    \"attr_offset\": null,\n    \"adjustment_percentage\": \"-20.0000\",\n    \"adjustment_fixed_amount\": null,\n    \"rounding_value\": \"0.1000\",\n    \"adjustment_mode\": \"markup\",\n    \"floor_pricing_tier_id\": null,\n    \"floor_pricing_tier_name\": null,\n    \"ceiling_pricing_tier_id\": null,\n    \"ceiling_pricing_tier_name\": null,\n    \"currency_code\": \"USD\",\n    \"is_default\": false,\n    \"dependent_tier_count\": 0,\n    \"chain_depth\": 1,\n    \"created_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"updated_at\": \"2024-03-05T14:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"product pricing tier Trade Wholesale was updated successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Delete Pricing Tier","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/{product_pricing_tier}","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers",":product_pricing_tier"],"variable":[{"key":"product_pricing_tier","value":"2","description":"Pricing tier ID"}]},"description":"Delete a product pricing tier. Fails if the tier is the default, is used by products, referenced in import mappings, or has active dependent tiers.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `pricing_tiers.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/{product_pricing_tier}","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers",":product_pricing_tier"],"variable":[{"key":"product_pricing_tier","value":"2","description":"Pricing tier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"product pricing tier Trade Wholesale was deleted successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"List Pricing Tiers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/product-pricing-tiers?page=1&limit=15","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"15","description":"Records per page (default: 15)","disabled":true}]},"description":"List product pricing tiers. Eager-loads the base pricing tier and counts dependent tiers.\n\nAuthentication: Requires Bearer token.\n\nQuery parameters:\n- `page` / `limit` — pagination\n- `search` — searches name and currency_code\n- `sort` — sortable columns: id, name, currency_code, is_default, pricing_type, created_at, updated_at, archived_at\n- `archived` — 0 (active only), 1 (archived only)\n- `filter[name]` / `filter[currency_code]` — column filters"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/product-pricing-tiers?page=1&limit=15","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"15","description":"Records per page (default: 15)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Standard\",\n      \"pricing_type\": \"absolute\",\n      \"base_pricing_tier_id\": null,\n      \"base_pricing_tier_name\": null,\n      \"relative_basis\": null,\n      \"base_supplier_pricing_tier_id\": null,\n      \"base_supplier_pricing_tier_name\": null,\n      \"base_attribute_id\": null,\n      \"base_attribute_name\": null,\n      \"attr_rate\": null,\n      \"attr_offset\": null,\n      \"adjustment_percentage\": null,\n      \"adjustment_fixed_amount\": null,\n      \"rounding_value\": null,\n      \"adjustment_mode\": \"markup\",\n      \"floor_pricing_tier_id\": null,\n      \"floor_pricing_tier_name\": null,\n      \"ceiling_pricing_tier_id\": null,\n      \"ceiling_pricing_tier_name\": null,\n      \"currency_code\": \"USD\",\n      \"is_default\": true,\n      \"dependent_tier_count\": 1,\n      \"chain_depth\": 0,\n      \"created_at\": \"2024-01-01T00:00:00.000000Z\",\n      \"updated_at\": \"2024-01-10T09:00:00.000000Z\",\n      \"archived_at\": null\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Wholesale\",\n      \"pricing_type\": \"relative\",\n      \"base_pricing_tier_id\": 1,\n      \"base_pricing_tier_name\": \"Standard\",\n      \"relative_basis\": \"pricing_tier\",\n      \"base_supplier_pricing_tier_id\": null,\n      \"base_supplier_pricing_tier_name\": null,\n      \"base_attribute_id\": null,\n      \"base_attribute_name\": null,\n      \"attr_rate\": null,\n      \"attr_offset\": null,\n      \"adjustment_percentage\": \"-15.0000\",\n      \"adjustment_fixed_amount\": null,\n      \"rounding_value\": \"0.0500\",\n      \"adjustment_mode\": \"markup\",\n      \"floor_pricing_tier_id\": null,\n      \"floor_pricing_tier_name\": null,\n      \"ceiling_pricing_tier_id\": null,\n      \"ceiling_pricing_tier_name\": null,\n      \"currency_code\": \"USD\",\n      \"is_default\": false,\n      \"dependent_tier_count\": 0,\n      \"chain_depth\": 1,\n      \"created_at\": \"2024-01-05T09:00:00.000000Z\",\n      \"updated_at\": \"2024-02-01T11:00:00.000000Z\",\n      \"archived_at\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 2,\n  \"status\": \"success\"\n}"}]},{"name":"Create Pricing Tier","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-pricing-tiers","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers"]},"description":"Create a new product pricing tier. Supports both absolute and relative pricing.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body fields:\n- `name` (required, string, max:255) — unique name for the tier\n- `currency_code` (required) — must exist in currencies table (e.g. USD, EUR). Relative tiers inherit currency from their base tier.\n- `pricing_type` (required) — `absolute` or `relative`. Defaults to `absolute` if omitted.\n- `base_pricing_tier_id` (required if pricing_type=relative, nullable) — the base tier to derive pricing from; must not create a circular reference\n- `adjustment_percentage` (required if pricing_type=relative, nullable, numeric -99.9999 to 999.9999) — percentage adjustment relative to the base tier\n- `adjustment_fixed_amount` (optional, nullable, numeric) — fixed amount adjustment\n- `rounding_value` (optional, nullable, numeric, gt:0) — round the final price to the nearest multiple of this value\n\nValidation rules:\n- A tier cannot reference itself as a base tier\n- Circular references are prevented\n- The default tier cannot be set to relative pricing type\n\n**Relative tiers** (`pricing_type=relative`) require `relative_basis`, one of:\n- `pricing_tier` — derive from another tier's price. Requires `base_pricing_tier_id` + `adjustment_percentage`.\n- `supplier_pricing_tier` — cost-plus from the product's primary supplier cost. Optional `base_supplier_pricing_tier_id` (defaults to the default supplier tier) + `adjustment_percentage`.\n- `attribute` — derive from a numeric/integer product attribute: `value × attr_rate + attr_offset`. Requires `base_attribute_id` + `attr_rate` (optional `attr_offset`).\n- `average_cost` — cost-plus from the product's average cost: the landed figure SKU.io maintains from received stock, so inbound freight, duty and customs are included once recorded. Requires `adjustment_percentage`; no base field. Products with no average cost yet get no price, and a cost that falls to zero (sold out) leaves the last price standing.\n\n`rounding_value` and `adjustment_fixed_amount` apply to every basis. Supplier/pricing-tier bases inherit currency from their source, the average-cost basis uses the default currency; attribute basis uses `currency_code`.\n\n**Adjustment mode** (`adjustment_mode`, optional, `markup` | `margin`, default `markup`) — how `adjustment_percentage` is applied on the percentage bases. `markup`: price = base × (1 + p%). `margin`: price = base ÷ (1 − p%), so p% of the sell price is margin over the base (the repricer's target-margin strategy); p must be below 100.\n\n**Price bounds** (`floor_pricing_tier_id`, `ceiling_pricing_tier_id`, optional, relative tiers only) — clamp the computed price to another tier's price for the same product, e.g. never below a Cost tier, never above a MAP tier. Applied after rounding and the fixed amount; if the two cross the ceiling wins. A bound tier may not be this tier or anything that derives from it (circular reference), and a tier used as a bound cannot be deleted while tiers clamp to it. Bound changes re-price the tier, and a price change on a bound tier re-clamps every tier bounded by it.\n\n**Requires permission:** `pricing_tiers.create`","body":{"mode":"raw","raw":"{\n  \"name\": \"Wholesale\",\n  \"currency_code\": \"USD\",\n  \"pricing_type\": \"relative\",\n  \"relative_basis\": \"pricing_tier\",\n  \"base_pricing_tier_id\": 1,\n  \"adjustment_percentage\": -15,\n  \"adjustment_mode\": \"markup\",\n  \"adjustment_fixed_amount\": null,\n  \"rounding_value\": 0.05,\n  \"floor_pricing_tier_id\": null,\n  \"ceiling_pricing_tier_id\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-pricing-tiers","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 2,\n    \"name\": \"Wholesale\",\n    \"pricing_type\": \"relative\",\n    \"base_pricing_tier_id\": 1,\n    \"base_pricing_tier_name\": \"Standard\",\n    \"relative_basis\": \"pricing_tier\",\n    \"base_supplier_pricing_tier_id\": null,\n    \"base_supplier_pricing_tier_name\": null,\n    \"base_attribute_id\": null,\n    \"base_attribute_name\": null,\n    \"attr_rate\": null,\n    \"attr_offset\": null,\n    \"adjustment_percentage\": \"-15.0000\",\n    \"adjustment_fixed_amount\": null,\n    \"rounding_value\": \"0.0500\",\n    \"adjustment_mode\": \"markup\",\n    \"floor_pricing_tier_id\": null,\n    \"floor_pricing_tier_name\": null,\n    \"ceiling_pricing_tier_id\": null,\n    \"ceiling_pricing_tier_name\": null,\n    \"currency_code\": \"USD\",\n    \"is_default\": false,\n    \"dependent_tier_count\": 0,\n    \"chain_depth\": 1,\n    \"created_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"updated_at\": \"2024-03-01T10:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"product pricing tier was created successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Delete Pricing Tiers","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/product-pricing-tiers","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers"]},"description":"Bulk delete multiple pricing tiers by ID.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- `ids` (required) — array of pricing tier IDs to delete\n\n**Requires permission:** `pricing_tiers.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/product-pricing-tiers","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 product pricing tiers deleted successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Set as Default Tier","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/{productPricingTier}/default","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers",":productPricingTier","default"],"variable":[{"key":"productPricingTier","value":"1","description":"Pricing tier ID"}]},"description":"Set a pricing tier as the system default. Only absolute (non-relative) tiers that are not archived can be set as default. Unsetting the previous default is done automatically.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 400 if the tier is archived or is a relative pricing type.\n\n**Requires permission:** `pricing_tiers.update`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/{productPricingTier}/default","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers",":productPricingTier","default"],"variable":[{"key":"productPricingTier","value":"1","description":"Pricing tier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"name\": \"Standard\",\n    \"pricing_type\": \"absolute\",\n    \"is_default\": true,\n    \"currency_code\": \"USD\",\n    \"created_at\": \"2024-01-01T00:00:00.000000Z\",\n    \"updated_at\": \"2024-03-10T11:00:00.000000Z\",\n    \"archived_at\": null\n  },\n  \"message\": \"product pricing tier Standard was set as default successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Archive Pricing Tier","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/{productPricingTier}/archive","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers",":productPricingTier","archive"],"variable":[{"key":"productPricingTier","value":"2","description":"Pricing tier ID"}]},"description":"Archive a product pricing tier. Fails if the tier is the default tier or has active dependent tiers.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `pricing_tiers.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/{productPricingTier}/archive","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers",":productPricingTier","archive"],"variable":[{"key":"productPricingTier","value":"2","description":"Pricing tier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"product pricing tier Trade Wholesale was archived successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Unarchive Pricing Tier","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/{productPricingTier}/unarchived","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers",":productPricingTier","unarchived"],"variable":[{"key":"productPricingTier","value":"2","description":"Pricing tier ID"}]},"description":"Unarchive a product pricing tier.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `pricing_tiers.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/{productPricingTier}/unarchived","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers",":productPricingTier","unarchived"],"variable":[{"key":"productPricingTier","value":"2","description":"Pricing tier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 2,\n    \"name\": \"Trade Wholesale\",\n    \"pricing_type\": \"relative\",\n    \"is_default\": false,\n    \"currency_code\": \"USD\",\n    \"archived_at\": null\n  },\n  \"message\": \"product pricing tier Trade Wholesale was unarchived successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Archive Pricing Tiers","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/archive","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers","archive"]},"description":"Bulk archive multiple pricing tiers. Default tiers and tiers with active dependents are skipped with warnings.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- `ids` (required) — array of pricing tier IDs to archive\n\n**Requires permission:** `pricing_tiers.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/archive","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 product pricing tiers archived successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Bulk Unarchive Pricing Tiers","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/unarchive","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers","unarchive"]},"description":"Bulk unarchive multiple pricing tiers.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- `ids` (required) — array of pricing tier IDs to unarchive\n\n**Requires permission:** `pricing_tiers.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/unarchive","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 product pricing tiers unarchived successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"Check Pricing Tiers Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/deletable","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers","deletable"]},"description":"Check which pricing tiers can be deleted. Deletion is blocked if: tier is default, is used by products, referenced in import mappings, or has active dependent (relative) tiers.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- `ids` (required, array) — pricing tier IDs to check; must exist in product_pricing_tiers table\n\n**Requires permission:** `pricing_tiers.delete`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/deletable","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Standard\",\n      \"deletable\": false,\n      \"reason\": {\n        \"is_default\": \"Cannot delete the default pricing tier 'Standard'.\"\n      }\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Wholesale\",\n      \"deletable\": false,\n      \"reason\": {\n        \"products\": \"5 products are currently using product pricing tier(Wholesale).\"\n      }\n    },\n    {\n      \"id\": 3,\n      \"name\": \"Clearance\",\n      \"deletable\": true,\n      \"reason\": null\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Check Pricing Tiers Archivable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/archivable","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers","archivable"]},"description":"Check which pricing tiers can be archived. The default tier cannot be archived.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRequest body:\n- `ids` (required, array) — pricing tier IDs to check; must exist in product_pricing_tiers table\n\n**Requires permission:** `pricing_tiers.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/archivable","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers","archivable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Standard\",\n      \"is_default\": true,\n      \"archivable\": false,\n      \"reason\": {\n        \"is_default\": \"Cannot archive the default pricing tier 'Standard'.\"\n      }\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Wholesale\",\n      \"is_default\": false,\n      \"archivable\": true,\n      \"reason\": null\n    },\n    {\n      \"id\": 3,\n      \"name\": \"Clearance\",\n      \"is_default\": false,\n      \"archivable\": true,\n      \"reason\": null\n    }\n  ],\n  \"status\": \"success\"\n}"}]},{"name":"Preview Product Pricing Tier Import","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/import/preview","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers","import","preview"]},"description":"Preview a pricing tier import file before committing. Returns parsed rows and validation errors.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data\n- `file` (required) — CSV or Excel file\n\n**Requires permission:** `pricing_tiers.import`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/import/preview","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers","import","preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"rows\": [\n      {\n        \"name\": \"VIP Tier\",\n        \"currency_code\": \"USD\",\n        \"pricing_type\": \"absolute\",\n        \"valid\": true,\n        \"errors\": []\n      }\n    ],\n    \"total\": 1,\n    \"valid\": 1,\n    \"invalid\": 0\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Import Pricing Tiers","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/import","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers","import"]},"description":"Import product pricing tiers from a CSV or Excel file.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nContent-Type: multipart/form-data\n- `file` (required) — CSV or Excel file\n\n**Requires permission:** `pricing_tiers.import`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/product-pricing-tiers/import","host":["{{baseUrl}}"],"path":["api","product-pricing-tiers","import"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"imported\": 2,\n    \"skipped\": 0,\n    \"errors\": []\n  },\n  \"message\": \"Import completed successfully\",\n  \"status\": \"success\"\n}"}]},{"name":"List Price Overrides","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/admin/portal/pricing/customer-overrides","host":["{{baseUrl}}"],"path":["api","admin","portal","pricing","customer-overrides"]},"description":"List customer-specific / contract price overrides.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/admin/portal/pricing/customer-overrides","host":["{{baseUrl}}"],"path":["api","admin","portal","pricing","customer-overrides"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 900,\n      \"customer_id\": 101,\n      \"product_id\": 5001,\n      \"min_quantity\": 1,\n      \"price\": \"42.5000\",\n      \"currency_code\": \"USD\",\n      \"effective_from\": null,\n      \"effective_to\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1\n}"}]},{"name":"Create Price Override","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/admin/portal/pricing/customer-overrides","host":["{{baseUrl}}"],"path":["api","admin","portal","pricing","customer-overrides"]},"description":"Create a customer-specific or contract (dated) price override.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.","body":{"mode":"raw","raw":"{\n  \"customer_id\": 101,\n  \"product_id\": 5001,\n  \"min_quantity\": 1,\n  \"price\": 42.5,\n  \"currency_code\": \"USD\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/admin/portal/pricing/customer-overrides","host":["{{baseUrl}}"],"path":["api","admin","portal","pricing","customer-overrides"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 900,\n    \"customer_id\": 101,\n    \"product_id\": 5001,\n    \"min_quantity\": 1,\n    \"price\": \"42.5000\",\n    \"currency_code\": \"USD\",\n    \"effective_from\": null,\n    \"effective_to\": null\n  }\n}"}]},{"name":"Update Price Override","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/admin/portal/pricing/customer-overrides/{override}","host":["{{baseUrl}}"],"path":["api","admin","portal","pricing","customer-overrides",":override"],"variable":[{"key":"override","value":"900","description":"The override ID."}]},"description":"Update a price override.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.","body":{"mode":"raw","raw":"{\n  \"price\": 40\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/admin/portal/pricing/customer-overrides/{override}","host":["{{baseUrl}}"],"path":["api","admin","portal","pricing","customer-overrides",":override"],"variable":[{"key":"override","value":"900","description":"The override ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 900,\n    \"customer_id\": 101,\n    \"product_id\": 5001,\n    \"min_quantity\": 1,\n    \"price\": \"40.0000\",\n    \"currency_code\": \"USD\",\n    \"effective_from\": null,\n    \"effective_to\": null\n  }\n}"}]},{"name":"Delete Price Override","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/admin/portal/pricing/customer-overrides/{override}","host":["{{baseUrl}}"],"path":["api","admin","portal","pricing","customer-overrides",":override"],"variable":[{"key":"override","value":"900","description":"The override ID."}]},"description":"Delete a price override.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/admin/portal/pricing/customer-overrides/{override}","host":["{{baseUrl}}"],"path":["api","admin","portal","pricing","customer-overrides",":override"],"variable":[{"key":"override","value":"900","description":"The override ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Override deleted.\"\n}"}]},{"name":"List Quantity Breaks","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/admin/portal/pricing/quantity-breaks","host":["{{baseUrl}}"],"path":["api","admin","portal","pricing","quantity-breaks"]},"description":"List tier volume-break prices.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/admin/portal/pricing/quantity-breaks","host":["{{baseUrl}}"],"path":["api","admin","portal","pricing","quantity-breaks"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 800,\n      \"product_pricing_tier_id\": 4,\n      \"product_id\": 5001,\n      \"min_quantity\": 10,\n      \"price\": \"7.2500\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1\n}"}]},{"name":"Create Quantity Break","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/admin/portal/pricing/quantity-breaks","host":["{{baseUrl}}"],"path":["api","admin","portal","pricing","quantity-breaks"]},"description":"Create a tier volume break (unit price at a quantity threshold).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Ladder rules.** A volume ladder must never make a larger quantity cost more per unit, so a rung is rejected with `422` when it would:\n- cost more per unit than any rung with a smaller minimum quantity (or less than one above it),\n- sit above the tier's own base price for that product — a volume break outranks the base price, so a rung dearer than list would actually be charged,\n- reuse a minimum quantity that already has a rung for this tier and product.\n\nThese rejections return `{\"error\": \"...\"}` with a message naming the conflicting rung. Field-level validation failures return the standard `{\"message\", \"errors\"}` shape.","body":{"mode":"raw","raw":"{\n  \"product_pricing_tier_id\": 4,\n  \"product_id\": 5001,\n  \"min_quantity\": 10,\n  \"price\": 7.25\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/admin/portal/pricing/quantity-breaks","host":["{{baseUrl}}"],"path":["api","admin","portal","pricing","quantity-breaks"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 800,\n    \"product_pricing_tier_id\": 4,\n    \"product_id\": 5001,\n    \"min_quantity\": 10,\n    \"price\": \"7.2500\"\n  }\n}"}]},{"name":"Update Quantity Break","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/admin/portal/pricing/quantity-breaks/{quantityBreak}","host":["{{baseUrl}}"],"path":["api","admin","portal","pricing","quantity-breaks",":quantityBreak"],"variable":[{"key":"quantityBreak","value":"800","description":"The quantity break ID."}]},"description":"Update a volume break.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Ladder rules.** A volume ladder must never make a larger quantity cost more per unit, so a rung is rejected with `422` when it would:\n- cost more per unit than any rung with a smaller minimum quantity (or less than one above it),\n- sit above the tier's own base price for that product — a volume break outranks the base price, so a rung dearer than list would actually be charged,\n- reuse a minimum quantity that already has a rung for this tier and product.\n\nThese rejections return `{\"error\": \"...\"}` with a message naming the conflicting rung. Field-level validation failures return the standard `{\"message\", \"errors\"}` shape.","body":{"mode":"raw","raw":"{\n  \"price\": 7\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/admin/portal/pricing/quantity-breaks/{quantityBreak}","host":["{{baseUrl}}"],"path":["api","admin","portal","pricing","quantity-breaks",":quantityBreak"],"variable":[{"key":"quantityBreak","value":"800","description":"The quantity break ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 800,\n    \"product_pricing_tier_id\": 4,\n    \"product_id\": 5001,\n    \"min_quantity\": 10,\n    \"price\": \"7.0000\"\n  }\n}"}]},{"name":"Delete Quantity Break","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/admin/portal/pricing/quantity-breaks/{quantityBreak}","host":["{{baseUrl}}"],"path":["api","admin","portal","pricing","quantity-breaks",":quantityBreak"],"variable":[{"key":"quantityBreak","value":"800","description":"The quantity break ID."}]},"description":"Delete a volume break.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/admin/portal/pricing/quantity-breaks/{quantityBreak}","host":["{{baseUrl}}"],"path":["api","admin","portal","pricing","quantity-breaks",":quantityBreak"],"variable":[{"key":"quantityBreak","value":"800","description":"The quantity break ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Break deleted.\"\n}"}]},{"name":"Quote Prices","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/portal/pricing/quote","host":["{{baseUrl}}"],"path":["api","portal","pricing","quote"]},"description":"Resolve unit and quantity-break prices for a batch of line items at the buyer's account. Send up to 500 items, each with a `product_id` and a `quantity` (minimum 1); `currency` is optional (defaults to the account's currency). For each item the response reports the resolved `unit_price`, which precedence rule set it (`source`), the quantity threshold that applied, the list price and any percentage discount, and the full quantity-break ladder. A product with no configured price returns `source` = `unconfigured` and a null `unit_price`.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.","body":{"mode":"raw","raw":"{\n  \"items\": [\n    {\n      \"product_id\": 5001,\n      \"quantity\": 12\n    },\n    {\n      \"product_id\": 5002,\n      \"quantity\": 1\n    }\n  ],\n  \"currency\": \"USD\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/portal/pricing/quote","host":["{{baseUrl}}"],"path":["api","portal","pricing","quote"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 5001,\n      \"unit_price\": 8,\n      \"currency_code\": \"USD\",\n      \"source\": \"volume_break\",\n      \"applied_min_quantity\": 12,\n      \"list_price\": null,\n      \"discount_percent\": null,\n      \"breaks\": [\n        {\n          \"min_quantity\": 12,\n          \"unit_price\": 8\n        },\n        {\n          \"min_quantity\": 48,\n          \"unit_price\": 7.5\n        }\n      ]\n    },\n    {\n      \"product_id\": 5002,\n      \"unit_price\": 4.2,\n      \"currency_code\": \"USD\",\n      \"source\": \"tier\",\n      \"applied_min_quantity\": 1,\n      \"list_price\": 4.2,\n      \"discount_percent\": null,\n      \"breaks\": []\n    }\n  ]\n}"}]}]},{"name":"CSV Templates","item":[{"name":"List CSV Templates","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/csv-templates?page=1&limit=15&search=&archived=0&sort=name","host":["{{baseUrl}}"],"path":["api","csv-templates"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"15","description":"Items per page (default: 15)","disabled":true},{"key":"search","value":"","description":"Search string to filter templates by name","disabled":true},{"key":"archived","value":"0","description":"Include archived: 0 = active only (default), 1 = archived only","disabled":true},{"key":"sort","value":"name","description":"Sort field. Prefix with - for descending. Allowed: id, name, model, created_at, updated_at","disabled":true}]},"description":"Returns a paginated list of CSV import/export templates. Supports filtering by model type, pagination, and archive status.\n\nAuthentication: Requires Bearer token\n\nResponse fields:\n- id: Template ID\n- name: Template name (unique per model type)\n- template_type: The model type this template is for (e.g. 'product', 'sales_order')\n- columns: Array of column mappings\n- used_count: How many times this template has been used\n- last_import: Timestamp of last import using this template\n- last_export: Timestamp of last export using this template\n- archived_at: Archive timestamp (null if active)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/csv-templates?page=1&limit=15&search=&archived=0&sort=name","host":["{{baseUrl}}"],"path":["api","csv-templates"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"15","description":"Items per page (default: 15)","disabled":true},{"key":"search","value":"","description":"Search string to filter templates by name","disabled":true},{"key":"archived","value":"0","description":"Include archived: 0 = active only (default), 1 = archived only","disabled":true},{"key":"sort","value":"name","description":"Sort field. Prefix with - for descending. Allowed: id, name, model, created_at, updated_at","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 5,\n      \"name\": \"Standard Product Import\",\n      \"template_type\": \"product\",\n      \"columns\": [\n        {\n          \"column\": \"sku\",\n          \"header\": \"SKU\"\n        },\n        {\n          \"column\": \"name\",\n          \"header\": \"Product Name\"\n        }\n      ],\n      \"used_count\": 12,\n      \"last_import\": \"2024-01-10T09:00:00.000000Z\",\n      \"last_export\": null,\n      \"create_at\": \"2024-01-01T00:00:00.000000Z\",\n      \"updated_at\": \"2024-01-10T09:00:00.000000Z\",\n      \"archived_at\": null\n    }\n  ],\n  \"last_page\": 2,\n  \"per_page\": 15,\n  \"total\": 18\n}"}]},{"name":"Create CSV Template","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/csv-templates","host":["{{baseUrl}}"],"path":["api","csv-templates"]},"description":"Creates a new CSV template for import/export operations.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nRequest body:\n- name (string, required, max:255): Template name. Must be unique per model type.\n- model (string, required): The entity type this template applies to. Valid values: 'product', 'sales_order', 'product_brand', 'supplier'\n- columns (array, required, min:1): Array of column mapping objects defining which fields to include\n\n**Requires permission:** `csv_templates.create`","body":{"mode":"raw","raw":"{\n  \"name\": \"Standard Product Import\",\n  \"model\": \"product\",\n  \"columns\": [\n    {\n      \"column\": \"sku\",\n      \"header\": \"SKU\"\n    },\n    {\n      \"column\": \"name\",\n      \"header\": \"Product Name\"\n    },\n    {\n      \"column\": \"barcode\",\n      \"header\": \"Barcode\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/csv-templates","host":["{{baseUrl}}"],"path":["api","csv-templates"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"csv template created successfully.\",\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Standard Product Import\",\n    \"template_type\": \"product\",\n    \"columns\": [\n      {\n        \"column\": \"sku\",\n        \"header\": \"SKU\"\n      }\n    ],\n    \"used_count\": 0,\n    \"last_import\": null,\n    \"last_export\": null,\n    \"archived_at\": null\n  }\n}"}]},{"name":"Get CSV Template","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/csv-templates/{csv_template}","host":["{{baseUrl}}"],"path":["api","csv-templates",":csv_template"],"variable":[{"key":"csv_template","value":"5","description":"CSV Template ID"}]},"description":"Returns the details of a single CSV template.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/csv-templates/{csv_template}","host":["{{baseUrl}}"],"path":["api","csv-templates",":csv_template"],"variable":[{"key":"csv_template","value":"5","description":"CSV Template ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Standard Product Import\",\n    \"template_type\": \"product\",\n    \"columns\": [\n      {\n        \"column\": \"sku\",\n        \"header\": \"SKU\"\n      },\n      {\n        \"column\": \"name\",\n        \"header\": \"Product Name\"\n      }\n    ],\n    \"used_count\": 12,\n    \"last_import\": \"2024-01-10T09:00:00.000000Z\",\n    \"last_export\": null,\n    \"archived_at\": null\n  }\n}"}]},{"name":"Update CSV Template","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/csv-templates/{csv_template}","host":["{{baseUrl}}"],"path":["api","csv-templates",":csv_template"],"variable":[{"key":"csv_template","value":"5","description":"CSV Template ID"}]},"description":"Updates an existing CSV template.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nRequest body:\n- name (string, required, max:255): Template name. Must be unique per model type (ignoring the current template).\n- model (string, required): The entity type. Valid values: 'product', 'sales_order', 'product_brand', 'supplier'\n- columns (array, required, min:1): Updated array of column mappings\n\n**Requires permission:** `csv_templates.update`","body":{"mode":"raw","raw":"{\n  \"name\": \"Standard Product Import v2\",\n  \"model\": \"product\",\n  \"columns\": [\n    {\n      \"column\": \"sku\",\n      \"header\": \"SKU\"\n    },\n    {\n      \"column\": \"name\",\n      \"header\": \"Product Name\"\n    },\n    {\n      \"column\": \"weight\",\n      \"header\": \"Weight\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/csv-templates/{csv_template}","host":["{{baseUrl}}"],"path":["api","csv-templates",":csv_template"],"variable":[{"key":"csv_template","value":"5","description":"CSV Template ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"csv template Standard Product Import v2 updated successfully.\",\n  \"data\": {\n    \"id\": 5,\n    \"name\": \"Standard Product Import v2\",\n    \"template_type\": \"product\",\n    \"columns\": [\n      {\n        \"column\": \"sku\",\n        \"header\": \"SKU\"\n      },\n      {\n        \"column\": \"name\",\n        \"header\": \"Product Name\"\n      },\n      {\n        \"column\": \"weight\",\n        \"header\": \"Weight\"\n      }\n    ],\n    \"archived_at\": null\n  }\n}"}]},{"name":"Delete CSV Template","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/csv-templates/{csv_template}","host":["{{baseUrl}}"],"path":["api","csv-templates",":csv_template"],"variable":[{"key":"csv_template","value":"5","description":"CSV Template ID"}]},"description":"Deletes the specified CSV template.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\n**Requires permission:** `csv_templates.delete`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/csv-templates/{csv_template}","host":["{{baseUrl}}"],"path":["api","csv-templates",":csv_template"],"variable":[{"key":"csv_template","value":"5","description":"CSV Template ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"csv template Standard Product Import deleted successfully.\"\n}"}]},{"name":"Archive CSV Template","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/csv-templates/{csvTemplate}/archive","host":["{{baseUrl}}"],"path":["api","csv-templates",":csvTemplate","archive"],"variable":[{"key":"csvTemplate","value":"5","description":"CSV Template ID"}]},"description":"Archives the specified CSV template.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required)\n\n**Requires permission:** `csv_templates.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/csv-templates/{csvTemplate}/archive","host":["{{baseUrl}}"],"path":["api","csv-templates",":csvTemplate","archive"],"variable":[{"key":"csvTemplate","value":"5","description":"CSV Template ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"csv template Standard Product Import archived successfully.\",\n  \"data\": {\n    \"id\": 5,\n    \"archived_at\": \"2024-01-15T10:30:00.000000Z\"\n  }\n}"}]},{"name":"Unarchive CSV Template","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/csv-templates/{csvTemplate}/unarchived","host":["{{baseUrl}}"],"path":["api","csv-templates",":csvTemplate","unarchived"],"variable":[{"key":"csvTemplate","value":"5","description":"CSV Template ID"}]},"description":"Restores (unarchives) the specified CSV template.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required)\n\n**Requires permission:** `csv_templates.archive`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/csv-templates/{csvTemplate}/unarchived","host":["{{baseUrl}}"],"path":["api","csv-templates",":csvTemplate","unarchived"],"variable":[{"key":"csvTemplate","value":"5","description":"CSV Template ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"csv template Standard Product Import unarchived successfully.\",\n  \"data\": {\n    \"id\": 5,\n    \"archived_at\": null\n  }\n}"}]},{"name":"Download Sample Template","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/csv-templates/{csvTemplate}/download","host":["{{baseUrl}}"],"path":["api","csv-templates",":csvTemplate","download"],"variable":[{"key":"csvTemplate","value":"5","description":"CSV Template ID"}]},"description":"Returns the URL to the sample CSV file for this template. The sample file shows the expected column headers and example data.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required)\n\nResponse: URL string (not a JSON object)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/csv-templates/{csvTemplate}/download","host":["{{baseUrl}}"],"path":["api","csv-templates",":csvTemplate","download"],"variable":[{"key":"csvTemplate","value":"5","description":"CSV Template ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"\"https://sku2.test/storage/csv-samples/standard-product-import.csv\""}]},{"name":"Export Data Using Template","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/csv-templates/{csvTemplate}/export?archived=0&limit=1000","host":["{{baseUrl}}"],"path":["api","csv-templates",":csvTemplate","export"],"query":[{"key":"archived","value":"0","description":"Include archived records: 0 = active only (default), 1 = archived only","disabled":true},{"key":"limit","value":"1000","description":"Maximum number of records to export (optional)","disabled":true}],"variable":[{"key":"csvTemplate","value":"5","description":"CSV Template ID"}]},"description":"Exports data using the specified CSV template. Returns a CSV file download with the columns defined in the template.\n\nAuthentication: Requires Bearer token (Power User access required)\n\nResponse: text/csv file download\n\n**Requires permission:** `csv_templates.export`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/csv-templates/{csvTemplate}/export?archived=0&limit=1000","host":["{{baseUrl}}"],"path":["api","csv-templates",":csvTemplate","export"],"query":[{"key":"archived","value":"0","description":"Include archived records: 0 = active only (default), 1 = archived only","disabled":true},{"key":"limit","value":"1000","description":"Maximum number of records to export (optional)","disabled":true}],"variable":[{"key":"csvTemplate","value":"5","description":"CSV Template ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"SKU,Product Name,Barcode\nSKU-001,Widget A,123456789\nSKU-002,Widget B,987654321"}]},{"name":"Import Data Using Template","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/csv-templates/import","host":["{{baseUrl}}"],"path":["api","csv-templates","import"]},"description":"Imports data from a CSV file using the specified template. Supports preview mode to validate the file before committing.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required)\n\nRequest body (multipart/form-data):\n- file (file, required unless tmp_file): The CSV file to import\n- tmp_file (string, required unless file): Path to a previously uploaded temporary file\n- template_id (integer, required unless model): ID of the template to use\n- model (string, required unless template_id): Model type for auto-detecting the template. Values: product, sales_order, product_brand, supplier\n- preview (boolean, optional): If true, returns a 10-row preview without importing (default: false)\n\n**Requires permission:** `csv_templates.import`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]},{"key":"template_id","type":"text","value":""},{"key":"model","type":"text","value":""},{"key":"preview","type":"text","value":""}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/csv-templates/import","host":["{{baseUrl}}"],"path":["api","csv-templates","import"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"imported\": 50,\n    \"errors\": [],\n    \"selected_template\": {\n      \"id\": 5,\n      \"name\": \"Standard Product Import\"\n    }\n  },\n  \"message\": \"Import completed successfully.\"\n}"}]},{"name":"Get Template Builder Columns","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/csv-templates/{model}/prepare","host":["{{baseUrl}}"],"path":["api","csv-templates",":model","prepare"],"variable":[{"key":"model","value":"product","description":"Model type. Valid values: product, sales_order, product_brand, supplier"}]},"description":"Returns the available columns and related data for building or editing a CSV template for the specified model type.\n\n**Required scope: `settings:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required)\n\nFor 'product' model, also returns:\n- relations.productAttributes: Available product attribute types\n- relations.productPricingTiers: Available pricing tiers\n- relations.suppliers: All suppliers\n- relations.SupplierPricingTiers: Supplier-specific pricing tiers"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/csv-templates/{model}/prepare","host":["{{baseUrl}}"],"path":["api","csv-templates",":model","prepare"],"variable":[{"key":"model","value":"product","description":"Model type. Valid values: product, sales_order, product_brand, supplier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"attributes\": [\n      \"id\",\n      \"sku\",\n      \"name\",\n      \"barcode\",\n      \"type\",\n      \"weight\",\n      \"weight_unit\",\n      \"brand\",\n      \"tags\",\n      \"image_url\",\n      \"category\",\n      \"length\",\n      \"width\",\n      \"height\",\n      \"created_at\"\n    ],\n    \"relations\": {\n      \"productAttributes\": [\n        {\n          \"id\": 1,\n          \"name\": \"Color\"\n        }\n      ],\n      \"productPricingTiers\": [\n        {\n          \"id\": 1,\n          \"name\": \"Retail\"\n        }\n      ],\n      \"suppliers\": [\n        {\n          \"id\": 1,\n          \"name\": \"ACME Corp\"\n        }\n      ],\n      \"SupplierPricingTiers\": [\n        {\n          \"id\": 1,\n          \"name\": \"Standard\"\n        }\n      ]\n    }\n  }\n}"}]},{"name":"Bulk Archive CSV Templates","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/csv-templates/archive","host":["{{baseUrl}}"],"path":["api","csv-templates","archive"]},"description":"Archives multiple CSV templates.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required)\n\nRequest body:\n- ids (array, required unless apply_to_all): Array of template IDs to archive\n- apply_to_all (boolean, optional): If true, archives all templates matching the provided filters\n- filters (array, optional): Filter criteria when apply_to_all is true\n\n**Requires permission:** `csv_templates.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    5,\n    6,\n    7\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/csv-templates/archive","host":["{{baseUrl}}"],"path":["api","csv-templates","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 csv templates archived successfully.\"\n}"}]},{"name":"Bulk Unarchive CSV Templates","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/csv-templates/unarchive","host":["{{baseUrl}}"],"path":["api","csv-templates","unarchive"]},"description":"Restores (unarchives) multiple CSV templates.\n\n**Required scope: `settings:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (Power User access required)\n\nRequest body:\n- ids (array, required unless apply_to_all): Array of template IDs to unarchive\n- apply_to_all (boolean, optional): If true, unarchives all templates matching the provided filters\n- filters (array, optional): Filter criteria when apply_to_all is true\n\n**Requires permission:** `csv_templates.archive`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    5,\n    6,\n    7\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/csv-templates/unarchive","host":["{{baseUrl}}"],"path":["api","csv-templates","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 csv templates unarchived successfully.\"\n}"}]}]},{"name":"Adjustments & Transfers","item":[{"name":"Get Inventory Adjustment","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-adjustments/{inventory_adjustment}","host":["{{baseUrl}}"],"path":["api","inventory-adjustments",":inventory_adjustment"],"variable":[{"key":"inventory_adjustment","value":"101","description":"Inventory adjustment ID"}]},"description":"Retrieve a single inventory adjustment by ID. Loads fifoLayers and required list view relations.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-adjustments/{inventory_adjustment}","host":["{{baseUrl}}"],"path":["api","inventory-adjustments",":inventory_adjustment"],"variable":[{"key":"inventory_adjustment","value":"101","description":"Inventory adjustment ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 101,\n    \"adjustment_date\": \"2024-03-15\",\n    \"product_id\": 42,\n    \"sku\": \"SKU-ABC-123\",\n    \"product_name\": \"Blue Widget 500ml\",\n    \"is_lot_tracked\": true,\n    \"lot_tracking_method\": \"fifo\",\n    \"shelf_life_days\": 180,\n    \"near_expiry_threshold_days\": 60,\n    \"min_remaining_shelf_life_days\": 105,\n    \"product_average_cost\": 12.5,\n    \"quantity\": 50,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"notes\": \"Cycle count correction\",\n    \"reason\": null,\n    \"is_correction\": false,\n    \"type\": {\n      \"link_type\": null,\n      \"link_id\": null,\n      \"parent_link_type\": null,\n      \"parent_link_id\": null\n    },\n    \"reference\": null,\n    \"unit_cost\": 12.5,\n    \"lots\": [\n      {\n        \"fifo_layer_id\": 9012,\n        \"batch_number\": \"LOT-2026-0042\",\n        \"expiry_date\": \"2026-07-04\",\n        \"manufacture_date\": \"2026-01-04\",\n        \"lot_notes\": \"Received from PO-1234\",\n        \"quantity\": 50\n      }\n    ],\n    \"archived_at\": null,\n    \"created_at\": \"2024-03-15T14:23:00Z\",\n    \"updated_at\": \"2024-03-15T14:23:00Z\"\n  }\n}"}]},{"name":"Update Inventory Adjustment","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-adjustments/{inventory_adjustment}","host":["{{baseUrl}}"],"path":["api","inventory-adjustments",":inventory_adjustment"],"variable":[{"key":"inventory_adjustment","value":"101","description":"Inventory adjustment ID"}]},"description":"Update an existing inventory adjustment.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- adjustment_date (optional): Date of adjustment in Y-m-d format\n- warehouse_id (optional): ID of the target warehouse\n- quantity (required): Adjustment quantity (numeric, < 1,000,000, decimals supported e.g. 0.5)\n- adjustment_type (optional): One of: increase, decrease\n- unit_cost (optional): Cost per unit (>= 0)\n- notes (optional): Free-text notes\n- warehouse_location_id (optional): Specific location within the warehouse\n- condition (optional): Item condition\n- integration_unique_id (optional): External reference ID\n\n**Requires permission:** `inventory.adjust`","body":{"mode":"raw","raw":"{\n  \"adjustment_date\": \"2024-03-16\",\n  \"warehouse_id\": 1,\n  \"quantity\": 75,\n  \"adjustment_type\": \"increase\",\n  \"unit_cost\": 13,\n  \"notes\": \"Updated: corrected quantity\",\n  \"warehouse_location_id\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/inventory-adjustments/{inventory_adjustment}","host":["{{baseUrl}}"],"path":["api","inventory-adjustments",":inventory_adjustment"],"variable":[{"key":"inventory_adjustment","value":"101","description":"Inventory adjustment ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Inventory has been adjusted\",\n  \"data\": {\n    \"id\": 101,\n    \"adjustment_date\": \"2024-03-16\",\n    \"product_id\": 42,\n    \"sku\": \"SKU-ABC-123\",\n    \"product_name\": \"Blue Widget 500ml\",\n    \"quantity\": 75,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"notes\": \"Updated: corrected quantity\",\n    \"unit_cost\": 13,\n    \"archived_at\": null,\n    \"updated_at\": \"2024-03-16T09:00:00Z\"\n  }\n}"}]},{"name":"Delete Inventory Adjustment","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-adjustments/{inventory_adjustment}","host":["{{baseUrl}}"],"path":["api","inventory-adjustments",":inventory_adjustment"],"variable":[{"key":"inventory_adjustment","value":"101","description":"Inventory adjustment ID"}]},"description":"Delete an inventory adjustment. Reverses the inventory movement. Will fail if doing so would result in negative fulfilled quantities.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.adjust`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/inventory-adjustments/{inventory_adjustment}","host":["{{baseUrl}}"],"path":["api","inventory-adjustments",":inventory_adjustment"],"variable":[{"key":"inventory_adjustment","value":"101","description":"Inventory adjustment ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"inventory adjustment 101 has been successfully deleted\"\n}"}]},{"name":"List Inventory Adjustments","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-adjustments?archived=0&search=&sort=-adjustment_date&page=1&limit=25","host":["{{baseUrl}}"],"path":["api","inventory-adjustments"],"query":[{"key":"archived","value":"0","description":"Filter by archived status: 0 (active) or 1 (archived). Default: 0","disabled":true},{"key":"search","value":"","description":"Search across product SKU, product name, notes","disabled":true},{"key":"sort","value":"-adjustment_date","description":"Sort field. Prefix with - for descending. Allowed: id, adjustment_date, quantity, unit_cost, created_at, updated_at","disabled":true},{"key":"page","value":"1","description":"Page number for pagination","disabled":true},{"key":"limit","value":"25","description":"Number of records per page","disabled":true}]},"description":"List all inventory adjustments. Supports filtering, sorting, search and pagination.\n\nAuthentication: Requires Bearer token.\n\nSort fields: id, adjustment_date, quantity, unit_cost, created_at, updated_at.\n\ntable filters: archived (0/1), search, filter[warehouse_id], filter[product_id]."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-adjustments?archived=0&search=&sort=-adjustment_date&page=1&limit=25","host":["{{baseUrl}}"],"path":["api","inventory-adjustments"],"query":[{"key":"archived","value":"0","description":"Filter by archived status: 0 (active) or 1 (archived). Default: 0","disabled":true},{"key":"search","value":"","description":"Search across product SKU, product name, notes","disabled":true},{"key":"sort","value":"-adjustment_date","description":"Sort field. Prefix with - for descending. Allowed: id, adjustment_date, quantity, unit_cost, created_at, updated_at","disabled":true},{"key":"page","value":"1","description":"Page number for pagination","disabled":true},{"key":"limit","value":"25","description":"Number of records per page","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 101,\n      \"adjustment_date\": \"2024-03-15\",\n      \"product_id\": 42,\n      \"sku\": \"SKU-ABC-123\",\n      \"product_name\": \"Blue Widget 500ml\",\n      \"product_average_cost\": 12.5,\n      \"quantity\": 50,\n      \"warehouse\": {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      },\n      \"notes\": \"Cycle count correction\",\n      \"reason\": null,\n      \"is_correction\": false,\n      \"type\": null,\n      \"reference\": null,\n      \"unit_cost\": 12.5,\n      \"archived_at\": null,\n      \"created_at\": \"2024-03-15T14:23:00Z\",\n      \"updated_at\": \"2024-03-15T14:23:00Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 5,\n  \"per_page\": 25,\n  \"total\": 112,\n  \"from\": 1,\n  \"to\": 25\n}"}]},{"name":"Create Inventory Adjustment","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-adjustments","host":["{{baseUrl}}"],"path":["api","inventory-adjustments"]},"description":"Create a new inventory adjustment. Supports increase, decrease, and set adjustment types.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields:\n- adjustment_date (required): Date of adjustment in Y-m-d format\n- product_id (required): ID of the product to adjust (not bundles/matrix products)\n- warehouse_id (required): ID of the target warehouse (must have a default location)\n- quantity (required): Adjustment quantity (numeric, < 1,000,000, decimals supported e.g. 0.5). Must be non-zero for increase/decrease\n- adjustment_type (required): One of: increase, decrease, set. Default: increase\n- unit_cost (optional/required for increase/set): Cost per unit (must be >= 0). Required if product has no average cost\n- notes (optional): Free-text notes\n- warehouse_location_id (optional): Specific location within the warehouse\n- condition (optional): Item condition description\n- integration_unique_id (optional): External reference ID\n\nErrors: 400 if insufficient stock (for decrease) or protected inventory conflict.\n\n**Requires permission:** `inventory.adjust`","body":{"mode":"raw","raw":"{\n  \"adjustment_date\": \"2024-03-15\",\n  \"product_id\": 42,\n  \"warehouse_id\": 1,\n  \"quantity\": 50,\n  \"adjustment_type\": \"increase\",\n  \"unit_cost\": 12.5,\n  \"notes\": \"Cycle count correction\",\n  \"warehouse_location_id\": null,\n  \"condition\": null,\n  \"integration_unique_id\": null,\n  \"lots\": [\n    {\n      \"batch_number\": \"LOT-2026-0042\",\n      \"manufacture_date\": \"2026-01-04\",\n      \"expiry_date\": \"2026-07-04\",\n      \"quantity\": 50,\n      \"lot_notes\": \"Received from PO-1234\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-adjustments","host":["{{baseUrl}}"],"path":["api","inventory-adjustments"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Inventory has been adjusted\",\n  \"data\": {\n    \"id\": 102,\n    \"adjustment_date\": \"2024-03-15\",\n    \"product_id\": 42,\n    \"sku\": \"SKU-ABC-123\",\n    \"product_name\": \"Blue Widget 500ml\",\n    \"is_lot_tracked\": true,\n    \"lot_tracking_method\": \"fifo\",\n    \"shelf_life_days\": 180,\n    \"near_expiry_threshold_days\": 60,\n    \"min_remaining_shelf_life_days\": 105,\n    \"product_average_cost\": 12.5,\n    \"quantity\": 50,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"notes\": \"Cycle count correction\",\n    \"reason\": null,\n    \"is_correction\": false,\n    \"unit_cost\": 12.5,\n    \"lots\": [\n      {\n        \"fifo_layer_id\": 9012,\n        \"batch_number\": \"LOT-2026-0042\",\n        \"expiry_date\": \"2026-07-04\",\n        \"manufacture_date\": \"2026-01-04\",\n        \"lot_notes\": \"Received from PO-1234\",\n        \"quantity\": 50\n      }\n    ],\n    \"archived_at\": null,\n    \"created_at\": \"2024-03-15T14:30:00Z\",\n    \"updated_at\": \"2024-03-15T14:30:00Z\"\n  }\n}"}]},{"name":"Bulk Delete Inventory Adjustments","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-adjustments","host":["{{baseUrl}}"],"path":["api","inventory-adjustments"]},"description":"Bulk delete multiple inventory adjustments by ID, or delete all matching current filters.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody: { ids: [integer array] } — specific IDs to delete. Omit ids to delete all matching current filters.\n\n**Requires permission:** `inventory.adjust`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/inventory-adjustments","host":["{{baseUrl}}"],"path":["api","inventory-adjustments"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 inventory adjustments deleted successfully\"\n}"}]},{"name":"Bulk Archive Inventory Adjustments","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-adjustments/archive","host":["{{baseUrl}}"],"path":["api","inventory-adjustments","archive"]},"description":"Bulk archive multiple inventory adjustments.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.adjust`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    101,\n    102\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/inventory-adjustments/archive","host":["{{baseUrl}}"],"path":["api","inventory-adjustments","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 inventory adjustments archived successfully\"\n}"}]},{"name":"Bulk Unarchive Inventory Adjustments","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-adjustments/unarchive","host":["{{baseUrl}}"],"path":["api","inventory-adjustments","unarchive"]},"description":"Bulk unarchive multiple inventory adjustments.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.adjust`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    101,\n    102\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/inventory-adjustments/unarchive","host":["{{baseUrl}}"],"path":["api","inventory-adjustments","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 inventory adjustments unarchived successfully\"\n}"}]},{"name":"Archive Inventory Adjustment","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-adjustments/{adjustment}/archive","host":["{{baseUrl}}"],"path":["api","inventory-adjustments",":adjustment","archive"],"variable":[{"key":"adjustment","value":"101","description":"Inventory adjustment ID"}]},"description":"Archive a single inventory adjustment.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.adjust`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/inventory-adjustments/{adjustment}/archive","host":["{{baseUrl}}"],"path":["api","inventory-adjustments",":adjustment","archive"],"variable":[{"key":"adjustment","value":"101","description":"Inventory adjustment ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"inventory adjustment 101 has been archived\",\n  \"data\": {\n    \"id\": 101,\n    \"archived_at\": \"2024-03-16T10:00:00Z\"\n  }\n}"}]},{"name":"Unarchive Inventory Adjustment","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-adjustments/{adjustment}/unarchived","host":["{{baseUrl}}"],"path":["api","inventory-adjustments",":adjustment","unarchived"],"variable":[{"key":"adjustment","value":"101","description":"Inventory adjustment ID"}]},"description":"Unarchive a single inventory adjustment.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.adjust`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/inventory-adjustments/{adjustment}/unarchived","host":["{{baseUrl}}"],"path":["api","inventory-adjustments",":adjustment","unarchived"],"variable":[{"key":"adjustment","value":"101","description":"Inventory adjustment ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"inventory adjustment 101 has been unarchived\",\n  \"data\": {\n    \"id\": 101,\n    \"archived_at\": null\n  }\n}"}]},{"name":"Check Inventory Adjustments Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory-adjustments/deletable","host":["{{baseUrl}}"],"path":["api","inventory-adjustments","deletable"]},"description":"Check which inventory adjustments can be deleted. Returns a deletability status for each requested ID.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody:\n- ids (required): Array of integer IDs to check\n\n**Requires permission:** `inventory.adjust`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    101,\n    102,\n    103\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory-adjustments/deletable","host":["{{baseUrl}}"],"path":["api","inventory-adjustments","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 101,\n      \"deletable\": true,\n      \"reason\": null\n    },\n    {\n      \"id\": 102,\n      \"deletable\": false,\n      \"reason\": {\n        \"fulfilledQuantity\": \"The fulfilled quantity would not be covered\"\n      }\n    },\n    {\n      \"id\": 103,\n      \"deletable\": true,\n      \"reason\": null\n    }\n  ]\n}"}]},{"name":"Get Adjustment Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory-adjustments/{inventoryAdjustment}/activity-log?per_page=15","host":["{{baseUrl}}"],"path":["api","inventory-adjustments",":inventoryAdjustment","activity-log"],"query":[{"key":"per_page","value":"15","description":"Number of activity log entries per page. Default: 15","disabled":true}],"variable":[{"key":"inventoryAdjustment","value":"101","description":"Inventory adjustment ID"}]},"description":"Get paginated activity log for a specific inventory adjustment. Includes who made the change and what changed.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nQuery parameters:\n- per_page: Number of entries per page (default: 15)\n- filter[search]: Search by causer name, event, or properties\n- page: Page number for pagination"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory-adjustments/{inventoryAdjustment}/activity-log?per_page=15","host":["{{baseUrl}}"],"path":["api","inventory-adjustments",":inventoryAdjustment","activity-log"],"query":[{"key":"per_page","value":"15","description":"Number of activity log entries per page. Default: 15","disabled":true}],"variable":[{"key":"inventoryAdjustment","value":"101","description":"Inventory adjustment ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5001,\n      \"description\": \"updated\",\n      \"event\": \"updated\",\n      \"subject_type\": \"InventoryAdjustment\",\n      \"subject_id\": 101,\n      \"properties\": {\n        \"old\": {\n          \"quantity\": 50\n        },\n        \"attributes\": {\n          \"quantity\": 75\n        }\n      },\n      \"causer_name\": \"Jane Smith\",\n      \"created_at\": \"2024-03-16T09:00:00Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1\n}"}]},{"name":"List Allocations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations?page=1&per_page=10&filter[search]=&filter[warehouse_id]=&filter[supplier_id]=&filter[product_id]=&filter[status]=active&filter[id]=&filter[quantity]=&filter[priority]=&filter[created_at]=&filter[fulfilled_at]=&filter[cancelled_at]=&filter[product_sku]=&filter[product_name]=&filter_groups[conjunction]=and&sort=-created_at","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"filter[search]","value":"","description":"Search across id, sku, product name, and source reference","disabled":true},{"key":"filter[warehouse_id]","value":"","description":"Exact match on warehouse ID","disabled":true},{"key":"filter[supplier_id]","value":"","description":"Exact match on supplier ID","disabled":true},{"key":"filter[product_id]","value":"","description":"Exact match on product ID","disabled":true},{"key":"filter[status]","value":"active","description":"Sentinel: all | active (default) | pending | released | cancelled | fulfilled","disabled":true},{"key":"filter[id]","value":"","description":"Numeric filter on id","disabled":true},{"key":"filter[quantity]","value":"","description":"Numeric filter on quantity","disabled":true},{"key":"filter[priority]","value":"","description":"Numeric filter on priority","disabled":true},{"key":"filter[created_at]","value":"","description":"Datetime filter on created_at","disabled":true},{"key":"filter[fulfilled_at]","value":"","description":"Datetime filter on fulfilled_at","disabled":true},{"key":"filter[cancelled_at]","value":"","description":"Datetime filter on cancelled_at","disabled":true},{"key":"filter[product_sku]","value":"","description":"Text filter on product SKU","disabled":true},{"key":"filter[product_name]","value":"","description":"Text filter on product name","disabled":true},{"key":"filter_groups[conjunction]","value":"and","description":"Advanced filter tree conjunction: and | or. Use with filter_groups[children][] for grouped conditions.","disabled":true},{"key":"sort","value":"-created_at","description":"Sort field. Prefix with - for descending. Options: id, quantity, priority, status, created_at","disabled":true}]},"description":"Returns a paginated list of inventory allocations.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Simple filters** (`filter[status]`): Sentinel values control the list scope:\n- `active` (default) — pending + released allocations\n- `all` — every status\n- `pending`, `released`, `cancelled`, `fulfilled` — specific status\n\n**Advanced filters** (`filter_groups`): Tree-based GroupedFilterSupport filters on id, quantity, priority, status, warehouse_id, supplier_id, created_at, fulfilled_at, cancelled_at, product_sku, product_name. The `filter[search]` parameter is a cross-column search.\n\n**Sort options**: id, quantity, priority, status, created_at. Default: `-created_at`.\n\nThe response also includes `unfiltered_total`, the count of rows in the base scope before any user filters are applied — used by the frontend to show export totals."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations?page=1&per_page=10&filter[search]=&filter[warehouse_id]=&filter[supplier_id]=&filter[product_id]=&filter[status]=active&filter[id]=&filter[quantity]=&filter[priority]=&filter[created_at]=&filter[fulfilled_at]=&filter[cancelled_at]=&filter[product_sku]=&filter[product_name]=&filter_groups[conjunction]=and&sort=-created_at","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"filter[search]","value":"","description":"Search across id, sku, product name, and source reference","disabled":true},{"key":"filter[warehouse_id]","value":"","description":"Exact match on warehouse ID","disabled":true},{"key":"filter[supplier_id]","value":"","description":"Exact match on supplier ID","disabled":true},{"key":"filter[product_id]","value":"","description":"Exact match on product ID","disabled":true},{"key":"filter[status]","value":"active","description":"Sentinel: all | active (default) | pending | released | cancelled | fulfilled","disabled":true},{"key":"filter[id]","value":"","description":"Numeric filter on id","disabled":true},{"key":"filter[quantity]","value":"","description":"Numeric filter on quantity","disabled":true},{"key":"filter[priority]","value":"","description":"Numeric filter on priority","disabled":true},{"key":"filter[created_at]","value":"","description":"Datetime filter on created_at","disabled":true},{"key":"filter[fulfilled_at]","value":"","description":"Datetime filter on fulfilled_at","disabled":true},{"key":"filter[cancelled_at]","value":"","description":"Datetime filter on cancelled_at","disabled":true},{"key":"filter[product_sku]","value":"","description":"Text filter on product SKU","disabled":true},{"key":"filter[product_name]","value":"","description":"Text filter on product name","disabled":true},{"key":"filter_groups[conjunction]","value":"and","description":"Advanced filter tree conjunction: and | or. Use with filter_groups[children][] for grouped conditions.","disabled":true},{"key":"sort","value":"-created_at","description":"Sort field. Prefix with - for descending. Options: id, quantity, priority, status, created_at","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 10045,\n      \"product_id\": 812,\n      \"product\": {\n        \"id\": 812,\n        \"sku\": \"WIDGET-BLU\",\n        \"name\": \"Blue Widget\"\n      },\n      \"warehouse_id\": 3,\n      \"warehouse\": {\n        \"id\": 3,\n        \"name\": \"East Coast DC\"\n      },\n      \"link_id\": 55347,\n      \"link_type\": \"App\\\\Models\\\\SalesOrderLine\",\n      \"link_type_label\": \"Sales Order Line\",\n      \"link_route\": \"/sales-orders/9821\",\n      \"link_reference\": \"SO-9821\",\n      \"date\": \"2025-04-10\",\n      \"quantity\": 5,\n      \"status\": \"pending\",\n      \"status_label\": \"Pending\",\n      \"priority\": 1,\n      \"supplier_id\": 14,\n      \"supplier\": {\n        \"id\": 14,\n        \"name\": \"Acme Corp\"\n      },\n      \"purchase_order_line_id\": null,\n      \"is_tight_coverage\": false,\n      \"release_source_id\": null,\n      \"release_source_type\": null,\n      \"fulfilled_at\": null,\n      \"cancelled_at\": null,\n      \"created_at\": \"2025-04-10T08:22:11.000000Z\",\n      \"updated_at\": \"2025-04-10T08:22:11.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 12,\n  \"per_page\": 10,\n  \"total\": 118,\n  \"unfiltered_total\": 947,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"Get Allocation","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/{allocation}","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations",":allocation"],"variable":[{"key":"allocation","value":"10045","description":"Allocation ID"}]},"description":"Returns a single inventory allocation by ID.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nwarehouse_location_id and the nested warehouse_location object reflect the bin this allocation is pinned to (null when not pinned). warehouse.locations_enabled indicates whether the warehouse uses bin locations."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/{allocation}","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations",":allocation"],"variable":[{"key":"allocation","value":"10045","description":"Allocation ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 10045,\n  \"product_id\": 812,\n  \"product\": {\n    \"id\": 812,\n    \"sku\": \"WIDGET-BLU\",\n    \"name\": \"Blue Widget\"\n  },\n  \"warehouse_id\": 3,\n  \"warehouse\": {\n    \"id\": 3,\n    \"name\": \"East Coast DC\",\n    \"locations_enabled\": true\n  },\n  \"warehouse_location_id\": 402,\n  \"warehouse_location\": {\n    \"id\": 402,\n    \"code\": \"ST-09\",\n    \"name\": \"Reserve Aisle 9\"\n  },\n  \"link_id\": 55347,\n  \"link_type\": \"App\\\\Models\\\\SalesOrderLine\",\n  \"link_type_label\": \"Sales Order Line\",\n  \"link_route\": \"/sales-orders/9821\",\n  \"link_reference\": \"SO-9821\",\n  \"date\": \"2025-04-10\",\n  \"quantity\": 5,\n  \"status\": \"pending\",\n  \"status_label\": \"Pending\",\n  \"priority\": 1,\n  \"supplier_id\": 14,\n  \"supplier\": {\n    \"id\": 14,\n    \"name\": \"Acme Corp\"\n  },\n  \"purchase_order_line_id\": null,\n  \"is_tight_coverage\": false,\n  \"release_source_id\": null,\n  \"release_source_type\": null,\n  \"fulfilled_at\": null,\n  \"cancelled_at\": null,\n  \"created_at\": \"2025-04-10T08:22:11.000000Z\",\n  \"updated_at\": \"2025-04-10T08:22:11.000000Z\"\n}"}]},{"name":"Reorder Allocation","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/{allocation}/reorder","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations",":allocation","reorder"],"variable":[{"key":"allocation","value":"10045","description":"Allocation ID"}]},"description":"Reorders an allocation relative to another.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Body**:\n- `reorder_type` — `above` | `below` | `top` | `bottom`\n- `options.above_id` — ID of the allocation to place this one above (used when reorder_type is `above`)","body":{"mode":"raw","raw":"{\n  \"reorder_type\": \"above\",\n  \"options\": {\n    \"above_id\": 10040\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/{allocation}/reorder","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations",":allocation","reorder"],"variable":[{"key":"allocation","value":"10045","description":"Allocation ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Allocation reordered successfully.\"\n}"}]},{"name":"Reassign Coverage","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/{allocation}/reassign-coverage","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations",":allocation","reassign-coverage"],"variable":[{"key":"allocation","value":"10045","description":"Allocation ID"}]},"description":"Manually (re)assigns a single allocation's PO-line coverage. Binding a PO line transitions the allocation to `AwaitingReceipt` and pins it as tight coverage (so a later automatic rebalance won't move it).\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Body**:\n- `purchase_order_line_id` (required, nullable integer) — the PO line to cover this allocation with. Pass `null` to clear coverage and revert the allocation to `Planned`.\n\nReturns `422` when the PO line belongs to a different product, or the PO is draft/closed/cancelled/voided, or the line is already fully received.","body":{"mode":"raw","raw":"{\n  \"purchase_order_line_id\": 5567\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/{allocation}/reassign-coverage","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations",":allocation","reassign-coverage"],"variable":[{"key":"allocation","value":"10045","description":"Allocation ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 10045,\n    \"priority\": 3,\n    \"backorder_date\": \"2026-05-10T14:22:00.000000Z\",\n    \"backordered_quantity\": 8,\n    \"quantity\": 8,\n    \"status\": \"awaiting_receipt\",\n    \"status_label\": \"Awaiting Receipt\",\n    \"is_tight_coverage\": true,\n    \"sales_order\": {\n      \"id\": 33021,\n      \"number\": \"SO-033021\",\n      \"order_date\": \"2026-05-08\"\n    },\n    \"sku\": {\n      \"id\": 812,\n      \"sku\": \"WIDGET-BLU\",\n      \"name\": \"Blue Widget\"\n    },\n    \"supplier\": {\n      \"id\": 47,\n      \"name\": \"Acme Supplies\"\n    },\n    \"coverage\": {\n      \"purchase_order_line_id\": 5567,\n      \"purchase_order_id\": 902,\n      \"purchase_order_number\": \"PO-000902\",\n      \"covered_quantity\": 8,\n      \"is_tight\": true\n    },\n    \"scheduled_at\": \"2026-05-20\",\n    \"created_at\": \"2026-05-10T14:22:00.000000Z\"\n  }\n}"}]},{"name":"Get Move Targets","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/{allocation}/move-targets","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations",":allocation","move-targets"],"variable":[{"key":"allocation","value":"10045","description":"Allocation ID (must be an on-hand `allocated` allocation linked to a sales order line)"}]},"description":"Lists the candidate destination sales order lines that on-hand (`allocated`) stock from this allocation can be moved to.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the backordered lines (status `planned` / `awaiting_receipt`) waiting on the **same product and warehouse pool** as the source allocation, excluding the source's own line. Each entry includes how much of that line is still backordered, so the caller can cap the move quantity.\n\nReturns an empty list when the allocation is not `allocated`, is not linked to a sales order line, or there are no other backordered lines in the pool."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/{allocation}/move-targets","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations",":allocation","move-targets"],"variable":[{"key":"allocation","value":"10045","description":"Allocation ID (must be an on-hand `allocated` allocation linked to a sales order line)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"sales_order_line_id\": 55512,\n      \"sales_order_id\": 33099,\n      \"sales_order_number\": \"SO-033099\",\n      \"customer_id\": 33099,\n      \"customer_name\": \"Acme Retail\",\n      \"order_date\": \"2026-05-12\",\n      \"backordered_quantity\": 4\n    },\n    {\n      \"sales_order_line_id\": 55540,\n      \"sales_order_id\": 33104,\n      \"sales_order_number\": \"SO-033104\",\n      \"customer_id\": 33104,\n      \"customer_name\": \"Blue Mountain Co\",\n      \"order_date\": \"2026-05-13\",\n      \"backordered_quantity\": 2\n    }\n  ]\n}"}]},{"name":"Move To Line","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/{allocation}/move","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations",":allocation","move"],"variable":[{"key":"allocation","value":"10045","description":"Source allocation ID (must be an on-hand `allocated` allocation linked to a sales order line)"}]},"description":"Moves on-hand (`allocated`) stock from this allocation to a backordered line in the same product + warehouse pool — a **conserved swap**.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe moved quantity releases the destination line's backorder (`planned` / `awaiting_receipt` → `allocated`), carrying the source allocation's FIFO `release_source` reference. The source's `allocated` row is reduced by the same quantity (or cancelled if fully consumed), and the source's demand is recreated as a `planned` backorder. Total on-hand commitment, total demand, and FIFO cost layering are all preserved.\n\n**Body**:\n- `destination_sales_order_line_id` (required, integer) — the backordered sales order line to move stock to. Must exist and be in the same product + warehouse pool. Get valid targets from `GET /move-targets`.\n- `quantity` (required, numeric, > 0) — how much on-hand stock to move. Must not exceed the source's movable on-hand quantity (allocated minus any stock committed to open fulfillments) nor the destination line's backordered quantity.\n\nReturns `422` when the source is not `allocated`, the destination is a different product/warehouse or the same line, the quantity exceeds the movable on-hand stock (e.g. committed to an open fulfillment) or the destination's backorder, or the destination has no matching backorder to release.","body":{"mode":"raw","raw":"{\n  \"destination_sales_order_line_id\": 55512,\n  \"quantity\": 4\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/{allocation}/move","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations",":allocation","move"],"variable":[{"key":"allocation","value":"10045","description":"Source allocation ID (must be an on-hand `allocated` allocation linked to a sales order line)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 10072,\n    \"priority\": 2,\n    \"backorder_date\": null,\n    \"backordered_quantity\": 0,\n    \"quantity\": 4,\n    \"status\": \"allocated\",\n    \"status_label\": \"Allocated\",\n    \"is_tight_coverage\": false,\n    \"sales_order\": {\n      \"id\": 33099,\n      \"number\": \"SO-033099\",\n      \"order_date\": \"2026-05-12\"\n    },\n    \"sku\": {\n      \"id\": 812,\n      \"sku\": \"WIDGET-BLU\",\n      \"name\": \"Blue Widget\"\n    },\n    \"supplier\": {\n      \"id\": 47,\n      \"name\": \"Acme Supplies\"\n    },\n    \"coverage\": null,\n    \"scheduled_at\": null,\n    \"created_at\": \"2026-06-15T09:14:00.000000Z\"\n  }\n}"}]},{"name":"List Pull-Stock Donors","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/pull-stock-donors?destination_sales_order_line_id=55600","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","pull-stock-donors"],"query":[{"key":"destination_sales_order_line_id","value":"55600","description":"Required. The backordered sales order line that would receive the pulled stock.","disabled":true}]},"description":"Lists the sales order lines a backordered line can pull on-hand stock from. Returns other sales orders holding **soft** allocations (`allocated`, not yet committed to a printed shipping label) of the same product in the same warehouse pool, excluding the backordered line's own sales order.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach donor entry reports `movable_quantity` (soft on-hand that can be pulled) and `priority` (lower number = higher priority), ordered least-urgent first so the safest donors appear at the top.\n\nQuery:\n- `destination_sales_order_line_id` (required, integer) — the backordered sales order line that would receive the stock.\n\nReturns an empty list when no other order in the pool holds soft-allocated stock."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/pull-stock-donors?destination_sales_order_line_id=55600","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","pull-stock-donors"],"query":[{"key":"destination_sales_order_line_id","value":"55600","description":"Required. The backordered sales order line that would receive the pulled stock.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"sales_order_line_id\": 55540,\n      \"sales_order_id\": 33104,\n      \"sales_order_number\": \"SO-033104\",\n      \"customer_id\": 44011,\n      \"customer_name\": \"Blue Mountain Co\",\n      \"order_date\": \"2026-05-13\",\n      \"priority\": 8,\n      \"movable_quantity\": 5\n    },\n    {\n      \"sales_order_line_id\": 55512,\n      \"sales_order_id\": 33099,\n      \"sales_order_number\": \"SO-033099\",\n      \"customer_id\": 44011,\n      \"customer_name\": \"Acme Retail\",\n      \"order_date\": \"2026-05-12\",\n      \"priority\": 3,\n      \"movable_quantity\": 2\n    }\n  ]\n}"}]},{"name":"Pull Stock To Line","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/pull-stock","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","pull-stock"]},"description":"Pulls soft on-hand stock from one or more donor sales order lines onto a backordered line, atomically. Each donor's soft allocation is backordered by the pulled quantity and the freed on-hand is allocated to the destination line — a **conserved swap** that preserves total demand and FIFO cost layering across every affected order. All pulls succeed or roll back together.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Body**:\n- `destination_sales_order_line_id` (required, integer) — the backordered line that receives the stock.\n- `pulls` (required, array, min 1) — the donors to pull from.\n- `pulls[].sales_order_line_id` (required, integer) — a donor sales order line holding soft allocations of the same product + warehouse. Get valid donors from `GET /pull-stock-donors`.\n- `pulls[].quantity` (required, numeric, > 0) — how much soft on-hand to pull from that donor.\n\nReturns `422` when a donor no longer holds enough soft-allocated stock, a donor is the same line as the destination, or the destination has no matching backorder to receive the pull.\n\nOn success returns the total `pulled_quantity` and the refreshed donor list.","body":{"mode":"raw","raw":"{\n  \"destination_sales_order_line_id\": 55600,\n  \"pulls\": [\n    {\n      \"sales_order_line_id\": 55540,\n      \"quantity\": 2\n    },\n    {\n      \"sales_order_line_id\": 55512,\n      \"quantity\": 1\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/pull-stock","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","pull-stock"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"pulled_quantity\": 3,\n    \"donors\": [\n      {\n        \"sales_order_line_id\": 55540,\n        \"sales_order_id\": 33104,\n        \"sales_order_number\": \"SO-033104\",\n        \"customer_id\": 44011,\n        \"customer_name\": \"Blue Mountain Co\",\n        \"order_date\": \"2026-05-13\",\n        \"priority\": 8,\n        \"movable_quantity\": 3\n      }\n    ]\n  }\n}"}]},{"name":"Reserve Allocation to Bin","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/{inventoryAllocation}/pin-bin","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations",":inventoryAllocation","pin-bin"],"variable":[{"key":"inventoryAllocation","value":"","description":"The inventory allocation ID."}]},"description":"Hard-reserve an allocation's stock to a specific bin so it's held there for its order and picked from there.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"warehouse_location_id\": 16\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/{inventoryAllocation}/pin-bin","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations",":inventoryAllocation","pin-bin"],"variable":[{"key":"inventoryAllocation","value":"","description":"The inventory allocation ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 221,\n    \"warehouse_location_id\": 16\n  },\n  \"message\": \"Stock reserved to the bin.\"\n}"}]},{"name":"Clear Bin Reservation","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/{inventoryAllocation}/pin-bin","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations",":inventoryAllocation","pin-bin"],"variable":[{"key":"inventoryAllocation","value":"","description":"The inventory allocation ID."}]},"description":"Clear an allocation's hard bin reservation.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/{inventoryAllocation}/pin-bin","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations",":inventoryAllocation","pin-bin"],"variable":[{"key":"inventoryAllocation","value":"","description":"The inventory allocation ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 221,\n    \"warehouse_location_id\": null\n  },\n  \"message\": \"Bin reservation cleared.\"\n}"}]},{"name":"List Backorders","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/backorders?page=1&per_page=10&filter[search]=&filter[status]=&filter[supplier_id]=&filter[covered_by_po]=&filter[eta_state]=&filter[sku]=&filter[product_name]=&filter[supplier_name]=&filter[quantity]=&filter[priority]=&filter[created_at]=&sort=priority","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","backorders"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"filter[search]","value":"","description":"Search across id, sku, product name, supplier name, warehouse name, sales order number, PO number","disabled":true},{"key":"filter[status]","value":"","description":"Backorder status: pending | released | partial | cancelled","disabled":true},{"key":"filter[supplier_id]","value":"","description":"Filter by supplier ID","disabled":true},{"key":"filter[covered_by_po]","value":"","description":"true | false — filter by whether the backorder is covered by a PO","disabled":true},{"key":"filter[eta_state]","value":"","description":"on_time | late | unknown","disabled":true},{"key":"filter[sku]","value":"","description":"Text filter on product SKU","disabled":true},{"key":"filter[product_name]","value":"","description":"Text filter on product name","disabled":true},{"key":"filter[supplier_name]","value":"","description":"Text filter on supplier name","disabled":true},{"key":"filter[quantity]","value":"","description":"Numeric filter on backorder quantity","disabled":true},{"key":"filter[priority]","value":"","description":"Numeric filter on priority","disabled":true},{"key":"filter[created_at]","value":"","description":"Datetime filter on created_at","disabled":true},{"key":"sort","value":"priority","description":"Sort field. Options: id, priority, quantity, created_at, updated_at, backorder_date, backordered_quantity, released_quantity, sku, product_name, supplier, warehouse, order_date, sales_order_number, eta. Default: priority","disabled":true}]},"description":"Returns a paginated backorder queue showing allocations that are awaiting stock.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Supports GET and POST** — POST can be used to send large filter payloads in the request body.\n\n**Sort options**: id, priority, quantity, created_at, updated_at, backorder_date, backordered_quantity, released_quantity, sku, product_name, supplier, warehouse, order_date, sales_order_number, eta. Default: `priority`.\n\nEach backorder row also carries `in_transit_stock` (inbound stock in motion across warehouses), alongside `other_warehouse_stock`. Both are populated in the CSV/XLSX export.\n\nEach row also carries `available_at_warehouse` — unclaimed stock in the backorder's own warehouse, named by `warehouse`. A value above zero means the backorder can be released now; filter to just those rows with `filter[releasable_now.is]=true`. This is distinct from `other_warehouse_stock`, which would require the line to be re-routed first.\n\nBackorder rows cover two demand types: sales-order lines and manufacturing-order component lines. Each row carries `demand_type` (`sales`|`manufacturing`), a generic `source_number`/`source_id` (sales order number or manufacturing order number), `manufacturing_order_id` (null for sales rows), and `demand_date` (sales order date, or the manufacturing order's scheduled start). `sales_order_number` remains for sales rows and is null for manufacturing rows. Search also matches manufacturing order numbers."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/backorders?page=1&per_page=10&filter[search]=&filter[status]=&filter[supplier_id]=&filter[covered_by_po]=&filter[eta_state]=&filter[sku]=&filter[product_name]=&filter[supplier_name]=&filter[quantity]=&filter[priority]=&filter[created_at]=&sort=priority","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","backorders"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"filter[search]","value":"","description":"Search across id, sku, product name, supplier name, warehouse name, sales order number, PO number","disabled":true},{"key":"filter[status]","value":"","description":"Backorder status: pending | released | partial | cancelled","disabled":true},{"key":"filter[supplier_id]","value":"","description":"Filter by supplier ID","disabled":true},{"key":"filter[covered_by_po]","value":"","description":"true | false — filter by whether the backorder is covered by a PO","disabled":true},{"key":"filter[eta_state]","value":"","description":"on_time | late | unknown","disabled":true},{"key":"filter[sku]","value":"","description":"Text filter on product SKU","disabled":true},{"key":"filter[product_name]","value":"","description":"Text filter on product name","disabled":true},{"key":"filter[supplier_name]","value":"","description":"Text filter on supplier name","disabled":true},{"key":"filter[quantity]","value":"","description":"Numeric filter on backorder quantity","disabled":true},{"key":"filter[priority]","value":"","description":"Numeric filter on priority","disabled":true},{"key":"filter[created_at]","value":"","description":"Datetime filter on created_at","disabled":true},{"key":"sort","value":"priority","description":"Sort field. Options: id, priority, quantity, created_at, updated_at, backorder_date, backordered_quantity, released_quantity, sku, product_name, supplier, warehouse, order_date, sales_order_number, eta. Default: priority","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 10045,\n      \"priority\": 1,\n      \"backorder_date\": \"2025-04-10\",\n      \"order_date\": \"2025-04-08\",\n      \"sku\": {\n        \"id\": 812,\n        \"sku\": \"WIDGET-BLU\"\n      },\n      \"product_name\": \"Blue Widget\",\n      \"warehouse\": {\n        \"id\": 3,\n        \"name\": \"Midpoint\"\n      },\n      \"backordered_quantity\": 5,\n      \"released_quantity\": 0,\n      \"quantity\": 5,\n      \"status\": \"pending\",\n      \"status_label\": \"Pending\",\n      \"supplier\": {\n        \"id\": 14,\n        \"name\": \"Acme Corp\"\n      },\n      \"sales_order_number\": {\n        \"sales_order_line_id\": 55347,\n        \"sales_order_id\": 9821,\n        \"sales_order_number\": \"SO-9821\"\n      },\n      \"covered_by_po\": false,\n      \"is_tight_coverage\": false,\n      \"purchase_order_line_id\": null,\n      \"purchase_order\": null,\n      \"eta\": null,\n      \"eta_state\": \"unknown\",\n      \"eta_source\": null,\n      \"has_split_coverage\": false,\n      \"available_at_warehouse\": 83,\n      \"other_warehouse_stock\": {\n        \"total\": 0,\n        \"warehouses\": []\n      },\n      \"in_transit_stock\": {\n        \"total\": 0,\n        \"warehouses\": []\n      },\n      \"scheduled_at\": null,\n      \"created_at\": \"2025-04-10T08:22:11.000000Z\",\n      \"updated_at\": \"2025-04-10T08:22:11.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 5,\n  \"per_page\": 10,\n  \"total\": 47,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"Search Backorders","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/backorders","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","backorders"]},"description":"Returns the same paginated list of backorders as the GET version of this endpoint, but accepts filters as a JSON request body instead of the query string. Use this POST variant when the filter set is too large or too complex to fit in a URL.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA `status` scope selects which backorders are returned: `active` (default; outstanding backorders awaiting stock or receipt), `released` (backorders that have since been allocated or fulfilled), `released_fulfilled`, `released_submitted`, `released_unfulfilled`, or `all`.\n\nProvide advanced filters under `filter_groups`, a tree of conditions combined with a `conjunction` of `and` or `or`. Each condition names a `column`, an `operator`, and a `value`. A free-text `search` term matches against allocation id, SKU, product name, supplier, warehouse name, sales order number, and purchase order number.\n\nText columns (`sku`, `product_name`, `supplier_name`, `warehouse_name`) accept: is, is_not, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, is_one_of, is_not_one_of.\nNumeric and date columns (`id`, `quantity`, `priority`, `backordered_quantity`, `released_quantity`, `created_at`, `updated_at`, `backorder_date`, `order_date`) accept: is, is_not, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between, is_empty, is_not_empty. Boolean filters `covered_by_po` and `has_other_wh_stock` accept `is` with true/false; `eta_state` accepts `is` with `has_eta`, `late_eta`, or `missing_eta`; `supplier_id` accepts a numeric id or `none`; `warehouse_id` accepts a numeric id.\n\nSortable columns (via `sort`, prefix with `-` for descending): id, priority, quantity, created_at, updated_at, backorder_date, backordered_quantity, released_quantity, sku, product_name, supplier, warehouse, order_date, sales_order_number, eta. Results default to `priority`.","body":{"mode":"raw","raw":"{\n  \"status\": \"active\",\n  \"per_page\": 25,\n  \"page\": 1,\n  \"sort\": \"priority\",\n  \"filter_groups\": {\n    \"conjunction\": \"and\",\n    \"children\": [\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"covered_by_po\",\n          \"operator\": \"is\",\n          \"value\": false\n        }\n      },\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"product_name\",\n          \"operator\": \"contains\",\n          \"value\": \"Widget\"\n        }\n      }\n    ]\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/backorders","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","backorders"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 77120,\n      \"priority\": 1,\n      \"backorder_date\": \"2026-06-25T11:03:00.000000Z\",\n      \"order_date\": \"2026-06-24\",\n      \"sku\": {\n        \"id\": 3312,\n        \"sku\": \"WIDGET-BLUE-01\"\n      },\n      \"product_name\": \"Blue Widget\",\n      \"warehouse\": {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      },\n      \"backordered_quantity\": 12,\n      \"released_quantity\": 0,\n      \"quantity\": 12,\n      \"status\": \"planned\",\n      \"status_label\": \"Planned\",\n      \"supplier\": {\n        \"id\": 44,\n        \"name\": \"Acme Supply Co\"\n      },\n      \"sales_order_number\": {\n        \"sales_order_line_id\": 88231,\n        \"sales_order_id\": 6021,\n        \"sales_order_number\": \"SO-6021\"\n      },\n      \"covered_by_po\": false,\n      \"is_tight_coverage\": false,\n      \"purchase_order_line_id\": null,\n      \"purchase_order\": null,\n      \"eta\": null,\n      \"eta_state\": \"missing_no_po\",\n      \"eta_source\": null,\n      \"has_split_coverage\": false,\n      \"other_warehouse_stock\": [],\n      \"scheduled_at\": \"2026-07-10\",\n      \"created_at\": \"2026-06-25T11:03:00.000000Z\",\n      \"updated_at\": \"2026-06-25T11:03:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"per_page\": 25,\n  \"from\": 1,\n  \"to\": 1,\n  \"last_page\": 3,\n  \"total\": 61,\n  \"next_page_url\": \"https://api.example.com/api/v2/inventory-allocations/backorders?page=2\",\n  \"prev_page_url\": null\n}"}]},{"name":"Export Backorders","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/backorders/export?format=xlsx&scope=filtered&filter[status]=active","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","backorders","export"],"query":[{"key":"format","value":"xlsx","description":"Export file format: xlsx (default) or csv","disabled":true},{"key":"scope","value":"filtered","description":"Which records to export: all (ignores filters, keeps status tab) | filtered (default, honors active filters/search) | current_page (just the page the user is viewing) | selected (requires ids param)","disabled":true},{"key":"filter[status]","value":"active","description":"Status tab. Preserved even when scope=all. Values: active | pending | released | partial | cancelled","disabled":true}]},"description":"Downloads the backorder queue as an XLSX or CSV file.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nUses the same status scope, search, and grouped filter pipeline as `List Backorders`, so the export always matches what the user sees in the UI for the active status tab.\n\n**Scopes:**\n- `all` — export every backorder in the current status tab, ignoring filters/search/sort\n- `filtered` (default) — honor all active filters, search, and sort\n- `current_page` — just the page currently rendered by the list view\n- `selected` — the IDs passed via the `ids` query param (comma-separated)\n\n**Columns:**\nPass `columns` as a comma-separated list of keys to limit the export to the visible list view columns (in request order). Omit `columns` to export every available column.\n\n**Response:**\nReturns a binary download — `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` for xlsx, `text/csv` for csv. Filename pattern: `backorders-YYYY-MM-DD-HH-MM-SS.{format}`.\n\nEach backorder row also carries `in_transit_stock` (inbound stock in motion across warehouses), alongside `other_warehouse_stock`. Both are populated in the CSV/XLSX export.\n\nThe export includes Demand Type, Source, and Demand Date columns covering both sales-order and manufacturing-order component backorders."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/backorders/export?format=xlsx&scope=filtered&filter[status]=active","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","backorders","export"],"query":[{"key":"format","value":"xlsx","description":"Export file format: xlsx (default) or csv","disabled":true},{"key":"scope","value":"filtered","description":"Which records to export: all (ignores filters, keeps status tab) | filtered (default, honors active filters/search) | current_page (just the page the user is viewing) | selected (requires ids param)","disabled":true},{"key":"filter[status]","value":"active","description":"Status tab. Preserved even when scope=all. Values: active | pending | released | partial | cancelled","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"<binary xlsx contents>"}]},{"name":"List Releasable Backorders","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/releasable-backorders","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","releasable-backorders"]},"description":"Backorders whose stock is already on hand in the warehouse the backorder is assigned to, and which a release would therefore promote immediately.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBackorder release runs when stock arrives and is not retried, so a backorder raised against stock that was already on hand — or one whose release did not run — stays open indefinitely while the units sit unallocated. This endpoint returns exactly those rows.\n\nAvailability is evaluated per (product, warehouse) pool: physical stock minus what is already allocated, on hold, or in a non-sellable location. Rows are consumed in release priority order, so when several backorders queue on one pool only those the pool can actually cover are returned.\n\nRows where releasing would be incorrect are excluded: orders that are cancelled or closed, lines the sales channel has already shipped, and lines with no quantity left to ship. Orders held to a future date ARE included — releasing reserves stock rather than shipping it, and the hold is reported per row in `hold_until_date`.\n\n`count`, `units` and `orders` describe the whole set; `rows` is capped by `limit`, so compare `scanned_count` against `count` (or read `truncated`) before treating the list as complete.\n\nRelease the returned rows with `POST /api/v2/inventory-allocations/releasable-backorders/release`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/releasable-backorders","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","releasable-backorders"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"count\": 12,\n    \"units\": 106,\n    \"orders\": 6,\n    \"scanned_count\": 12,\n    \"truncated\": false,\n    \"rows\": [\n      {\n        \"id\": 10045,\n        \"sales_order_id\": 9821,\n        \"sales_order_number\": \"SO-9821\",\n        \"sales_order_line_id\": 55347,\n        \"product_id\": 812,\n        \"sku\": \"WIDGET-BLU\",\n        \"product_name\": \"Blue Widget\",\n        \"warehouse_id\": 3,\n        \"warehouse_name\": \"Midpoint\",\n        \"status\": \"planned\",\n        \"backordered_quantity\": 6,\n        \"available_at_warehouse\": 83,\n        \"days_waiting\": 23,\n        \"hold_until_date\": null\n      },\n      {\n        \"id\": 10052,\n        \"sales_order_id\": 9834,\n        \"sales_order_number\": \"SO-9834\",\n        \"sales_order_line_id\": 55412,\n        \"product_id\": 913,\n        \"sku\": \"JUICER-GO\",\n        \"product_name\": \"Countertop Juicer\",\n        \"warehouse_id\": 3,\n        \"warehouse_name\": \"Midpoint\",\n        \"status\": \"awaiting_receipt\",\n        \"backordered_quantity\": 6,\n        \"available_at_warehouse\": 82,\n        \"days_waiting\": 4,\n        \"hold_until_date\": \"2026-08-23T07:00:00+00:00\"\n      }\n    ]\n  }\n}"}]},{"name":"Get Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/summary","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","summary"]},"description":"Returns summary statistics for inventory allocations — total counts by status, total quantities, coverage metrics.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/summary","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"pending_count\": 34,\n    \"released_count\": 12,\n    \"fulfilled_count\": 201,\n    \"cancelled_count\": 5,\n    \"pending_quantity\": 340,\n    \"released_quantity\": 87,\n    \"tight_coverage_count\": 8\n  }\n}"}]},{"name":"Get Breakdown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/breakdown","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","breakdown"]},"description":"Returns a breakdown of allocations grouped by relevant dimensions (warehouse, supplier, status) for dashboard widgets.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/breakdown","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","breakdown"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"by_status\": [\n      {\n        \"status\": \"pending\",\n        \"count\": 34,\n        \"quantity\": 340\n      },\n      {\n        \"status\": \"released\",\n        \"count\": 12,\n        \"quantity\": 87\n      }\n    ],\n    \"by_warehouse\": [\n      {\n        \"warehouse_id\": 3,\n        \"warehouse_name\": \"East Coast DC\",\n        \"count\": 28\n      }\n    ],\n    \"by_supplier\": [\n      {\n        \"supplier_id\": 14,\n        \"supplier_name\": \"Acme Corp\",\n        \"count\": 20\n      }\n    ]\n  }\n}"}]},{"name":"Get Product Analysis","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/product/{productId}/analysis","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","product",":productId","analysis"],"variable":[{"key":"productId","value":"812","description":"Product ID"}]},"description":"Returns allocation analysis for a specific product — current allocations, coverage status, and demand history.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/product/{productId}/analysis","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","product",":productId","analysis"],"variable":[{"key":"productId","value":"812","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product\": {\n      \"id\": 812,\n      \"sku\": \"WIDGET-BLU\",\n      \"name\": \"Blue Widget\"\n    },\n    \"active_allocations\": 3,\n    \"total_allocated_quantity\": 15,\n    \"total_stock\": 20,\n    \"available_after_allocations\": 5,\n    \"tight_coverage\": false\n  }\n}"}]},{"name":"Get Supplier Analysis","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/supplier/{supplierId}/analysis","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","supplier",":supplierId","analysis"],"variable":[{"key":"supplierId","value":"14","description":"Supplier ID"}]},"description":"Returns allocation analysis for a specific supplier — coverage metrics, pending PO quantities, backorder exposure.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/supplier/{supplierId}/analysis","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","supplier",":supplierId","analysis"],"variable":[{"key":"supplierId","value":"14","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"supplier\": {\n      \"id\": 14,\n      \"name\": \"Acme Corp\"\n    },\n    \"pending_backorders\": 8,\n    \"total_backordered_quantity\": 42,\n    \"po_covered_quantity\": 30,\n    \"uncovered_quantity\": 12\n  }\n}"}]},{"name":"List Coverage Options","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/product/{productId}/coverage-options","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","product",":productId","coverage-options"],"variable":[{"key":"productId","value":"812","description":"Product ID"}]},"description":"Lists the open PO lines a product's allocation can be manually covered by — used to populate the 'Change PO Coverage' picker.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns non-draft / non-closed / non-cancelled / non-voided PO lines with unreceived quantity remaining, sorted by earliest resolved ETA first (lines with no resolvable ETA sort last, by creation order). `resolved_eta` is the inbound-shipment expected arrival date, falling back to the PO header's estimated delivery date."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/product/{productId}/coverage-options","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","product",":productId","coverage-options"],"variable":[{"key":"productId","value":"812","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"purchase_order_line_id\": 5567,\n      \"purchase_order_id\": 902,\n      \"purchase_order_number\": \"PO-000902\",\n      \"quantity\": 100,\n      \"received_quantity\": 20,\n      \"unreceived_quantity\": 80,\n      \"resolved_eta\": \"2026-05-28\"\n    },\n    {\n      \"purchase_order_line_id\": 5612,\n      \"purchase_order_id\": 915,\n      \"purchase_order_number\": \"PO-000915\",\n      \"quantity\": 50,\n      \"received_quantity\": 0,\n      \"unreceived_quantity\": 50,\n      \"resolved_eta\": \"2026-06-10\"\n    }\n  ]\n}"}]},{"name":"List Allocation Suppliers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/suppliers","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","suppliers"]},"description":"Returns a list of suppliers that have active inventory allocations — used to populate supplier filter dropdowns.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/suppliers","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","suppliers"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 14,\n      \"name\": \"Acme Corp\"\n    },\n    {\n      \"id\": 22,\n      \"name\": \"Global Supply Ltd\"\n    }\n  ]\n}"}]},{"name":"Search Allocation Suppliers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/search-suppliers?query=acme","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","search-suppliers"],"query":[{"key":"query","value":"acme","description":"Supplier name search string","disabled":true}]},"description":"Searches suppliers by name for autocomplete dropdowns. Returns suppliers that match the query string.\n\n**Required scope: `orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/search-suppliers?query=acme","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","search-suppliers"],"query":[{"key":"query","value":"acme","description":"Supplier name search string","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 14,\n      \"name\": \"Acme Corp\"\n    },\n    {\n      \"id\": 31,\n      \"name\": \"Acme International\"\n    }\n  ]\n}"}]},{"name":"Export Allocations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/export?format=xlsx&scope=filtered&ids=","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","export"],"query":[{"key":"format","value":"xlsx","description":"Export format: xlsx (default)","disabled":true},{"key":"scope","value":"filtered","description":"Export scope: filtered | selected | all","disabled":true},{"key":"ids","value":"","description":"Comma-separated allocation IDs when scope=selected","disabled":true}]},"description":"Downloads an export of inventory allocations.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Query Parameters**:\n- `format` — `xlsx` (default)\n- `scope` — `filtered` (apply current filters), `selected` (specific IDs), `all`\n- `ids` — Required when `scope=selected`. Comma-separated allocation IDs.\n\nReturns a binary file download response."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/export?format=xlsx&scope=filtered&ids=","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","export"],"query":[{"key":"format","value":"xlsx","description":"Export format: xlsx (default)","disabled":true},{"key":"scope","value":"filtered","description":"Export scope: filtered | selected | all","disabled":true},{"key":"ids","value":"","description":"Comma-separated allocation IDs when scope=selected","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary XLSX file)"}]},{"name":"Sync Coverages","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/sync-coverages","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","sync-coverages"]},"description":"Dispatches a background job to sync allocation coverages. Returns a `tracked_job_log_id` for monitoring progress via the tracked job log endpoints.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/sync-coverages","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","sync-coverages"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 4821\n  },\n  \"message\": \"Sync coverages job started.\"\n}"}]},{"name":"Rebalance Coverages","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/rebalance-coverages","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","rebalance-coverages"]},"description":"Dispatches a background job that rebalances allocation coverages — it tears down every non-tight `AwaitingReceipt` coverage and rebuilds it from scratch using the current resolved PO ETAs (inbound-shipment expected arrival, falling back to the PO header's estimated delivery date).\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nManually pinned (tight) coverage is never touched. Returns a `tracked_job_log_id` for monitoring progress via the tracked job log endpoints.\n\nUse this after PO ETAs change so existing PO-line ↔ allocation bindings reflect earliest-ETA-first ordering.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/rebalance-coverages","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","rebalance-coverages"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 4822\n  },\n  \"message\": \"Coverage rebalance job has been queued\"\n}"}]},{"name":"Release Allocations","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/release-allocations","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","release-allocations"]},"description":"Dispatches a background job to release eligible allocations to fulfillment. Returns a `tracked_job_log_id` for monitoring progress via the tracked job log endpoints.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/release-allocations","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","release-allocations"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 4822\n  },\n  \"message\": \"Release allocations job started.\"\n}"}]},{"name":"Reset Priorities","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/reset-priorities","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","reset-priorities"]},"description":"Dispatches a background job to reset allocation priorities using the specified method.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Body**:\n- `method` (required) — Priority reset method: `fifo` | `lifo` | custom\n- `overwrite_tight_coverages` (boolean) — Whether to reassign tight coverage allocations\n- `resync_coverages` (boolean) — Whether to resync coverages after reset\n\nReturns a `tracked_job_log_id`.","body":{"mode":"raw","raw":"{\n  \"method\": \"fifo\",\n  \"overwrite_tight_coverages\": false,\n  \"resync_coverages\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/reset-priorities","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","reset-priorities"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 4823\n  },\n  \"message\": \"Reset priorities job started.\"\n}"}]},{"name":"Compact Priorities","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/compact-priorities","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","compact-priorities"]},"description":"Dispatches a background job to compact allocation priority numbers — fills in gaps from deleted/cancelled allocations so priorities are sequential. Returns a `tracked_job_log_id`.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/compact-priorities","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","compact-priorities"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 4825\n  },\n  \"message\": \"Compact priorities job started.\"\n}"}]},{"name":"Evaluate Releases","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/evaluate-releases","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","evaluate-releases"]},"description":"Dispatches a background job to evaluate which allocations are ready for release.\n\n**Body**:\n- `dispatch_fulfillments` (boolean) — If `true`, automatically dispatch fulfillments for ready allocations. If `false`, only evaluate and mark.\n\nReturns a `tracked_job_log_id`.","body":{"mode":"raw","raw":"{\n  \"dispatch_fulfillments\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/evaluate-releases","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","evaluate-releases"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 4824\n  },\n  \"message\": \"Evaluate releases job started.\"\n}"}]},{"name":"Check Release Availability","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/check-release-availability","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","check-release-availability"]},"description":"Checks whether the specified allocations have sufficient stock to be released. Returns availability status per allocation.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Body**:\n- `allocation_ids` — Array of allocation IDs to check","body":{"mode":"raw","raw":"{\n  \"allocation_ids\": [\n    10045,\n    10046,\n    10047\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/check-release-availability","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","check-release-availability"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"allocation_id\": 10045,\n      \"available\": true,\n      \"stock\": 20,\n      \"required\": 5\n    },\n    {\n      \"allocation_id\": 10046,\n      \"available\": false,\n      \"stock\": 2,\n      \"required\": 10\n    },\n    {\n      \"allocation_id\": 10047,\n      \"available\": true,\n      \"stock\": 8,\n      \"required\": 3\n    }\n  ]\n}"}]},{"name":"Release Selected Backorders","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/release-selected","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","release-selected"]},"description":"Immediately releases the specified allocations to fulfillment.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Body**:\n- `ids` — Array of allocation IDs to release","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    10045,\n    10047\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/release-selected","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","release-selected"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Released 2 allocation(s), skipped 0.\",\n  \"data\": {\n    \"released\": 2,\n    \"skipped\": 0\n  }\n}"}]},{"name":"Submit Fulfillments","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/submit-fulfillments","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","submit-fulfillments"]},"description":"Submits fulfillments for the specified released allocations.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Body**:\n- `allocation_ids` — Array of allocation IDs to submit fulfillments for\n\nApplies to sales-order backorders only. Including a manufacturing-order component allocation id returns a 422 validation error.","body":{"mode":"raw","raw":"{\n  \"allocation_ids\": [\n    10045,\n    10047\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/submit-fulfillments","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","submit-fulfillments"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Fulfillments submitted for 2 allocations.\"\n}"}]},{"name":"Delete Fulfillments","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/delete-fulfillments","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","delete-fulfillments"]},"description":"Deletes pending fulfillments for the specified allocations, reverting them to released status.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Body**:\n- `allocation_ids` — Array of allocation IDs whose fulfillments should be deleted\n\nApplies to sales-order backorders only. Including a manufacturing-order component allocation id returns a 422 validation error.","body":{"mode":"raw","raw":"{\n  \"allocation_ids\": [\n    10045\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/delete-fulfillments","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","delete-fulfillments"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Fulfillments deleted for 1 allocation.\"\n}"}]},{"name":"Unreserve Allocations","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/unreserve","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","unreserve"]},"description":"Cancels (unreserves) the specified allocations.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Body**:\n- `allocation_ids` — Array of allocation IDs to cancel\n- `disable_auto_release` (boolean) — If `true`, disables automatic re-release for the linked source document after cancellation\n\nApplies to sales-order backorders only. Including a manufacturing-order component allocation id returns a 422 validation error.","body":{"mode":"raw","raw":"{\n  \"allocation_ids\": [\n    10045,\n    10046\n  ],\n  \"disable_auto_release\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/unreserve","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","unreserve"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"2 allocations cancelled.\"\n}"}]},{"name":"Switch Supplier Options","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/switch-supplier-options","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","switch-supplier-options"]},"description":"Returns the per-allocation supplier picker payload used by the Allocation Pipeline Change Supplier dialog. Only allocations in `planned` or `awaiting_receipt` status are eligible. For each eligible allocation, returns the list of suppliers configured for that product (via `supplier_products`), excluding archived suppliers.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Body**:\n- `ids` (required, array, min 1) — Array of inventory allocation IDs.\n- `ids.*` (integer) — Each ID must be an integer.\n\n**Notes**:\n- Allocations whose status is not `planned` or `awaiting_receipt` are silently filtered out (they are not eligible for supplier change).\n- Each row's `suppliers` list is sorted with the default supplier first, then alphabetically by name.\n- Use the returned `current_supplier.id` to highlight the existing selection in the UI.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    10045,\n    10046\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/switch-supplier-options","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","switch-supplier-options"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 10045,\n      \"product_id\": 8821,\n      \"sku\": \"SPN-STD-SPIRAL-BLK\",\n      \"product_name\": \"Spiral Notebook - Black\",\n      \"current_supplier\": {\n        \"id\": 14,\n        \"name\": \"HomeLife\"\n      },\n      \"suppliers\": [\n        {\n          \"id\": 14,\n          \"name\": \"HomeLife\",\n          \"is_default\": true\n        },\n        {\n          \"id\": 27,\n          \"name\": \"A Bar Above\",\n          \"is_default\": false\n        }\n      ]\n    },\n    {\n      \"id\": 10046,\n      \"product_id\": 9102,\n      \"sku\": \"WGT-001\",\n      \"product_name\": \"Widget 001\",\n      \"current_supplier\": null,\n      \"suppliers\": [\n        {\n          \"id\": 31,\n          \"name\": \"Acme Supply Co.\",\n          \"is_default\": true\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"Switch Suppliers","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/switch-suppliers","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","switch-suppliers"]},"description":"Bulk updates the supplier on one or more inventory allocations. Each assignment is validated against `supplier_products` so the chosen supplier must already be configured for the allocation's product. Allocations not in `planned` or `awaiting_receipt` status are skipped.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Body**:\n- `assignments` (required, array, min 1) — List of allocation/supplier pairs to apply.\n- `assignments.*.allocation_id` (required, integer, exists in `inventory_allocations`) — ID of the allocation to update.\n- `assignments.*.supplier_id` (required, integer, exists in `suppliers`) — ID of the supplier to assign.\n\n**Behavior**:\n- Assignments where the new supplier matches the current supplier are counted as `skipped` (no-op).\n- Assignments referencing an allocation that is no longer in `planned`/`awaiting_receipt` are counted as `skipped`.\n- Assignments where the supplier is not configured for the product return per-row errors and do NOT update the allocation.\n- The endpoint is best-effort per row; partial success is normal (e.g., 3 updated, 1 skipped, 1 error).","body":{"mode":"raw","raw":"{\n  \"assignments\": [\n    {\n      \"allocation_id\": 10045,\n      \"supplier_id\": 27\n    },\n    {\n      \"allocation_id\": 10046,\n      \"supplier_id\": 31\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/switch-suppliers","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","switch-suppliers"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Updated supplier on 2 allocation(s).\",\n  \"data\": {\n    \"updated\": 2,\n    \"skipped\": 0,\n    \"errors\": []\n  }\n}"}]},{"name":"Release Backorders and Dispatch Fulfillment","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/releasable-backorders/release","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","releasable-backorders","release"]},"description":"Releases the given backorders against on-hand stock and dispatches fulfillment for their orders in one call.\n\n**Required scope: `orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach allocation moves from `planned` / `awaiting_receipt` to `allocated`, claiming the stock, and a fulfillment is then created for every affected sales order — matching what happens automatically when stock arrives.\n\nAvailability is re-checked under a pool lock at release time, so a backorder whose stock was claimed by another operation since the list was fetched is reported in `skipped` rather than over-allocated. Fulfillment is only dispatched when at least one backorder was released.\n\nPair with `GET /api/v2/inventory-allocations/releasable-backorders` to obtain eligible ids. To release without dispatching fulfillment, use `POST /api/v2/inventory-allocations/release-selected` instead.\n\n**Body**:\n- `ids` — Array of allocation IDs to release (required, at least one integer)","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    10045,\n    10052\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/inventory-allocations/releasable-backorders/release","host":["{{baseUrl}}"],"path":["api","v2","inventory-allocations","releasable-backorders","release"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Released 2 backorder(s); fulfillment is being created for 2 order(s).\",\n  \"data\": {\n    \"released\": 2,\n    \"skipped\": 0,\n    \"dispatched\": 2\n  }\n}"}]},{"name":"Create Warehouse Transfer","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers","host":["{{baseUrl}}"],"path":["api","warehouses","transfers"]},"description":"Creates a warehouse transfer (in draft status) moving stock between two warehouses.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: from_warehouse_id and to_warehouse_id (required, must differ), transfer_date, eta (must not be before transfer_date), products (array of {product_id, quantity, quantity_cancelled, description, sales_channel_unique_id} - product_id is required on every line and must be an existing product, unless the line carries a sales_channel_unique_id), shipping_method_id, shipment (object with shipment_date, tracking_number, cost - creates an initial shipment), shipment_id (required when the destination is an Amazon FBA warehouse - the Amazon inbound shipment ID), order_id and custom_field_values (array of {custom_field_id, value}).\n\nReturns 400 when a product does not have enough available stock in the source warehouse.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"transfer_date\": \"2026-06-01\",\n  \"eta\": \"2026-06-15\",\n  \"from_warehouse_id\": 2,\n  \"to_warehouse_id\": 5,\n  \"shipping_method_id\": 3,\n  \"products\": [\n    {\n      \"product_id\": 101,\n      \"quantity\": 100\n    },\n    {\n      \"product_id\": 102,\n      \"quantity\": 50\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/warehouses/transfers","host":["{{baseUrl}}"],"path":["api","warehouses","transfers"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse transfer created successfully.\",\n  \"data\": {\n    \"id\": 512,\n    \"transfer_type\": \"standard\",\n    \"warehouse_transfer_number\": \"WT-000512\",\n    \"transfer_date\": \"2026-06-01T00:00:00.000000Z\",\n    \"from_warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"type\": \"direct\",\n      \"integration_instance_id\": null\n    },\n    \"to_warehouse\": {\n      \"id\": 5,\n      \"name\": \"West Coast 3PL\",\n      \"type\": \"3pl\"\n    },\n    \"status\": \"open\",\n    \"shipment_status\": \"unshipped\",\n    \"shipment_id\": null,\n    \"receipt_status\": \"unreceived\",\n    \"fully_shipped_at\": null,\n    \"fully_received_at\": null,\n    \"eta\": \"2026-06-15\",\n    \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"updated_at\": \"2026-06-02T08:30:00.000000Z\",\n    \"archived_at\": null,\n    \"line_item_count\": 1,\n    \"first_line_sku\": \"WIDGET-001\",\n    \"asn_last_sent_at\": null\n  }\n}"}]},{"name":"Bulk Delete Warehouse Transfers","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers","host":["{{baseUrl}}"],"path":["api","warehouses","transfers"]},"description":"Deletes multiple warehouse transfers, reversing their inventory movements. Provide either ids (array of transfer IDs) or apply_to_all=true with an optional filters object.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns 400 when received stock from a transfer has already been consumed downstream.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/warehouses/transfers","host":["{{baseUrl}}"],"path":["api","warehouses","transfers"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"totalCount\": 2,\n    \"failCount\": 0\n  },\n  \"message\": \"2 records processed successfully.\"\n}"}]},{"name":"Update Warehouse Transfer","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]},"description":"Updates a warehouse transfer. Accepts the same fields as Create Warehouse Transfer (all optional) plus tags (array of tag names to sync). The products array replaces the transfer's line items; include existing line ids to keep them. A line without an id is a new line and must include an existing product_id (unless it carries a sales_channel_unique_id); a line sent with its id keeps its stored product.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nTransfers to an Amazon FBA warehouse can only be edited while in draft status. Returns 422 when editing a line that has already been shipped or when stock is insufficient.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"eta\": \"2026-06-20\",\n  \"products\": [\n    {\n      \"id\": 9001,\n      \"product_id\": 101,\n      \"quantity\": 120\n    }\n  ],\n  \"tags\": [\n    \"restock\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse transfer updated successfully.\",\n  \"data\": {\n    \"id\": 512,\n    \"transfer_type\": \"standard\",\n    \"warehouse_transfer_number\": \"WT-000512\",\n    \"transfer_date\": \"2026-06-01T00:00:00.000000Z\",\n    \"from_warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"type\": \"direct\",\n      \"integration_instance_id\": null\n    },\n    \"to_warehouse\": {\n      \"id\": 5,\n      \"name\": \"West Coast 3PL\",\n      \"type\": \"3pl\"\n    },\n    \"status\": \"open\",\n    \"shipment_status\": \"unshipped\",\n    \"shipment_id\": null,\n    \"receipt_status\": \"unreceived\",\n    \"fully_shipped_at\": null,\n    \"fully_received_at\": null,\n    \"eta\": \"2026-06-15\",\n    \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"updated_at\": \"2026-06-02T08:30:00.000000Z\",\n    \"archived_at\": null,\n    \"line_item_count\": 1,\n    \"first_line_sku\": \"WIDGET-001\",\n    \"asn_last_sent_at\": null,\n    \"shipment_percent\": 0,\n    \"receipt_percent\": 0,\n    \"items\": [\n      {\n        \"id\": 9001,\n        \"quantity\": 100,\n        \"product_id\": 101,\n        \"product\": {\n          \"id\": 101,\n          \"sku\": \"WIDGET-001\",\n          \"name\": \"Widget\",\n          \"is_lot_tracked\": false,\n          \"lot_tracking_method\": null,\n          \"shelf_life_days\": null,\n          \"near_expiry_threshold_days\": null,\n          \"min_remaining_shelf_life_days\": null\n        },\n        \"sku\": \"WIDGET-001\",\n        \"barcode\": 123456789012,\n        \"sales_channel_unique_id\": null,\n        \"name\": \"Widget\",\n        \"image_url\": null,\n        \"quantity_cancelled\": 0,\n        \"quantity_received\": 0,\n        \"quantity_shipped\": 0,\n        \"quantity_receivable\": 0,\n        \"quantity_in_transit\": 0,\n        \"quantity_unshipped\": 100\n      }\n    ],\n    \"tags\": [\n      \"restock\"\n    ],\n    \"total_quantity\": 100\n  }\n}"}]},{"name":"Delete Warehouse Transfer","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]},"description":"Deletes a warehouse transfer and reverses its inventory movements. Deletion fails when received stock from this transfer has already been consumed downstream (sold, transferred again, or adjusted).\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse transfer 512 deleted successfully.\"\n}"}]},{"name":"Duplicate Warehouse Transfer","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/duplicate","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","duplicate"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID to duplicate"}]},"description":"Creates a new draft warehouse transfer copying the source transfer's warehouses and line items.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/duplicate","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","duplicate"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID to duplicate"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse transfer created successfully.\",\n  \"data\": {\n    \"id\": 512,\n    \"transfer_type\": \"standard\",\n    \"warehouse_transfer_number\": \"WT-000512\",\n    \"transfer_date\": \"2026-06-01T00:00:00.000000Z\",\n    \"from_warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"type\": \"direct\",\n      \"integration_instance_id\": null\n    },\n    \"to_warehouse\": {\n      \"id\": 5,\n      \"name\": \"West Coast 3PL\",\n      \"type\": \"3pl\"\n    },\n    \"status\": \"open\",\n    \"shipment_status\": \"unshipped\",\n    \"shipment_id\": null,\n    \"receipt_status\": \"unreceived\",\n    \"fully_shipped_at\": null,\n    \"fully_received_at\": null,\n    \"eta\": \"2026-06-15\",\n    \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"updated_at\": \"2026-06-02T08:30:00.000000Z\",\n    \"archived_at\": null,\n    \"line_item_count\": 1,\n    \"first_line_sku\": \"WIDGET-001\",\n    \"asn_last_sent_at\": null\n  }\n}"}]},{"name":"List Warehouse Transfers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers?page=1&per_page=10&filter[search]=&filter[archived]=&filter[transfer_type]=&filter[has_blemished]=&filter[has_inbound_plan]=&filter[include_deleted]=&filter_groups[conjunction]=and&sort=-transfer_date","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"filter[search]","value":"","description":"Cross-column search: id, transfer_number, from_warehouse name, to_warehouse name, item SKU, item FNSKU","disabled":true},{"key":"filter[archived]","value":"","description":"true | false — filter archived transfers","disabled":true},{"key":"filter[transfer_type]","value":"","description":"Text filter on transfer_type: standard | fba_inbound | awd","disabled":true},{"key":"filter[has_blemished]","value":"","description":"Text filter — true | false","disabled":true},{"key":"filter[has_inbound_plan]","value":"","description":"Text filter — true | false (FBA inbound plan link)","disabled":true},{"key":"filter[include_deleted]","value":"","description":"true — include soft-deleted transfers","disabled":true},{"key":"filter_groups[conjunction]","value":"and","description":"Advanced filter tree conjunction: and | or. Use with filter_groups[children][] for grouped conditions.","disabled":true},{"key":"sort","value":"-transfer_date","description":"Sort field. Prefix with - for descending. Options: id, warehouse_transfer_number, transfer_date, transfer_status, shipment_status, receipt_status, created_at, updated_at, eta, archived_at, asn_last_sent_at, status, from_warehouse_name, to_warehouse_name, from_warehouse, to_warehouse. Default: -transfer_date","disabled":true}]},"description":"Returns a paginated list of warehouse transfers.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Supports GET and POST** — POST can be used to send large filter payloads in the request body.\n\n**Filters** (via Spatie QueryBuilder + GroupedFilterSupport):\n- `filter[search]` — Cross-column search on id, transfer_number, from/to warehouse name, item SKU, item FNSKU\n- `filter[archived]` — Boolean (true|false) — archived transfers\n- `filter[transfer_type]` — Transfer type text filter: `standard`, `fba_inbound`, `awd`\n- `filter[has_blemished]` — Boolean text filter\n- `filter[has_inbound_plan]` — Boolean text filter (FBA)\n- `filter[include_deleted]` — Include soft-deleted records\n- `filter_groups` — Tree-based advanced filters (GroupedFilterSupport)\n\n**Sort options**: id, warehouse_transfer_number, transfer_date, transfer_status, shipment_status, receipt_status, created_at, updated_at, eta, archived_at, asn_last_sent_at, status, from_warehouse_name, to_warehouse_name, from_warehouse, to_warehouse. Default: `-transfer_date`.\n\nThe response also includes `unfiltered_total`, the count of rows in the base scope before any user filters are applied — used by the frontend to show export totals."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers?page=1&per_page=10&filter[search]=&filter[archived]=&filter[transfer_type]=&filter[has_blemished]=&filter[has_inbound_plan]=&filter[include_deleted]=&filter_groups[conjunction]=and&sort=-transfer_date","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"10","description":"Results per page (default 10).","disabled":true},{"key":"filter[search]","value":"","description":"Cross-column search: id, transfer_number, from_warehouse name, to_warehouse name, item SKU, item FNSKU","disabled":true},{"key":"filter[archived]","value":"","description":"true | false — filter archived transfers","disabled":true},{"key":"filter[transfer_type]","value":"","description":"Text filter on transfer_type: standard | fba_inbound | awd","disabled":true},{"key":"filter[has_blemished]","value":"","description":"Text filter — true | false","disabled":true},{"key":"filter[has_inbound_plan]","value":"","description":"Text filter — true | false (FBA inbound plan link)","disabled":true},{"key":"filter[include_deleted]","value":"","description":"true — include soft-deleted transfers","disabled":true},{"key":"filter_groups[conjunction]","value":"and","description":"Advanced filter tree conjunction: and | or. Use with filter_groups[children][] for grouped conditions.","disabled":true},{"key":"sort","value":"-transfer_date","description":"Sort field. Prefix with - for descending. Options: id, warehouse_transfer_number, transfer_date, transfer_status, shipment_status, receipt_status, created_at, updated_at, eta, archived_at, asn_last_sent_at, status, from_warehouse_name, to_warehouse_name, from_warehouse, to_warehouse. Default: -transfer_date","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 501,\n      \"transfer_type\": \"standard\",\n      \"warehouse_transfer_number\": \"WT-501\",\n      \"transfer_date\": \"2025-04-10\",\n      \"from_warehouse\": {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      },\n      \"to_warehouse\": {\n        \"id\": 3,\n        \"name\": \"East Coast DC\"\n      },\n      \"status\": \"in_transit\",\n      \"shipment_status\": \"shipped\",\n      \"shipment_percent\": 100,\n      \"receipt_status\": \"partial\",\n      \"receipt_percent\": 60,\n      \"shipping_method\": \"Ground\",\n      \"fully_shipped_at\": \"2025-04-11T14:00:00.000000Z\",\n      \"fully_received_at\": null,\n      \"items\": 3,\n      \"eta\": \"2025-04-15\",\n      \"created_at\": \"2025-04-10T08:00:00.000000Z\",\n      \"updated_at\": \"2025-04-11T14:05:00.000000Z\",\n      \"archived_at\": null,\n      \"tags\": [],\n      \"line_item_count\": 3,\n      \"total_quantity\": 150,\n      \"notes\": null,\n      \"adjustments\": [],\n      \"shipments\": [],\n      \"financial_lines\": []\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 8,\n  \"per_page\": 10,\n  \"total\": 78,\n  \"unfiltered_total\": 412,\n  \"from\": 1,\n  \"to\": 10\n}"}]},{"name":"Search Warehouse Transfers","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers"]},"description":"Returns the same paginated list of warehouse transfers as the GET version of this endpoint, but accepts filters as a JSON request body instead of the query string. Use this POST variant when the filter set is too large or too complex to fit in a URL. The response includes an `unfiltered_total` alongside the standard paginator totals so clients can show both the filtered and total record counts.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nProvide advanced filters under `filter_groups`, a tree of conditions combined with a `conjunction` of `and` or `or`. Each condition names a `column`, an `operator`, and a `value`. A free-text `search` term matches against transfer id, transfer number, source and destination warehouses, item SKU, and item FNSKU.\n\nText and status columns (`transfer_status`, `shipment_status`, `receipt_status`, `from_warehouse`, `to_warehouse`) accept: is, is_not, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, is_one_of, is_not_one_of.\nNumeric and date columns (`id`, `transfer_date`, `created_at`, `updated_at`) accept: is, is_not, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between, is_empty, is_not_empty. The `transfer_type` filter accepts `standard`, `fba_inbound`, `awd_inbound`, or `awd_fba_replenishment`; `has_blemished`, `has_inbound_plan`, `archived`, and `include_deleted` are boolean toggles. Custom field filters are also supported.\n\nSortable columns (via `sort`, prefix with `-` for descending): id, warehouse_transfer_number, transfer_date, transfer_status, shipment_status, receipt_status, created_at, updated_at, eta, archived_at, asn_last_sent_at, from_warehouse_name, to_warehouse_name. Results default to `-transfer_date`.","body":{"mode":"raw","raw":"{\n  \"per_page\": 25,\n  \"page\": 1,\n  \"sort\": \"-transfer_date\",\n  \"filter_groups\": {\n    \"conjunction\": \"and\",\n    \"children\": [\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"transfer_type\",\n          \"operator\": \"is\",\n          \"value\": \"fba_inbound\"\n        }\n      },\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"shipment_status\",\n          \"operator\": \"is_not\",\n          \"value\": \"shipped\"\n        }\n      }\n    ]\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3021,\n      \"transfer_type\": \"fba_inbound\",\n      \"warehouse_transfer_number\": \"WT-3021\",\n      \"transfer_date\": \"2026-06-27T00:00:00.000000Z\",\n      \"from_warehouse\": {\n        \"id\": 2,\n        \"name\": \"Main Warehouse\",\n        \"type\": \"standard\",\n        \"integration_instance_id\": null\n      },\n      \"to_warehouse\": {\n        \"id\": 9,\n        \"name\": \"Amazon FBA - US\",\n        \"type\": \"amazon_fba\"\n      },\n      \"status\": \"in_progress\",\n      \"shipment_status\": \"partially_shipped\",\n      \"shipment_id\": 1201,\n      \"receipt_status\": \"pending\",\n      \"fully_shipped_at\": null,\n      \"fully_received_at\": null,\n      \"eta\": \"2026-07-05\",\n      \"created_at\": \"2026-06-27T13:00:00.000000Z\",\n      \"updated_at\": \"2026-06-28T08:22:00.000000Z\",\n      \"archived_at\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"per_page\": 25,\n  \"from\": 1,\n  \"to\": 1,\n  \"last_page\": 5,\n  \"total\": 118,\n  \"unfiltered_total\": 342,\n  \"next_page_url\": \"https://api.example.com/api/v2/warehouse-transfers?page=2\",\n  \"prev_page_url\": null\n}"}]},{"name":"Get Warehouse Transfer","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer"],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]},"description":"Returns the full detail view of a warehouse transfer.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEager-loads: from_warehouse, to_warehouse, tags, adjustments, shipments (with lines and receipts), financial_lines, and COGS data. Also preloads current stock levels for both warehouses.\n\nFor transfers with an Amazon FBA destination, the response includes back-links to the Amazon records the transfer materializes: `amazon_inbound_shipment` (the FBA inbound shipment) and `amazon_inbound_plan` (the FBA inbound plan it was built from). Each carries an `integration_instance_id`, which identifies the Amazon account the record belongs to. Both are omitted when the transfer has no Amazon destination.\n\n`amazon_inbound_shipment.is_legacy` tells you which shipment detail endpoint to call for it: `true` means the record is a legacy FBA inbound shipment, retrievable at `GET /api/amazon/{integrationInstance}/inbound/shipments/{shipment}`; `false` means it is a current-format shipment, retrievable at `GET /api/amazon/{integrationInstance}/new-inbound/shipments/{shipment}`. The two record sets use independent id sequences, so the same id can exist in both — always branch on this flag rather than assuming a format.\n\nLanded-cost invoices attached to the transfer are returned under `bills`. A transfer is always denominated in the tenant's base currency, so each bill carries both the supplier's own figures and the same figures restated in that base currency: `total_amount` and `allocations[].allocation_amount` are in the bill's currency (`currency_code`, converted at `currency_rate`), while `total_amount_in_tenant_currency` and `allocations[].allocation_amount_in_tenant_currency` are in the base currency. Only the tenant-currency figures may be summed into transfer totals — adding the native amounts mixes currencies. `is_foreign_currency` is true when the invoice was raised in something other than the base currency.\n\nThe same applies to each line's `cogs` block: `cogs.landed_costs_total` and `cogs.landed_costs[].amount` are reported in the base currency, matching what the FIFO layer and the accounting COGS book."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer"],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 501,\n  \"transfer_type\": \"standard\",\n  \"warehouse_transfer_number\": \"WT-501\",\n  \"transfer_date\": \"2025-04-10\",\n  \"from_warehouse\": {\n    \"id\": 1,\n    \"name\": \"Main Warehouse\"\n  },\n  \"to_warehouse\": {\n    \"id\": 3,\n    \"name\": \"East Coast DC\"\n  },\n  \"status\": \"in_transit\",\n  \"shipment_status\": \"shipped\",\n  \"shipment_percent\": 100,\n  \"receipt_status\": \"partial\",\n  \"receipt_percent\": 60,\n  \"shipping_method\": \"Ground\",\n  \"amazon_inbound_shipment\": {\n    \"id\": 672,\n    \"integration_instance_id\": 3,\n    \"is_legacy\": false,\n    \"name\": \"FBA STA (05/21/2026 13:38)-PSC2\",\n    \"shipmentId\": \"sh1234abcd\",\n    \"shipmentConfirmationId\": \"FBA19DQL5NR1\",\n    \"status\": \"CLOSED\",\n    \"destinationWarehouse\": \"PSC2\",\n    \"destinationAddress\": {\n      \"city\": \"Aurora\",\n      \"stateOrProvinceCode\": \"IL\"\n    }\n  },\n  \"amazon_inbound_plan\": {\n    \"id\": 28,\n    \"name\": \"2026/07/02 13:43 (Resubmit)\",\n    \"status\": \"SHIPPED\",\n    \"integration_instance_id\": 3\n  },\n  \"fully_shipped_at\": \"2025-04-11T14:00:00.000000Z\",\n  \"fully_received_at\": null,\n  \"items\": [\n    {\n      \"id\": 87727,\n      \"product_id\": 4821,\n      \"sku\": \"BKG-004\",\n      \"quantity\": 160,\n      \"quantity_received\": 160,\n      \"cogs\": {\n        \"base_unit_cost\": 14.24,\n        \"cost_lines_total\": 0,\n        \"cost_lines\": [],\n        \"landed_costs_total\": 827.67,\n        \"landed_costs\": [\n          {\n            \"name\": \"Freightos\",\n            \"invoice_number\": \"S260716000370\",\n            \"amount\": 681.36\n          },\n          {\n            \"name\": \"Clearit\",\n            \"invoice_number\": \"0013993801-01\",\n            \"amount\": 146.31\n          }\n        ],\n        \"unallocated_total\": 0,\n        \"unallocated\": [],\n        \"total_cost\": 3106.07,\n        \"unit_cost\": 19.4129,\n        \"is_finalized\": true\n      }\n    }\n  ],\n  \"eta\": \"2025-04-15\",\n  \"created_at\": \"2025-04-10T08:00:00.000000Z\",\n  \"updated_at\": \"2025-04-11T14:05:00.000000Z\",\n  \"archived_at\": null,\n  \"tags\": [\n    {\n      \"id\": 5,\n      \"name\": \"urgent\"\n    }\n  ],\n  \"line_item_count\": 1,\n  \"total_quantity\": 150,\n  \"notes\": \"Rush transfer for East Coast restock\",\n  \"adjustments\": [],\n  \"shipments\": [\n    {\n      \"id\": 201,\n      \"shipped_at\": \"2025-04-11T14:00:00.000000Z\",\n      \"tracking_number\": \"1Z999AA1234567890\",\n      \"line_count\": 3,\n      \"receipts\": []\n    }\n  ],\n  \"financial_lines\": [],\n  \"bills\": [\n    {\n      \"id\": 484,\n      \"supplier_id\": 92,\n      \"supplier_name\": \"Freightos\",\n      \"invoice_number\": \"S260716000370\",\n      \"invoice_date\": \"2026-09-01\",\n      \"proration_strategy\": \"quantity_based\",\n      \"descriptions\": [\n        \"freight\"\n      ],\n      \"total_amount\": 1328.65,\n      \"total_amount_in_tenant_currency\": 1328.65,\n      \"currency_code\": \"USD\",\n      \"currency_rate\": 1,\n      \"is_foreign_currency\": false,\n      \"allocations\": [\n        {\n          \"id\": 1476,\n          \"allocation_amount\": 681.359,\n          \"allocation_amount_in_tenant_currency\": 681.359,\n          \"allocation_percentage\": 51.28,\n          \"proration_strategy\": \"quantity_based\"\n        }\n      ],\n      \"created_at\": \"2026-09-01T12:08:09.000000Z\",\n      \"updated_at\": \"2026-09-01T12:08:09.000000Z\"\n    },\n    {\n      \"id\": 485,\n      \"supplier_id\": 93,\n      \"supplier_name\": \"Clearit\",\n      \"invoice_number\": \"0013993801-01\",\n      \"invoice_date\": \"2026-09-01\",\n      \"proration_strategy\": \"quantity_based\",\n      \"descriptions\": [\n        \"import duty\"\n      ],\n      \"total_amount\": 396.96,\n      \"total_amount_in_tenant_currency\": 285.31,\n      \"currency_code\": \"CAD\",\n      \"currency_rate\": 0.7187264168,\n      \"is_foreign_currency\": true,\n      \"allocations\": [\n        {\n          \"id\": 1478,\n          \"allocation_amount\": 203.5692,\n          \"allocation_amount_in_tenant_currency\": 146.3106,\n          \"allocation_percentage\": 51.28,\n          \"proration_strategy\": \"quantity_based\"\n        }\n      ],\n      \"created_at\": \"2026-09-01T12:09:14.000000Z\",\n      \"updated_at\": \"2026-09-01T12:09:14.000000Z\"\n    }\n  ]\n}"}]},{"name":"Get Tab Counts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}/tab-counts","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer","tab-counts"],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]},"description":"Returns badge counts for all detail page tabs in a single request — line items, shipments, receipts, inventory movements, FIFO layers, allocations, activity log. Used to populate tab badges without loading each tab's full data.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}/tab-counts","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer","tab-counts"],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"line_items\": 3,\n    \"shipments\": 1,\n    \"receipts\": 1,\n    \"inventory_movements\": 45,\n    \"fifo_layers\": 3,\n    \"allocations\": 2,\n    \"activity_log\": 12\n  }\n}"}]},{"name":"Browse Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}/browse-products?query=widget&in_stock_only=true&limit=25","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer","browse-products"],"query":[{"key":"query","value":"widget","description":"Product search string (name or SKU)","disabled":true},{"key":"in_stock_only","value":"true","description":"true — only return products with available stock in the from_warehouse","disabled":true},{"key":"limit","value":"25","description":"Maximum number of results (default: 25)","disabled":true}],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]},"description":"Searches products that can be added to this warehouse transfer. Scoped to the transfer's `from_warehouse`.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Query Parameters**:\n- `query` — Product name or SKU search\n- `in_stock_only` — Restrict to products with stock in the source warehouse\n- `limit` — Max results (default: 25)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}/browse-products?query=widget&in_stock_only=true&limit=25","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer","browse-products"],"query":[{"key":"query","value":"widget","description":"Product search string (name or SKU)","disabled":true},{"key":"in_stock_only","value":"true","description":"true — only return products with available stock in the from_warehouse","disabled":true},{"key":"limit","value":"25","description":"Maximum number of results (default: 25)","disabled":true}],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 812,\n      \"sku\": \"WIDGET-BLU\",\n      \"name\": \"Blue Widget\",\n      \"stock\": 45,\n      \"available_stock\": 38\n    },\n    {\n      \"id\": 813,\n      \"sku\": \"WIDGET-RED\",\n      \"name\": \"Red Widget\",\n      \"stock\": 20,\n      \"available_stock\": 20\n    }\n  ]\n}"}]},{"name":"Get Line Items","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}/line-items","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer","line-items"],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]},"description":"Returns the line items for a warehouse transfer, including product details, quantities, shipped/received quantities, and COGS values.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}/line-items","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer","line-items"],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3001,\n      \"product_id\": 812,\n      \"product\": {\n        \"id\": 812,\n        \"sku\": \"WIDGET-BLU\",\n        \"name\": \"Blue Widget\"\n      },\n      \"quantity\": 50,\n      \"shipped_quantity\": 50,\n      \"received_quantity\": 30,\n      \"unit_cost\": \"12.50\",\n      \"total_cost\": \"625.00\",\n      \"notes\": null\n    },\n    {\n      \"id\": 3002,\n      \"product_id\": 813,\n      \"product\": {\n        \"id\": 813,\n        \"sku\": \"WIDGET-RED\",\n        \"name\": \"Red Widget\"\n      },\n      \"quantity\": 100,\n      \"shipped_quantity\": 100,\n      \"received_quantity\": 60,\n      \"unit_cost\": \"9.75\",\n      \"total_cost\": \"975.00\",\n      \"notes\": null\n    }\n  ]\n}"}]},{"name":"Get Inventory Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}/inventory-movements?page=1&per_page=25","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer","inventory-movements"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"25","description":"Results per page (default 10).","disabled":true}],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]},"description":"Returns paginated inventory movements generated by this warehouse transfer (both ship-out and receive-in movements).\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nCost fields on each movement: `unit_cost` is the FIFO layer's current average cost (falling back to the movement's own cost when it has no layer) and keeps that meaning for compatibility. `recorded_unit_cost` is the unit cost the movement was recorded at, the figure its cost-of-goods journal booked (null when unknown). `layer_unit_cost` is the FIFO layer's current per-unit cost after any later revaluation or landed cost (null when the movement has no layer). The two differ when a layer is revalued after the movement, for example by a supplier price true-up or a freight bill."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}/inventory-movements?page=1&per_page=25","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer","inventory-movements"],"query":[{"key":"page","value":"1","description":"Page number (default 1).","disabled":true},{"key":"per_page","value":"25","description":"Results per page (default 10).","disabled":true}],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 88001,\n      \"type\": \"warehouse_transfer_ship\",\n      \"inventory_status\": \"active\",\n      \"quantity\": -50,\n      \"reference\": \"WT-501\",\n      \"link_type\": \"App\\\\Models\\\\WarehouseTransferShipmentLine\",\n      \"link_id\": 7101,\n      \"parent_link_id\": 3301,\n      \"detail_link_id\": null,\n      \"product_id\": 812,\n      \"product_sku\": \"WIDGET-BLU\",\n      \"product_name\": \"Blue Widget\",\n      \"warehouse_id\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"unit_cost\": 12.9,\n      \"recorded_unit_cost\": 12.5,\n      \"layer_unit_cost\": 12.9,\n      \"unit_cost_provisional\": false,\n      \"layer\": {\n        \"fifo_layer_id\": 4410,\n        \"name\": \"fifo\"\n      },\n      \"inventory_movement_date\": \"2025-04-11T14:00:00.000000Z\",\n      \"created_at\": \"2025-04-11T14:00:00.000000Z\"\n    },\n    {\n      \"id\": 88002,\n      \"type\": \"warehouse_transfer_receive\",\n      \"inventory_status\": \"active\",\n      \"quantity\": 30,\n      \"reference\": \"#912\",\n      \"link_type\": \"App\\\\Models\\\\WarehouseTransferShipmentReceiptLine\",\n      \"link_id\": 7120,\n      \"parent_link_id\": 3301,\n      \"detail_link_id\": 912,\n      \"product_id\": 812,\n      \"product_sku\": \"WIDGET-BLU\",\n      \"product_name\": \"Blue Widget\",\n      \"warehouse_id\": 3,\n      \"warehouse_name\": \"East Coast DC\",\n      \"unit_cost\": 12.5,\n      \"recorded_unit_cost\": 12.5,\n      \"layer_unit_cost\": 12.5,\n      \"unit_cost_provisional\": false,\n      \"layer\": {\n        \"fifo_layer_id\": 4532,\n        \"name\": \"fifo\"\n      },\n      \"inventory_movement_date\": \"2025-04-13T09:30:00.000000Z\",\n      \"created_at\": \"2025-04-13T09:30:00.000000Z\"\n    }\n  ],\n  \"last_page\": 2,\n  \"per_page\": 25,\n  \"total\": 45\n}"}]},{"name":"Get FIFO Layers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}/fifo-layers?per_page=25&filter[search]=","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer","fifo-layers"],"query":[{"key":"per_page","value":"25","description":"Results per page (default 10).","disabled":true},{"key":"filter[search]","value":"","description":"Search FIFO layers by product SKU or name","disabled":true}],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]},"description":"Returns the FIFO inventory layers for this warehouse transfer (paginated).\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Two layer types depending on the transfer's destination:**\n- **Standard transfers** (destination is a regular warehouse) return regular `FifoLayer` rows produced when the transfer was received. The `origin` object carries the polymorphic receipt-line link.\n- **Amazon inbound transfers** (destination is an Amazon FBA/AWD warehouse — i.e. FBA inbound, AWD inbound, or AWD→FBA replenishment) don't produce regular `FifoLayer` rows; inventory enters Amazon and is tracked by `AmazonFifoLayer`. For these, the corresponding Amazon FIFO layers are returned in the same shape, each flagged with `is_amazon: true`, exposing `source_label` / `source_reference` (the Amazon source: ledger / discrepancy / initial inventory) in place of a receipt `origin`, plus a `detail_route` to the Amazon FIFO layer detail page. The Amazon layers are linked indirectly via the transfer's Amazon inbound shipment(s) (`AmazonNewFbaInboundShipment.sku_link`).\n\n**Query Parameters**:\n- `per_page` — Items per page (default: 15)\n- `filter[search]` — Search by product SKU or name"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}/fifo-layers?per_page=25&filter[search]=","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer","fifo-layers"],"query":[{"key":"per_page","value":"25","description":"Results per page (default 10).","disabled":true},{"key":"filter[search]","value":"","description":"Search FIFO layers by product SKU or name","disabled":true}],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 7701,\n      \"is_amazon\": false,\n      \"product_id\": 812,\n      \"fifo_layer_date\": \"2025-04-13T09:30:00.000000Z\",\n      \"sku\": \"WIDGET-BLU\",\n      \"product_name\": \"Blue Widget\",\n      \"original_quantity\": 30,\n      \"fulfilled_quantity\": 2,\n      \"available_quantity\": 28,\n      \"avg_cost\": \"12.5000\",\n      \"total_cost\": \"375.0000\",\n      \"origin\": {\n        \"link_type\": \"App\\\\Models\\\\WarehouseTransferShipmentReceiptLine\",\n        \"link_id\": 44012,\n        \"name\": \"Warehouse Transfer Receipt Line\",\n        \"parent_link_id\": 9120,\n        \"detail_link_id\": 501,\n        \"reference\": \"WT-501\",\n        \"parent_reference\": \"WT-501\",\n        \"manufacturing_order\": null\n      },\n      \"warehouse\": {\n        \"id\": 3,\n        \"name\": \"East Coast DC\"\n      },\n      \"created_at\": \"2025-04-13T09:30:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 3\n}"}]},{"name":"Get Product Cost Composition","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{productId}/cost-composition","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":productId","cost-composition"],"variable":[{"key":"productId","value":"101","description":"Product ID"}]},"description":"Returns the per-component cost composition of a product's weighted-average unit cost — how much each consumed component (and any landed-cost or labor category) contributes to the average unit cost. Useful for manufactured or assembled goods to see what makes up the final cost, not just the blended number.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAggregated across the product's active warehouse FIFO layers, prorated by each layer's remaining quantity, so the composition ties out to the weighted-average cost. Amazon FBA inventory is excluded (separate WAC ledger). Layers with no categorized breakdown fall into an \"Uncategorized\" bucket.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{productId}/cost-composition","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":productId","cost-composition"],"variable":[{"key":"productId","value":"101","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"components\": [\n      {\n        \"category\": \"Helmet Shell\",\n        \"total\": 400,\n        \"per_unit\": 40,\n        \"pct\": 80\n      },\n      {\n        \"category\": \"Carry Pouch\",\n        \"total\": 60,\n        \"per_unit\": 6,\n        \"pct\": 12\n      },\n      {\n        \"category\": \"Sticker Pack\",\n        \"total\": 40,\n        \"per_unit\": 4,\n        \"pct\": 8\n      }\n    ],\n    \"total_available\": 10,\n    \"avg_cost\": 50\n  }\n}"}]},{"name":"Get Allocations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}/allocations?filter[status]=all&filter[warehouse_id]=&filter[search]=&per_page=25","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer","allocations"],"query":[{"key":"filter[status]","value":"all","description":"Allocation status filter: all | active | pending | released | cancelled | fulfilled","disabled":true},{"key":"filter[warehouse_id]","value":"","description":"Filter by destination warehouse ID","disabled":true},{"key":"filter[search]","value":"","description":"Search by product SKU or name","disabled":true},{"key":"per_page","value":"25","description":"Results per page (default 10).","disabled":true}],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]},"description":"Returns inventory allocations linked to this warehouse transfer.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Query Parameters**:\n- `filter[status]` — `all` | `active` | `pending` | `released` | `cancelled` | `fulfilled`\n- `filter[warehouse_id]` — Filter by warehouse\n- `filter[search]` — Search by product SKU or name\n- `per_page` — Items per page"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}/allocations?filter[status]=all&filter[warehouse_id]=&filter[search]=&per_page=25","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer","allocations"],"query":[{"key":"filter[status]","value":"all","description":"Allocation status filter: all | active | pending | released | cancelled | fulfilled","disabled":true},{"key":"filter[warehouse_id]","value":"","description":"Filter by destination warehouse ID","disabled":true},{"key":"filter[search]","value":"","description":"Search by product SKU or name","disabled":true},{"key":"per_page","value":"25","description":"Results per page (default 10).","disabled":true}],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 10045,\n      \"product_id\": 812,\n      \"product\": {\n        \"id\": 812,\n        \"sku\": \"WIDGET-BLU\",\n        \"name\": \"Blue Widget\"\n      },\n      \"warehouse_id\": 3,\n      \"warehouse\": {\n        \"id\": 3,\n        \"name\": \"East Coast DC\"\n      },\n      \"quantity\": 5,\n      \"status\": \"pending\",\n      \"status_label\": \"Pending\",\n      \"priority\": 1,\n      \"created_at\": \"2025-04-10T08:22:11.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 2\n}"}]},{"name":"Generate Accounting Transactions","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}/generate-accounting-transactions","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer","generate-accounting-transactions"],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]},"description":"Generates or regenerates accounting transactions (financial lines) for this warehouse transfer based on the current COGS data.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}/generate-accounting-transactions","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer","generate-accounting-transactions"],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Accounting transactions generated successfully.\",\n  \"financial_lines_count\": 6\n}"}]},{"name":"Create Receiving Discrepancy","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/receiving-discrepancy","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","receiving-discrepancy"],"variable":[{"key":"transfer","value":"501","description":"Warehouse Transfer ID"}]},"description":"Creates a receiving-discrepancy accounting transaction for a warehouse transfer, recording the financial gain/loss from the difference between expected and received quantities.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest body:\n- `transaction_date` (optional, date) — date to record the transaction on; defaults to now when omitted.\n\nNote: Receiving discrepancies cannot be created for Amazon-to-Amazon transfers (e.g. AWD→FBA) — Amazon reconciles those transfers automatically, so the endpoint returns 400.\n\n**Requires permission:** `warehouse_transfers.receive`","body":{"mode":"raw","raw":"{\n  \"transaction_date\": \"2026-05-15\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/receiving-discrepancy","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","receiving-discrepancy"],"variable":[{"key":"transfer","value":"501","description":"Warehouse Transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Successfully closed with receiving discrepancy\"\n}"}]},{"name":"Get Transfer Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}/activity-log?per_page=25&filter[search]=","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer","activity-log"],"query":[{"key":"per_page","value":"25","description":"Results per page (default 10).","disabled":true},{"key":"filter[search]","value":"","description":"Search activity log entries","disabled":true}],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]},"description":"Returns paginated activity log entries for a warehouse transfer.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/{warehouseTransfer}/activity-log?per_page=25&filter[search]=","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers",":warehouseTransfer","activity-log"],"query":[{"key":"per_page","value":"25","description":"Results per page (default 10).","disabled":true},{"key":"filter[search]","value":"","description":"Search activity log entries","disabled":true}],"variable":[{"key":"warehouseTransfer","value":"501","description":"Warehouse Transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 9901,\n      \"event\": \"created\",\n      \"description\": \"Warehouse transfer WT-501 created\",\n      \"causer\": {\n        \"id\": 1,\n        \"name\": \"Jane Smith\"\n      },\n      \"created_at\": \"2025-04-10T08:00:00.000000Z\",\n      \"properties\": {}\n    },\n    {\n      \"id\": 9902,\n      \"event\": \"updated\",\n      \"description\": \"Status changed to in_transit\",\n      \"causer\": {\n        \"id\": 1,\n        \"name\": \"Jane Smith\"\n      },\n      \"created_at\": \"2025-04-11T14:05:00.000000Z\",\n      \"properties\": {\n        \"old\": {\n          \"status\": \"pending\"\n        },\n        \"new\": {\n          \"status\": \"in_transit\"\n        }\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 12\n}"}]},{"name":"Download Lines Import Template","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/import-lines/template","host":["{{baseUrl}}"],"path":["api","warehouses","transfers","import-lines","template"]},"description":"Downloads a multi-sheet XLSX template for the warehouse transfer lines bulk import.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Response**: A binary `.xlsx` file (zip-based Office Open XML).\n- `Content-Type`: `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`\n- `Content-Disposition`: `attachment; filename=\"warehouse-transfer-lines-import-template-YYYY-MM-DD.xlsx\"`\n\n**Sheets:**\n1. **Import Here** — the row-data sheet with two required columns (`sku`, `quantity`) plus 3 example rows. Header row is frozen; columns are autofiltered; the SKU column has a comment explaining required fields.\n2. **Field Guide** — reference sheet describing each column (Required, Description, Valid Values, Example).\n\nNo query parameters or request body. Used by the \"Import Lines from CSV\" workflow on the warehouse transfer detail page (the resulting filled-out file is then POSTed to `/api/warehouses/transfers/import-lines`).\n\n**Requires permission:** `warehouse_transfers.import`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/import-lines/template","host":["{{baseUrl}}"],"path":["api","warehouses","transfers","import-lines","template"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"<binary XLSX file — zip archive starting with PK\\x03\\x04 signature>"}]},{"name":"Import Warehouse Transfer Lines","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/import-lines","host":["{{baseUrl}}"],"path":["api","warehouses","transfers","import-lines"]},"description":"Imports line items into an existing warehouse transfer from CSV data. Provide either file (a stored upload reference) or csvString (inline CSV content). The CSV must contain a sku column; quantity is optional and defaults to 1. Rows with the same SKU are merged and quantities summed.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: warehouse_transfer_id (required), file or csvString (one required), replace (boolean - when true, replaces all existing lines instead of adding), separator (default \",\") and escape (default '\"').\n\nReturns 400 when the CSV contains SKUs that do not match any product. Download a template from GET /api/warehouses/transfers/import-lines/template.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"warehouse_transfer_id\": 512,\n  \"csvString\": \"sku,quantity\\nWIDGET-001,100\\nWIDGET-002,50\",\n  \"replace\": false,\n  \"separator\": \",\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/import-lines","host":["{{baseUrl}}"],"path":["api","warehouses","transfers","import-lines"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse transfer lines imported successfully.\"\n}"}]},{"name":"Export Warehouse Transfers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/export","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","export"]},"description":"Download warehouse transfers as an XLSX or CSV file. Requires export permission for warehouse transfers.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nExport controls:\n- `format` — xlsx (default) or csv.\n- `content_mode` — summary (default, one row per transfer) or line_items (one row per transfer line, with the transfer columns repeated and line columns appended: Line ID, Item SKU, Item Name, Item Description, Item Qty, Cancelled Qty, Item FNSKU).\n- `scope` — filtered (default: respects active filters/search), all (ignores filters), current_page (only the current page of the list), or selected (only the IDs passed in `ids`).\n- `ids` — comma-separated transfer IDs; required when scope=selected.\n- `columns` — comma-separated column keys to restrict/reorder transfer-level columns. Available keys: id, warehouse_transfer_number, transfer_date, from_warehouse, to_warehouse, transfer_type, status, shipment_status, receipt_status, shipping_method, shipment_id, line_item_count, eta, fully_shipped_at, fully_received_at, tags, created_at, updated_at (plus any custom-field columns defined for transfers).\n\nFilters (same set as List Warehouse Transfers; applied when scope is filtered or current_page):\n- `filter[search]` — matches transfer ID, transfer number, from/to warehouse name, item SKU, and item FNSKU.\n- `filter[archived]` — archived visibility: omit for non-archived only, `only` for archived only, `all` for everything.\n- `filter[include_deleted]` — `include` adds deleted transfers, `only` returns just deleted ones.\n- `filter[transfer_status.{op}]`, `filter[shipment_status.{op}]`, `filter[receipt_status.{op}]` — text operators; receipt_status also accepts the special value received_for_less.\n- `filter[transfer_type.{op}]` — values: standard, fba_inbound, awd_inbound, awd_fba_replenishment; operators: is, is_not, is_one_of, is_not_one_of.\n- `filter[has_blemished.is]=1` / `filter[has_inbound_plan.is]=1` — flag filters (is / is_not).\n- `filter[id.{op}]` — numeric operators.\n- `filter[from_warehouse.{op}]`, `filter[to_warehouse.{op}]` — warehouse ID, text operators.\n- `filter[transfer_date.{op}]`, `filter[created_at.{op}]`, `filter[updated_at.{op}]` — date operators.\n\nText operators: contains, does_not_contain, is, is_not, is_one_of, is_not_one_of, starts_with, does_not_start_with, ends_with, does_not_end_with, is_empty, is_not_empty. Numeric operators: is, is_not, is_one_of, is_not_one_of, greater_than, less_than, greater_than_or_equal, less_than_or_equal, between, is_empty, is_not_empty. Date operators: is, is_not, before, after, on_or_before, on_or_after, between, is_empty, is_not_empty, today, yesterday, tomorrow, past_week, past_month, past_year, next_week, next_month, next_year, days_ago, days_from_now, past_days, next_days.\n\n`filter_groups` — base64-encoded JSON tree for nested AND/OR filter groups over the same columns.\n\nSorting: `sort` with optional `-` prefix for descending. Allowed sorts: id, warehouse_transfer_number, transfer_date, transfer_status, shipment_status, receipt_status, created_at, updated_at, eta, archived_at, asn_last_sent_at, status, from_warehouse_name, to_warehouse_name, from_warehouse, to_warehouse. Default: -transfer_date.\n\nResponse: binary file download (Content-Type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet for xlsx, text/csv for csv).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/export","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary XLSX file download — one row per warehouse transfer, or per line item when content_mode=line_items)"}]},{"name":"Get Warehouse Transfer Filter Options","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/filter-options","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","filter-options"]},"description":"Returns the list of warehouses available for filtering warehouse transfers, as id/name pairs (Amazon FBA, virtual, and supplier warehouses are excluded, and archived warehouses are omitted). Use it to populate the warehouse filter dropdowns on the transfers list.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/filter-options","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","filter-options"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"warehouses\": [\n      {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"East Coast DC\"\n      }\n    ]\n  }\n}"}]},{"name":"Update Transfer Lines (Bulk)","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{warehouseTransfer}/lines","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":warehouseTransfer","lines"],"variable":[{"key":"warehouseTransfer","value":"50","description":"Warehouse Transfer ID"}]},"description":"Bulk updates warehouse transfer lines (products/quantities). Uses WarehouseTransferData DTO.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"products\": [\n    {\n      \"id\": 101,\n      \"product_id\": 10,\n      \"quantity\": 75,\n      \"quantity_cancelled\": 5,\n      \"description\": \"Updated line\",\n      \"sales_channel_unique_id\": \"X001234\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{warehouseTransfer}/lines","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":warehouseTransfer","lines"],"variable":[{"key":"warehouseTransfer","value":"50","description":"Warehouse Transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse transfer updated successfully\"\n}"}]},{"name":"Create Shipment","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/shipments","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","shipments"],"variable":[{"key":"transfer","value":"38207","description":"Warehouse transfer ID (must be in open status)"}]},"description":"Creates a shipment for an already-open traditional (non-Amazon FBA) warehouse transfer. This is the explicit second step of the split lifecycle: open the transfer first (allocations only), then ship.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Effects:**\n- Deducts source warehouse stock\n- Creates `-active` and `+in_transit` inventory movements\n- Fulfils the line-level allocations created during open (Allocated → Fulfilled)\n\n**Request body (all fields optional unless noted):**\n- `shipment_date` (date) — defaults to today if omitted\n- `shipping_method_id` (integer) — must exist in `shipping_methods`\n- `tracking_number` (string, max 255)\n- `cost` (numeric, >= 0, < 100000)\n- `lines` (array, min 1 when present) — if omitted, all remaining open line quantities are shipped\n  - `lines[].warehouse_transfer_line_id` (integer, required_with:lines) — must belong to the transfer\n  - `lines[].quantity` (numeric, required_with:lines, min 0.0001)\n\n**Responses:**\n- `201 Created` — warehouse transfer object with updated shipments\n- `422 Unprocessable Entity` (`WarehouseTransferIsNotOpen`) — transfer is not in open status\n- `422 Unprocessable Entity` (`WarehouseTransferLineIsUnacceptable`) — a transfer line's requested quantity exceeds its allocated sum (allocations out of sync, e.g. stuck Fulfilled rows); the message includes the product SKU and the allocated quantity\n- `422 Unprocessable Entity` — transfer is an Amazon FBA transfer (use the Amazon-specific flow instead)\n- `400 Bad Request` (`ProductIsUnacceptable`) — not enough stock at the source warehouse for one of the requested lines\n\n**Requires permission:** `warehouse_transfers.ship`\nForm Request: `App\\Http\\Requests\\CreateWarehouseTransferShipmentRequest`\nRoute name: `warehouses.transfers.shipments.store`\n\nSource bin: each line may optionally set `warehouse_location_id` — the source-warehouse bin the line's dispatch should draw stock from. It must be an active (non-deprecated) bin in the transfer's source warehouse. When omitted, pickable source bins are walked automatically.\n\n**Serial numbers.** `lines[].serials` (optional array of strings) names the serials leaving on a serialized line. It must hold exactly one serial per unit shipped; an empty or omitted list sends the oldest serials in stock at the source warehouse. Under Require exact match, a named serial that isn't in stock at the source returns 422 on `lines.N.serials`. Shipped serials are In Transit, still on the source warehouse, until received. For a destination that is never received here (FBA and other externally managed warehouses), they are marked Shipped instead. Deleting the shipment returns them to stock at the source. `items[].product` on the transfer now includes `tracks_serials` and `serial_generation_mode`.","body":{"mode":"raw","raw":"{\n  \"shipment_date\": \"2026-06-12\",\n  \"shipping_method_id\": 3,\n  \"tracking_number\": \"1Z999AA10123456784\",\n  \"cost\": 25.5,\n  \"lines\": [\n    {\n      \"warehouse_transfer_line_id\": 87499,\n      \"quantity\": 150,\n      \"warehouse_location_id\": 15\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/shipments","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","shipments"],"variable":[{"key":"transfer","value":"38207","description":"Warehouse transfer ID (must be in open status)"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 38207,\n    \"warehouse_transfer_number\": \"WH-38207\",\n    \"transfer_status\": \"open\",\n    \"shipment_status\": \"shipped\",\n    \"from_warehouse\": {\n      \"id\": 7,\n      \"name\": \"Main Warehouse\"\n    },\n    \"to_warehouse\": {\n      \"id\": 8,\n      \"name\": \"Overflow Warehouse\"\n    },\n    \"transfer_date\": \"2026-06-12\",\n    \"shipments\": [\n      {\n        \"id\": 901,\n        \"tracking_number\": \"1Z999AA10123456784\",\n        \"shipped_at\": \"2026-06-12T16:00:00.000000Z\",\n        \"lines\": [\n          {\n            \"id\": 4001,\n            \"warehouse_transfer_line_id\": 87499,\n            \"quantity\": 150\n          }\n        ]\n      }\n    ]\n  },\n  \"message\": \"The warehouse transfer shipment created successfully\"\n}"}]},{"name":"Get Shipment Detail","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/shipments/{shipment}","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","shipments",":shipment"],"variable":[{"key":"shipment","value":"201","description":"Shipment ID"}]},"description":"Returns the detail view of a specific warehouse transfer shipment, including shipment lines and any associated receipts.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/shipments/{shipment}","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","shipments",":shipment"],"variable":[{"key":"shipment","value":"201","description":"Shipment ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 201,\n  \"warehouse_transfer_id\": 501,\n  \"shipped_at\": \"2025-04-11T14:00:00.000000Z\",\n  \"tracking_number\": \"1Z999AA1234567890\",\n  \"carrier\": \"UPS\",\n  \"notes\": null,\n  \"lines\": [\n    {\n      \"id\": 4001,\n      \"warehouse_transfer_line_id\": 3001,\n      \"product_id\": 812,\n      \"product\": {\n        \"id\": 812,\n        \"sku\": \"WIDGET-BLU\",\n        \"name\": \"Blue Widget\"\n      },\n      \"quantity\": 50\n    }\n  ],\n  \"receipts\": [\n    {\n      \"id\": 301,\n      \"received_at\": \"2025-04-13T09:30:00.000000Z\",\n      \"received_by\": \"John Doe\"\n    }\n  ],\n  \"created_at\": \"2025-04-11T14:00:00.000000Z\",\n  \"updated_at\": \"2025-04-13T09:30:00.000000Z\"\n}"}]},{"name":"Preview Shipment Line Quantity Change","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/shipments/{shipment}/lines/{line}/quantity-preview?quantity=12","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","shipments",":shipment","lines",":line","quantity-preview"],"query":[{"key":"quantity","value":"12","description":"Target shipment line quantity (integer, >= 0). Required.","disabled":true}],"variable":[{"key":"shipment","value":"201","description":"Warehouse transfer shipment ID"},{"key":"line","value":"4001","description":"Warehouse transfer shipment line ID"}]},"description":"Preview the inventory implications of changing a warehouse transfer shipment line's quantity, without persisting any change.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns:\n- `current_quantity`, `new_quantity`, `delta`\n- `requires_confirmation` — true whenever delta != 0\n- `can_update` — false when either block applies\n- `blocks.below_received` — true if the requested quantity is below the total already received against this line; adjust receipts first\n- `blocks.above_transfer_line` — true if the requested quantity exceeds the parent warehouse transfer line quantity (the WT line caps the shipment, not source on-hand stock)\n- `implications.shipment_quantity` / `transfer_quantity` / `received_quantity` / `in_transit_quantity` — current vs after-update values. The parent warehouse transfer line quantity is ALWAYS unchanged (BBP invariant: the WT line is the canonical record of original intent)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/shipments/{shipment}/lines/{line}/quantity-preview?quantity=12","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","shipments",":shipment","lines",":line","quantity-preview"],"query":[{"key":"quantity","value":"12","description":"Target shipment line quantity (integer, >= 0). Required.","disabled":true}],"variable":[{"key":"shipment","value":"201","description":"Warehouse transfer shipment ID"},{"key":"line","value":"4001","description":"Warehouse transfer shipment line ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"shipment_id\": 201,\n    \"line_id\": 4001,\n    \"current_quantity\": 10,\n    \"new_quantity\": 8,\n    \"delta\": -2,\n    \"requires_confirmation\": true,\n    \"can_update\": true,\n    \"blocks\": {\n      \"below_received\": false,\n      \"above_transfer_line\": false\n    },\n    \"implications\": {\n      \"shipment_quantity\": {\n        \"label\": \"Warehouse transfer shipment line quantity\",\n        \"current\": 10,\n        \"after_update\": 8\n      },\n      \"transfer_quantity\": {\n        \"label\": \"Warehouse transfer line quantity (unchanged)\",\n        \"current\": 10,\n        \"after_update\": 10\n      },\n      \"received_quantity\": {\n        \"label\": \"Already received against this line\",\n        \"current\": 0,\n        \"after_update\": 0\n      },\n      \"in_transit_quantity\": {\n        \"label\": \"In-transit at destination after edit\",\n        \"current\": 10,\n        \"after_update\": 8\n      }\n    }\n  }\n}"}]},{"name":"Update Shipment Line Quantity","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/shipments/{shipment}/lines/{line}","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","shipments",":shipment","lines",":line"],"variable":[{"key":"shipment","value":"201","description":"Warehouse transfer shipment ID"},{"key":"line","value":"4001","description":"Warehouse transfer shipment line ID"}]},"description":"Update a warehouse transfer shipment line's quantity in place, scaling its inventory movements at the source (active negative) and destination (in-transit positive) accordingly.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequest body:\n- `quantity` (integer, required, >= 0) — new shipment line quantity. Capped by the parent warehouse transfer line quantity.\n- `confirmed_implications` (boolean, optional) — must be `true` when the change is non-zero; otherwise the endpoint returns 409 with the preview payload so the caller can confirm.\n\nBehavior:\n- Increase: pulls `delta` additional units from source on-hand FIFO and places them in-transit toward the destination.\n- Decrease: returns `abs(delta)` units to source on-hand and removes them from in-transit at the destination.\n- The parent warehouse transfer line quantity is NEVER mutated by this endpoint — it preserves the original transfer intent (BBP invariant).\n\nErrors:\n- `409 Conflict` — non-zero delta without `confirmed_implications: true`; the preview is returned in `data` so callers can render and confirm.\n- `422 Unprocessable Entity` with `data.blocks.below_received: true` — requested quantity is below the total already received against this line; adjust receipts first.\n- `422 Unprocessable Entity` with `data.blocks.above_transfer_line: true` — requested quantity exceeds the parent warehouse transfer line quantity.\n- `422 Unprocessable Entity` from `InsufficientStockException` thrown by the inventory manager if FIFO consumption cannot satisfy the delta at the moment of save (race / concurrency safety net).\n\n**Requires permission:** `warehouse_transfers.ship`","body":{"mode":"raw","raw":"{\n  \"quantity\": 12,\n  \"confirmed_implications\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/shipments/{shipment}/lines/{line}","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","shipments",":shipment","lines",":line"],"variable":[{"key":"shipment","value":"201","description":"Warehouse transfer shipment ID"},{"key":"line","value":"4001","description":"Warehouse transfer shipment line ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 201,\n    \"warehouse_transfer\": {\n      \"id\": 501,\n      \"warehouse_transfer_number\": \"WT-0501\"\n    },\n    \"from_warehouse\": {\n      \"id\": 7,\n      \"name\": \"Main Warehouse\"\n    },\n    \"to_warehouse\": {\n      \"id\": 8,\n      \"name\": \"Overflow Warehouse\"\n    },\n    \"shipping_method\": null,\n    \"tracking_number\": \"1Z999AA1234567890\",\n    \"shipped_at\": \"2025-04-11T14:00:00.000000Z\",\n    \"fully_received_at\": null,\n    \"auto_generated_at_receipt\": false,\n    \"summary\": {\n      \"total_lines\": 1,\n      \"total_shipped\": 12,\n      \"total_received\": 0,\n      \"total_receipts\": 0\n    },\n    \"lines\": [\n      {\n        \"id\": 4001,\n        \"product\": {\n          \"id\": 812,\n          \"sku\": \"WIDGET-BLU\",\n          \"name\": \"Blue Widget\",\n          \"image_url\": null\n        },\n        \"description\": null,\n        \"quantity\": 12,\n        \"received_quantity\": 0\n      }\n    ],\n    \"receipts\": []\n  },\n  \"message\": \"Shipment line quantity updated successfully.\"\n}"}]},{"name":"Get Shipment Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/shipments/{shipment}/activity-log?per_page=25&filter[search]=","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","shipments",":shipment","activity-log"],"query":[{"key":"per_page","value":"25","description":"Results per page (default 10).","disabled":true},{"key":"filter[search]","value":"","description":"Search activity log entries","disabled":true}],"variable":[{"key":"shipment","value":"201","description":"Shipment ID"}]},"description":"Returns paginated activity log entries for a specific shipment.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/shipments/{shipment}/activity-log?per_page=25&filter[search]=","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","shipments",":shipment","activity-log"],"query":[{"key":"per_page","value":"25","description":"Results per page (default 10).","disabled":true},{"key":"filter[search]","value":"","description":"Search activity log entries","disabled":true}],"variable":[{"key":"shipment","value":"201","description":"Shipment ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 9910,\n      \"event\": \"created\",\n      \"description\": \"Shipment created with tracking 1Z999AA1234567890\",\n      \"causer\": {\n        \"id\": 1,\n        \"name\": \"Jane Smith\"\n      },\n      \"created_at\": \"2025-04-11T14:00:00.000000Z\",\n      \"properties\": {}\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 3\n}"}]},{"name":"Get Shipment Inventory Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/shipments/{shipment}/inventory-movements?page=1&per_page=25","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","shipments",":shipment","inventory-movements"],"query":[{"key":"page","value":"1","description":"Page number (default 1)","disabled":true},{"key":"per_page","value":"25","description":"Items per page (default 25)","disabled":true}],"variable":[{"key":"shipment","value":"201","description":"Shipment ID"}]},"description":"Returns paginated inventory movements scoped to the given warehouse transfer shipment. Movements are linked to the shipment's `WarehouseTransferShipmentLine` records (negative inventory events — stock leaving the source warehouse). Includes movement type, quantity, FIFO layer link, unit/total cost, and product/warehouse references for each movement. Used by the Movements tab on the shipment detail page.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/shipments/{shipment}/inventory-movements?page=1&per_page=25","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","shipments",":shipment","inventory-movements"],"query":[{"key":"page","value":"1","description":"Page number (default 1)","disabled":true},{"key":"per_page","value":"25","description":"Items per page (default 25)","disabled":true}],"variable":[{"key":"shipment","value":"201","description":"Shipment ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 87553,\n      \"type\": \"transfer\",\n      \"inventory_status\": \"available\",\n      \"quantity\": -10,\n      \"reference\": \"WT-2025-0042\",\n      \"link_type\": \"App\\\\Models\\\\WarehouseTransferShipmentLine\",\n      \"link_id\": 4012,\n      \"parent_link_id\": 201,\n      \"detail_link_id\": null,\n      \"product_id\": 812,\n      \"product_sku\": \"WIDGET-BLU\",\n      \"product_name\": \"Blue Widget\",\n      \"warehouse_id\": 8,\n      \"warehouse_name\": \"Source Warehouse\",\n      \"unit_cost\": 4.5,\n      \"unit_cost_provisional\": false,\n      \"total_cost\": 45,\n      \"layer\": {\n        \"name\": \"FIFO Layer #43850\",\n        \"layer_type\": \"App\\\\Models\\\\FifoLayer\",\n        \"layer_id\": 43850\n      },\n      \"inventory_movement_date\": \"2025-04-11T14:00:00.000000Z\",\n      \"created_at\": \"2025-04-11T14:00:01.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Delete Warehouse Transfer Shipment","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/shipments/{shipment}","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","shipments",":shipment"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"},{"key":"shipment","value":"204","description":"Shipment ID"}]},"description":"Deletes a shipment from the warehouse transfer, reversing its stock deduction at the source warehouse and returning the shipped quantities to available stock. Returns the updated transfer.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/shipments/{shipment}","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","shipments",":shipment"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"},{"key":"shipment","value":"204","description":"Shipment ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse transfer shipment 204 deleted successfully.\",\n  \"data\": {\n    \"id\": 512,\n    \"transfer_type\": \"standard\",\n    \"warehouse_transfer_number\": \"WT-000512\",\n    \"transfer_date\": \"2026-06-01T00:00:00.000000Z\",\n    \"from_warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"type\": \"direct\",\n      \"integration_instance_id\": null\n    },\n    \"to_warehouse\": {\n      \"id\": 5,\n      \"name\": \"West Coast 3PL\",\n      \"type\": \"3pl\"\n    },\n    \"status\": \"open\",\n    \"shipment_status\": \"unshipped\",\n    \"shipment_id\": null,\n    \"receipt_status\": \"unreceived\",\n    \"fully_shipped_at\": null,\n    \"fully_received_at\": null,\n    \"eta\": \"2026-06-15\",\n    \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"updated_at\": \"2026-06-02T08:30:00.000000Z\",\n    \"archived_at\": null,\n    \"line_item_count\": 1,\n    \"first_line_sku\": \"WIDGET-001\",\n    \"asn_last_sent_at\": null\n  }\n}"}]},{"name":"List Warehouse Transfer Receipts","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/receipts","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","receipts"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]},"description":"Returns all shipment receipts recorded against the warehouse transfer, including receipt lines with received quantities and the units still pending per shipment line.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/receipts","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","receipts"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 31,\n      \"received_at\": \"2026-06-10T09:00:00.000000Z\",\n      \"user\": {\n        \"id\": 4,\n        \"name\": \"Jane Smith\"\n      },\n      \"receipt_lines\": [\n        {\n          \"id\": 74,\n          \"product_id\": 101,\n          \"product_name\": \"Widget\",\n          \"product_sku\": \"WIDGET-001\",\n          \"quantity\": 60,\n          \"units_pending\": 40,\n          \"warehouse_transfer_line_id\": 9001\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"Get Receipt Detail","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/receipts/{receipt}","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","receipts",":receipt"],"variable":[{"key":"receipt","value":"301","description":"Receipt ID"}]},"description":"Returns the detail view of a specific warehouse transfer receipt, including receipt lines. Automatically detects lines that generated blemished inventory adjustments.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/receipts/{receipt}","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","receipts",":receipt"],"variable":[{"key":"receipt","value":"301","description":"Receipt ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 301,\n  \"warehouse_transfer_id\": 501,\n  \"shipment_id\": 201,\n  \"received_at\": \"2025-04-13T09:30:00.000000Z\",\n  \"received_by\": \"John Doe\",\n  \"notes\": null,\n  \"lines\": [\n    {\n      \"id\": 5001,\n      \"warehouse_transfer_line_id\": 3001,\n      \"product_id\": 812,\n      \"product\": {\n        \"id\": 812,\n        \"sku\": \"WIDGET-BLU\",\n        \"name\": \"Blue Widget\"\n      },\n      \"quantity\": 28,\n      \"blemished_quantity\": 2,\n      \"has_blemished_adjustment\": true\n    },\n    {\n      \"id\": 5002,\n      \"warehouse_transfer_line_id\": 3002,\n      \"product_id\": 813,\n      \"product\": {\n        \"id\": 813,\n        \"sku\": \"WIDGET-RED\",\n        \"name\": \"Red Widget\"\n      },\n      \"quantity\": 60,\n      \"blemished_quantity\": 0,\n      \"has_blemished_adjustment\": false\n    }\n  ],\n  \"created_at\": \"2025-04-13T09:30:00.000000Z\",\n  \"updated_at\": \"2025-04-13T09:30:00.000000Z\"\n}"}]},{"name":"Preview Receipt Line Quantity Change","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/receipts/{receipt}/lines/{line}/quantity-preview?quantity=8","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","receipts",":receipt","lines",":line","quantity-preview"],"query":[{"key":"quantity","value":"8","description":"Desired receipt line quantity (integer ≥ 0)","disabled":true}],"variable":[{"key":"receipt","value":"301","description":"Warehouse transfer receipt ID"},{"key":"line","value":"5001","description":"Warehouse transfer receipt line ID"}]},"description":"Preview the implications of changing a warehouse transfer receipt line quantity without persisting any changes.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns:\n- `delta` — change vs. current quantity\n- `requires_confirmation` — true if the user must confirm before saving\n- `can_update` — false when blocking conditions exist\n- `removes_line` — true when the new quantity is 0: saving removes the receipt line with its inventory movement and FIFO layer, and the units return to in transit at the destination\n- `blocks.last_line` — true when the new quantity is 0 and this is the receipt's only line; the line cannot be removed this way, delete the receipt instead\n- `blocks.above_shipped` — true if the new quantity exceeds the parent shipment line's shipped quantity (minus other receipts against the same shipment line)\n- `blocks.above_transfer_line` — true if the new quantity exceeds the parent warehouse transfer line quantity (minus other receipts against the same shipment line)\n- `caps.shipped_quantity` — shipment line shipped quantity\n- `caps.transfer_line_quantity` — warehouse transfer line quantity\n- `caps.other_receipts_total` — total received on the same shipment line via other receipts (excluding the line being edited)\n- `caps.max_quantity` — minimum of `shipped - other_receipts_total` and `transfer_line - other_receipts_total`\n- `implications.shipment_quantity` — shipment line quantity is NOT mutated by a receipt edit\n- `implications.transfer_quantity` — warehouse transfer line quantity is NOT mutated by a receipt edit\n- `implications.in_transit_quantity` — recomputed in-transit at destination after edit\n- `inventory` — FIFO usage / redistribution preview (when decreasing)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/receipts/{receipt}/lines/{line}/quantity-preview?quantity=8","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","receipts",":receipt","lines",":line","quantity-preview"],"query":[{"key":"quantity","value":"8","description":"Desired receipt line quantity (integer ≥ 0)","disabled":true}],"variable":[{"key":"receipt","value":"301","description":"Warehouse transfer receipt ID"},{"key":"line","value":"5001","description":"Warehouse transfer receipt line ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"receipt_type\": \"warehouse_transfer\",\n    \"line_id\": 5001,\n    \"current_quantity\": 4,\n    \"new_quantity\": 0,\n    \"delta\": -4,\n    \"requires_confirmation\": true,\n    \"can_update\": true,\n    \"removes_line\": true,\n    \"blocks\": {\n      \"above_shipped\": false,\n      \"above_transfer_line\": false,\n      \"last_line\": false\n    },\n    \"caps\": {\n      \"shipped_quantity\": 4,\n      \"transfer_line_quantity\": 4,\n      \"other_receipts_total\": 0,\n      \"max_quantity\": 4\n    },\n    \"implications\": {\n      \"shipment_quantity\": {\n        \"label\": \"Warehouse transfer shipment line quantity (unchanged)\",\n        \"current\": 4,\n        \"after_update\": 4,\n        \"total_received_after_edit\": 0\n      },\n      \"transfer_quantity\": {\n        \"label\": \"Warehouse transfer line quantity (unchanged)\",\n        \"current\": 4,\n        \"after_update\": 4\n      },\n      \"in_transit_quantity\": {\n        \"label\": \"In-transit quantity at destination after edit\",\n        \"current\": 0,\n        \"after_update\": 4\n      }\n    },\n    \"inventory\": {\n      \"fifo_layer\": {\n        \"id\": 9001,\n        \"product_id\": 812,\n        \"warehouse_id\": 8,\n        \"original_quantity\": 4,\n        \"fulfilled_quantity\": 0,\n        \"available_quantity\": 4\n      },\n      \"decrease_quantity\": 4,\n      \"available_without_reassignment\": 4,\n      \"usage_quantity_to_redistribute\": 0,\n      \"requires_redistribution\": false,\n      \"can_redistribute\": true,\n      \"blocks_update\": false,\n      \"alternative_available_quantity\": 0,\n      \"alternative_shortage_quantity\": 0,\n      \"usages\": [],\n      \"alternative_layers\": []\n    }\n  }\n}"}]},{"name":"Update Receipt Line Quantity","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/receipts/{receipt}/lines/{line}","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","receipts",":receipt","lines",":line"],"variable":[{"key":"receipt","value":"301","description":"Warehouse transfer receipt ID"},{"key":"line","value":"5001","description":"Warehouse transfer receipt line ID"}]},"description":"Update a warehouse transfer receipt line's quantity. The shipment line and warehouse transfer line are NOT mutated by this edit — they remain canonical intent.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nSetting `quantity` to 0 removes the line: a quantity of 0 means the line was not received, so the receipt line, its inventory movement and its FIFO layer are removed and the units return to in transit at the destination (the receipt itself is kept). The preview reports this as `removes_line: true`. A receipt's only line cannot be removed this way - the request fails with 422 and `blocks.last_line: true`; delete the receipt instead.\n\nBody fields:\n- `quantity` (required, integer ≥ 0) — new receipt line quantity\n- `confirmed_implications` (optional, boolean) — required when the preview reports `requires_confirmation: true`\n- `redistribute_usages` (optional, boolean) — required when reducing quantity causes FIFO usage redistribution\n\nReturns 409 with the preview payload when confirmation is required and not yet provided.\n\nReturns 422 with `blocks.above_shipped: true` when the new quantity exceeds the shipment line shipped quantity (minus other receipts), and `blocks.above_transfer_line: true` when it exceeds the warehouse transfer line quantity. The cap subtracts the totals of OTHER receipt lines on the same shipment line, so concurrent partial receipts each get their fair share.\n\n**Requires permission:** `warehouse_transfers.receive`","body":{"mode":"raw","raw":"{\n  \"quantity\": 8,\n  \"confirmed_implications\": true,\n  \"redistribute_usages\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/receipts/{receipt}/lines/{line}","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","receipts",":receipt","lines",":line"],"variable":[{"key":"receipt","value":"301","description":"Warehouse transfer receipt ID"},{"key":"line","value":"5001","description":"Warehouse transfer receipt line ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 301,\n    \"warehouse_transfer_id\": 501,\n    \"shipment_id\": 201,\n    \"received_at\": \"2025-04-13T09:30:00.000000Z\",\n    \"received_by\": \"John Doe\",\n    \"notes\": null,\n    \"lines\": [\n      {\n        \"id\": 5001,\n        \"warehouse_transfer_line_id\": 3001,\n        \"product_id\": 812,\n        \"product\": {\n          \"id\": 812,\n          \"sku\": \"WIDGET-BLU\",\n          \"name\": \"Blue Widget\"\n        },\n        \"quantity\": 8,\n        \"blemished_quantity\": 0,\n        \"has_blemished_adjustment\": false\n      }\n    ]\n  },\n  \"message\": \"Receipt line quantity updated successfully.\"\n}"}]},{"name":"Get Receipt Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/receipts/{receipt}/activity-log?per_page=25&filter[search]=","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","receipts",":receipt","activity-log"],"query":[{"key":"per_page","value":"25","description":"Results per page (default 10).","disabled":true},{"key":"filter[search]","value":"","description":"Search activity log entries","disabled":true}],"variable":[{"key":"receipt","value":"301","description":"Receipt ID"}]},"description":"Returns paginated activity log entries for a specific receipt.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/receipts/{receipt}/activity-log?per_page=25&filter[search]=","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","receipts",":receipt","activity-log"],"query":[{"key":"per_page","value":"25","description":"Results per page (default 10).","disabled":true},{"key":"filter[search]","value":"","description":"Search activity log entries","disabled":true}],"variable":[{"key":"receipt","value":"301","description":"Receipt ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 9920,\n      \"event\": \"created\",\n      \"description\": \"Receipt recorded — 90 units across 2 products\",\n      \"causer\": {\n        \"id\": 2,\n        \"name\": \"John Doe\"\n      },\n      \"created_at\": \"2025-04-13T09:30:00.000000Z\",\n      \"properties\": {}\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 2\n}"}]},{"name":"Get Receipt Inventory Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/receipts/{receipt}/inventory-movements?page=1&per_page=25","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","receipts",":receipt","inventory-movements"],"query":[{"key":"page","value":"1","description":"Page number (default 1)","disabled":true},{"key":"per_page","value":"25","description":"Items per page (default 25)","disabled":true}],"variable":[{"key":"receipt","value":"301","description":"Receipt ID"}]},"description":"Returns paginated inventory movements scoped to the given warehouse transfer receipt. Includes movement type, quantity, FIFO layer link, unit/total cost, and product/warehouse references for each movement. Used by the Movements tab on the receipt detail page.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/receipts/{receipt}/inventory-movements?page=1&per_page=25","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","receipts",":receipt","inventory-movements"],"query":[{"key":"page","value":"1","description":"Page number (default 1)","disabled":true},{"key":"per_page","value":"25","description":"Items per page (default 25)","disabled":true}],"variable":[{"key":"receipt","value":"301","description":"Receipt ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 88012,\n      \"type\": \"receipt\",\n      \"inventory_status\": \"available\",\n      \"quantity\": 28,\n      \"reference\": \"WT-2025-0042\",\n      \"link_type\": \"App\\\\Models\\\\WarehouseTransferShipmentReceiptLine\",\n      \"link_id\": 5001,\n      \"parent_link_id\": 301,\n      \"detail_link_id\": null,\n      \"product_id\": 812,\n      \"product_sku\": \"WIDGET-BLU\",\n      \"product_name\": \"Blue Widget\",\n      \"warehouse_id\": 12,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"unit_cost\": 4.5,\n      \"unit_cost_provisional\": false,\n      \"total_cost\": 126,\n      \"layer\": {\n        \"name\": \"FIFO Layer #44021\",\n        \"layer_type\": \"App\\\\Models\\\\FifoLayer\",\n        \"layer_id\": 44021\n      },\n      \"inventory_movement_date\": \"2025-04-13T09:30:00.000000Z\",\n      \"created_at\": \"2025-04-13T09:30:01.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 2,\n  \"from\": 1,\n  \"to\": 2\n}"}]},{"name":"Get Receipt FIFO Layers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/receipts/{receipt}/fifo-layers?page=1&per_page=25","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","receipts",":receipt","fifo-layers"],"query":[{"key":"page","value":"1","description":"Page number (default 1)","disabled":true},{"key":"per_page","value":"25","description":"Items per page (default 25)","disabled":true}],"variable":[{"key":"receipt","value":"301","description":"Receipt ID"}]},"description":"Returns paginated FIFO layers created by the given warehouse transfer receipt. Each layer includes original/available quantity, average unit cost, total cost, polymorphic source link, and product/warehouse references. Used by the FIFO Layers tab on the receipt detail page.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/warehouse-transfers/receipts/{receipt}/fifo-layers?page=1&per_page=25","host":["{{baseUrl}}"],"path":["api","v2","warehouse-transfers","receipts",":receipt","fifo-layers"],"query":[{"key":"page","value":"1","description":"Page number (default 1)","disabled":true},{"key":"per_page","value":"25","description":"Items per page (default 25)","disabled":true}],"variable":[{"key":"receipt","value":"301","description":"Receipt ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 44021,\n      \"fifo_layer_date\": \"2025-04-13T09:30:00.000000Z\",\n      \"quantity\": 28,\n      \"fulfilled_quantity\": 0,\n      \"available_quantity\": 28,\n      \"unit_cost\": 4.5,\n      \"total_cost\": 126,\n      \"product_id\": 812,\n      \"product_sku\": \"WIDGET-BLU\",\n      \"product_name\": \"Blue Widget\",\n      \"warehouse_id\": 12,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"source_type\": \"Warehouse Transfer Receipt Line\",\n      \"source_reference\": \"WT-2025-0042\",\n      \"link_type\": \"App\\\\Models\\\\WarehouseTransferShipmentReceiptLine\",\n      \"link_id\": 5001,\n      \"parent_link_id\": 301,\n      \"detail_link_id\": null,\n      \"condition\": \"new\",\n      \"created_at\": \"2025-04-13T09:30:01.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 2,\n  \"from\": 1,\n  \"to\": 2\n}"}]},{"name":"Receive Warehouse Transfer Shipment","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/receive","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","receive"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]},"description":"Records a receipt against a warehouse transfer, adding the received quantities to stock at the destination warehouse.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: products (required array of {id: warehouse transfer line ID, quantity, shipment_line_id}), shipment_id (the shipment being received), receipt_date (defaults to now), blemished (array of {product_id, sku, quantity, condition, reference} - received units in damaged condition are booked in as a new blemished product with the given SKU), condition and integration_unique_id.\n\nThe transfer must be open.\n\nShipping is handled for you per line: any line in the request that has NOT been dispatched yet has a shipment created for the quantity being received, so a transfer can be received without calling the shipment endpoint first. This applies per line, so it still works when other lines on the same transfer already shipped.\n\nA line that is only PARTLY dispatched is not auto-shipped — a receipt cannot be split across two shipments — so such a line yields only its already-dispatched remainder. Read quantity_shipped and quantity_receivable on the transfer's lines to see what a receipt can take. Requesting a receipt where nothing at all is receivable returns 422 rather than silently recording nothing.\n\nAuto-shipping does not apply to channel-managed destinations (Amazon FBA/AWD, Walmart WFS), whose dispatch and receipt are driven by the channel.\n\nAuthentication: Requires Bearer token.\n\n- `rechain_affected` (optional bool, default false) — when the entry is dated earlier than existing consumption in an affected product/warehouse pool, opt in to re-anchor those later transactions to the oldest available stock (true FIFO) after the save commits. Runs as a background job; pools in a locked accounting period or with unsupported shapes are skipped and reported.\n\n**Serial numbers.** `products[].serials` (optional array of strings) names the serials received on a serialized line. They must be In Transit on this shipment; otherwise the call returns 422. Without them, the oldest serials in transit are received. Received serials become In Stock at the destination. Deleting the receipt puts them back In Transit. Closing the transfer with a receiving discrepancy scraps the serials that never arrived, with the reason \"Lost in transit\".","body":{"mode":"raw","raw":"{\n  \"shipment_id\": 204,\n  \"receipt_date\": \"2026-06-10 09:00:00\",\n  \"products\": [\n    {\n      \"id\": 9001,\n      \"quantity\": 60,\n      \"shipment_line_id\": 3301\n    }\n  ],\n  \"blemished\": [],\n  \"rechain_affected\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/receive","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","receive"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse transfer shipment receipt created successfully.\",\n  \"data\": {\n    \"id\": 512,\n    \"transfer_type\": \"standard\",\n    \"warehouse_transfer_number\": \"WT-000512\",\n    \"transfer_date\": \"2026-06-01T00:00:00.000000Z\",\n    \"from_warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"type\": \"direct\",\n      \"integration_instance_id\": null\n    },\n    \"to_warehouse\": {\n      \"id\": 5,\n      \"name\": \"West Coast 3PL\",\n      \"type\": \"3pl\"\n    },\n    \"status\": \"open\",\n    \"shipment_status\": \"unshipped\",\n    \"shipment_id\": null,\n    \"receipt_status\": \"unreceived\",\n    \"fully_shipped_at\": null,\n    \"fully_received_at\": null,\n    \"eta\": \"2026-06-15\",\n    \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"updated_at\": \"2026-06-02T08:30:00.000000Z\",\n    \"archived_at\": null,\n    \"line_item_count\": 1,\n    \"first_line_sku\": \"WIDGET-001\",\n    \"asn_last_sent_at\": null\n  }\n}"}]},{"name":"Update Warehouse Transfer Receipt","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/receipts/{receipt}","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","receipts",":receipt"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"},{"key":"receipt","value":"31","description":"Receipt ID"}]},"description":"Updates a previously recorded receipt, adjusting received quantities.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: receipt_date (date, not in the future), products (required array of {id: product ID, quantity}) and blemished (array of {product_id, sku, quantity, condition, reference}).\n\nTotal received per product cannot exceed the total shipped quantity, and the transfer must be open.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"receipt_date\": \"2026-06-10 09:00:00\",\n  \"products\": [\n    {\n      \"id\": 101,\n      \"quantity\": 80\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/receipts/{receipt}","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","receipts",":receipt"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"},{"key":"receipt","value":"31","description":"Receipt ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse transfer shipment receipt updated successfully.\"\n}"}]},{"name":"Delete Warehouse Transfer Receipt","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/receipts/{receipt}","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","receipts",":receipt"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"},{"key":"receipt","value":"31","description":"Receipt ID"}]},"description":"Deletes a receipt from the warehouse transfer, removing the received stock from the destination warehouse.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns 422 when the received stock has already been consumed downstream; the response lists the blocking lines. Returns 400 when the transfer is not open.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"Created","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/receipts/{receipt}","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","receipts",":receipt"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"},{"key":"receipt","value":"31","description":"Receipt ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse transfer shipment receipt 31 deleted successfully.\",\n  \"data\": {\n    \"id\": 512,\n    \"transfer_type\": \"standard\",\n    \"warehouse_transfer_number\": \"WT-000512\",\n    \"transfer_date\": \"2026-06-01T00:00:00.000000Z\",\n    \"from_warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"type\": \"direct\",\n      \"integration_instance_id\": null\n    },\n    \"to_warehouse\": {\n      \"id\": 5,\n      \"name\": \"West Coast 3PL\",\n      \"type\": \"3pl\"\n    },\n    \"status\": \"open\",\n    \"shipment_status\": \"unshipped\",\n    \"shipment_id\": null,\n    \"receipt_status\": \"unreceived\",\n    \"fully_shipped_at\": null,\n    \"fully_received_at\": null,\n    \"eta\": \"2026-06-15\",\n    \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"updated_at\": \"2026-06-02T08:30:00.000000Z\",\n    \"archived_at\": null,\n    \"line_item_count\": 1,\n    \"first_line_sku\": \"WIDGET-001\",\n    \"asn_last_sent_at\": null\n  }\n}"}]},{"name":"Open Warehouse Transfer","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/open","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","open"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]},"description":"Opens a draft warehouse transfer, making it active. Opening validates that the transfer has line items and allocates stock at the source warehouse.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nOptionally accepts the same fields as Update Warehouse Transfer to apply changes while opening, plus force_shipment (boolean) to immediately create a shipment for the full quantities.\n\nReturns 400 when the transfer is already open, has no products, or stock is insufficient.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"force_shipment\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/open","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","open"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse transfer opened successfully.\",\n  \"data\": {\n    \"id\": 512,\n    \"transfer_type\": \"standard\",\n    \"warehouse_transfer_number\": \"WT-000512\",\n    \"transfer_date\": \"2026-06-01T00:00:00.000000Z\",\n    \"from_warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"type\": \"direct\",\n      \"integration_instance_id\": null\n    },\n    \"to_warehouse\": {\n      \"id\": 5,\n      \"name\": \"West Coast 3PL\",\n      \"type\": \"3pl\"\n    },\n    \"status\": \"open\",\n    \"shipment_status\": \"unshipped\",\n    \"shipment_id\": null,\n    \"receipt_status\": \"unreceived\",\n    \"fully_shipped_at\": null,\n    \"fully_received_at\": null,\n    \"eta\": \"2026-06-15\",\n    \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"updated_at\": \"2026-06-02T08:30:00.000000Z\",\n    \"archived_at\": null,\n    \"line_item_count\": 1,\n    \"first_line_sku\": \"WIDGET-001\",\n    \"asn_last_sent_at\": null\n  }\n}"}]},{"name":"Revert Warehouse Transfer to Draft","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/draft","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","draft"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]},"description":"Reverts an open warehouse transfer back to draft status, releasing allocated stock.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns 400 when stock received by this transfer has already been consumed downstream, or when an Amazon FBA transfer still has shipments or pending inbound records (delete the shipments first).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"Created","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/draft","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","draft"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse transfer reverted to draft successfully.\",\n  \"data\": {\n    \"id\": 512,\n    \"transfer_type\": \"standard\",\n    \"warehouse_transfer_number\": \"WT-000512\",\n    \"transfer_date\": \"2026-06-01T00:00:00.000000Z\",\n    \"from_warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"type\": \"direct\",\n      \"integration_instance_id\": null\n    },\n    \"to_warehouse\": {\n      \"id\": 5,\n      \"name\": \"West Coast 3PL\",\n      \"type\": \"3pl\"\n    },\n    \"status\": \"open\",\n    \"shipment_status\": \"unshipped\",\n    \"shipment_id\": null,\n    \"receipt_status\": \"unreceived\",\n    \"fully_shipped_at\": null,\n    \"fully_received_at\": null,\n    \"eta\": \"2026-06-15\",\n    \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"updated_at\": \"2026-06-02T08:30:00.000000Z\",\n    \"archived_at\": null,\n    \"line_item_count\": 1,\n    \"first_line_sku\": \"WIDGET-001\",\n    \"asn_last_sent_at\": null\n  }\n}"}]},{"name":"Close Warehouse Transfer Draft","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/close-draft","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","close-draft"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]},"description":"Closes a draft warehouse transfer, marking it closed without moving any stock.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"Created","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/close-draft","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","close-draft"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse transfer closed successfully.\",\n  \"data\": {\n    \"id\": 512,\n    \"transfer_type\": \"standard\",\n    \"warehouse_transfer_number\": \"WT-000512\",\n    \"transfer_date\": \"2026-06-01T00:00:00.000000Z\",\n    \"from_warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"type\": \"direct\",\n      \"integration_instance_id\": null\n    },\n    \"to_warehouse\": {\n      \"id\": 5,\n      \"name\": \"West Coast 3PL\",\n      \"type\": \"3pl\"\n    },\n    \"status\": \"open\",\n    \"shipment_status\": \"unshipped\",\n    \"shipment_id\": null,\n    \"receipt_status\": \"unreceived\",\n    \"fully_shipped_at\": null,\n    \"fully_received_at\": null,\n    \"eta\": \"2026-06-15\",\n    \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"updated_at\": \"2026-06-02T08:30:00.000000Z\",\n    \"archived_at\": null,\n    \"line_item_count\": 1,\n    \"first_line_sku\": \"WIDGET-001\",\n    \"asn_last_sent_at\": null\n  }\n}"}]},{"name":"Mark Warehouse Transfer as Shipped","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/mark-as-shipped","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","mark-as-shipped"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]},"description":"Marks an Amazon FBA inbound warehouse transfer as shipped, recording the shipment with Amazon inbound processing (including shortage handling and status updates).\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nOnly valid for transfers whose destination is an Amazon FBA warehouse with a linked Amazon inbound shipment. Fields: shipment_date (Y-m-d, not in the future) and tracking_number (both optional).\n\nReturns 422 when the transfer is not an FBA transfer or has already been marked as shipped.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"shipment_date\": \"2026-06-05\",\n  \"tracking_number\": \"1Z999AA10123456784\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/mark-as-shipped","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","mark-as-shipped"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Warehouse transfer marked as shipped successfully.\",\n  \"data\": {\n    \"id\": 512,\n    \"transfer_type\": \"standard\",\n    \"warehouse_transfer_number\": \"WT-000512\",\n    \"transfer_date\": \"2026-06-01T00:00:00.000000Z\",\n    \"from_warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"type\": \"direct\",\n      \"integration_instance_id\": null\n    },\n    \"to_warehouse\": {\n      \"id\": 5,\n      \"name\": \"West Coast 3PL\",\n      \"type\": \"3pl\"\n    },\n    \"status\": \"open\",\n    \"shipment_status\": \"unshipped\",\n    \"shipment_id\": null,\n    \"receipt_status\": \"unreceived\",\n    \"fully_shipped_at\": null,\n    \"fully_received_at\": null,\n    \"eta\": \"2026-06-15\",\n    \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"updated_at\": \"2026-06-02T08:30:00.000000Z\",\n    \"archived_at\": null,\n    \"line_item_count\": 1,\n    \"first_line_sku\": \"WIDGET-001\",\n    \"asn_last_sent_at\": null\n  }\n}"}]},{"name":"Update Warehouse Transfer Shipment Details","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/update-shipment","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","update-shipment"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]},"description":"Updates the shipped date and tracking number of an Amazon FBA warehouse transfer after it has been shipped.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: shipment_date (Y-m-d, not in the future) and tracking_number (both optional).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"shipment_date\": \"2026-06-06\",\n  \"tracking_number\": \"1Z999AA10123456784\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/update-shipment","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","update-shipment"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Warehouse transfer shipment details updated successfully.\",\n  \"data\": {\n    \"id\": 512,\n    \"transfer_type\": \"standard\",\n    \"warehouse_transfer_number\": \"WT-000512\",\n    \"transfer_date\": \"2026-06-01T00:00:00.000000Z\",\n    \"from_warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"type\": \"direct\",\n      \"integration_instance_id\": null\n    },\n    \"to_warehouse\": {\n      \"id\": 5,\n      \"name\": \"West Coast 3PL\",\n      \"type\": \"3pl\"\n    },\n    \"status\": \"open\",\n    \"shipment_status\": \"unshipped\",\n    \"shipment_id\": null,\n    \"receipt_status\": \"unreceived\",\n    \"fully_shipped_at\": null,\n    \"fully_received_at\": null,\n    \"eta\": \"2026-06-15\",\n    \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"updated_at\": \"2026-06-02T08:30:00.000000Z\",\n    \"archived_at\": null,\n    \"line_item_count\": 1,\n    \"first_line_sku\": \"WIDGET-001\",\n    \"asn_last_sent_at\": null\n  }\n}"}]},{"name":"Send Warehouse Transfer ASN","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{warehouseTransfer}/asn","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":warehouseTransfer","asn"],"variable":[{"key":"warehouseTransfer","value":"512","description":"Warehouse transfer ID"}]},"description":"Sends (or resends) an Advance Shipment Notice email for the transfer to its destination third-party logistics (3PL) warehouse.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe destination warehouse must be of type 3pl and have an email address on its address record; otherwise a 422 is returned.\n\nAuthentication: Requires Bearer token.\n\nReturns 422 when the order has no product lines — the spreadsheet's columns are derived from its first row, so an order with nothing on it has no notice to send."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{warehouseTransfer}/asn","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":warehouseTransfer","asn"],"variable":[{"key":"warehouseTransfer","value":"512","description":"Warehouse transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"ASN email sent successfully.\"\n}"}]},{"name":"Archive Warehouse Transfer","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/archive","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","archive"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]},"description":"Archives a warehouse transfer, hiding it from active listings.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/archive","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","archive"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse transfer 512 archived successfully.\",\n  \"data\": {\n    \"id\": 512,\n    \"transfer_type\": \"standard\",\n    \"warehouse_transfer_number\": \"WT-000512\",\n    \"transfer_date\": \"2026-06-01T00:00:00.000000Z\",\n    \"from_warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"type\": \"direct\",\n      \"integration_instance_id\": null\n    },\n    \"to_warehouse\": {\n      \"id\": 5,\n      \"name\": \"West Coast 3PL\",\n      \"type\": \"3pl\"\n    },\n    \"status\": \"open\",\n    \"shipment_status\": \"unshipped\",\n    \"shipment_id\": null,\n    \"receipt_status\": \"unreceived\",\n    \"fully_shipped_at\": null,\n    \"fully_received_at\": null,\n    \"eta\": \"2026-06-15\",\n    \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"updated_at\": \"2026-06-02T08:30:00.000000Z\",\n    \"archived_at\": null,\n    \"line_item_count\": 1,\n    \"first_line_sku\": \"WIDGET-001\",\n    \"asn_last_sent_at\": null\n  }\n}"}]},{"name":"Unarchive Warehouse Transfer","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/unarchived","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","unarchived"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]},"description":"Restores an archived warehouse transfer to active status.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/unarchived","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","unarchived"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"warehouse transfer 512 unarchived successfully.\",\n  \"data\": {\n    \"id\": 512,\n    \"transfer_type\": \"standard\",\n    \"warehouse_transfer_number\": \"WT-000512\",\n    \"transfer_date\": \"2026-06-01T00:00:00.000000Z\",\n    \"from_warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"type\": \"direct\",\n      \"integration_instance_id\": null\n    },\n    \"to_warehouse\": {\n      \"id\": 5,\n      \"name\": \"West Coast 3PL\",\n      \"type\": \"3pl\"\n    },\n    \"status\": \"open\",\n    \"shipment_status\": \"unshipped\",\n    \"shipment_id\": null,\n    \"receipt_status\": \"unreceived\",\n    \"fully_shipped_at\": null,\n    \"fully_received_at\": null,\n    \"eta\": \"2026-06-15\",\n    \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"updated_at\": \"2026-06-02T08:30:00.000000Z\",\n    \"archived_at\": null,\n    \"line_item_count\": 1,\n    \"first_line_sku\": \"WIDGET-001\",\n    \"asn_last_sent_at\": null\n  }\n}"}]},{"name":"Bulk Archive Warehouse Transfers","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/archive","host":["{{baseUrl}}"],"path":["api","warehouses","transfers","archive"]},"description":"Archives multiple warehouse transfers. Provide either ids (array of transfer IDs) or apply_to_all=true with an optional filters object to target all transfers matching the current listing filters.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    512,\n    513\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/archive","host":["{{baseUrl}}"],"path":["api","warehouses","transfers","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"totalCount\": 2,\n    \"failCount\": 0\n  },\n  \"message\": \"2 records processed successfully.\"\n}"}]},{"name":"Bulk Unarchive Warehouse Transfers","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/unarchive","host":["{{baseUrl}}"],"path":["api","warehouses","transfers","unarchive"]},"description":"Restores multiple archived warehouse transfers. Provide either ids (array of transfer IDs) or apply_to_all=true with an optional filters object.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    512,\n    513\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/unarchive","host":["{{baseUrl}}"],"path":["api","warehouses","transfers","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"totalCount\": 2,\n    \"failCount\": 0\n  },\n  \"message\": \"2 records processed successfully.\"\n}"}]},{"name":"Check Warehouse Transfers Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/deletable","host":["{{baseUrl}}"],"path":["api","warehouses","transfers","deletable"]},"description":"Checks whether the given warehouse transfers can be deleted. Each result includes deletable (boolean) and reason (why deletion is blocked, or null). A transfer is not deletable when stock it received has already been consumed downstream.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    512,\n    513\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/deletable","host":["{{baseUrl}}"],"path":["api","warehouses","transfers","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 512,\n      \"deletable\": true,\n      \"reason\": null\n    },\n    {\n      \"id\": 513,\n      \"deletable\": false,\n      \"reason\": \"Received stock has been consumed by later movements\"\n    }\n  ]\n}"}]},{"name":"List Warehouse Transfer Notes","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/notes","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","notes"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]},"description":"Returns the transfer's notes with their authors, newest first, paginated.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/notes","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","notes"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 15,\n      \"note\": \"Carrier pickup confirmed for Friday.\",\n      \"is_pinned\": false,\n      \"notable_type\": \"App\\\\Models\\\\WarehouseTransfer\",\n      \"notable_id\": 512,\n      \"user_id\": 4,\n      \"user\": {\n        \"id\": 4,\n        \"name\": \"Jane Smith\"\n      },\n      \"created_at\": \"2026-06-03T15:20:00.000000Z\",\n      \"updated_at\": \"2026-06-03T15:20:00.000000Z\"\n    }\n  ],\n  \"first_page_url\": \"https://example.sku.io/api/warehouses/transfers/512/notes?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://example.sku.io/api/warehouses/transfers/512/notes?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://example.sku.io/api/warehouses/transfers/512/notes\",\n  \"per_page\": 15,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"Add Warehouse Transfer Note","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/notes","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","notes"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]},"description":"Adds a note to the warehouse transfer. Fields: note (required).\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"note\": \"Carrier pickup confirmed for Friday.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/notes","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","notes"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"note created successfully.\",\n  \"data\": {\n    \"id\": 15,\n    \"note\": \"Carrier pickup confirmed for Friday.\",\n    \"is_pinned\": false,\n    \"notable_type\": \"App\\\\Models\\\\WarehouseTransfer\",\n    \"notable_id\": 512,\n    \"user_id\": 4,\n    \"user\": {\n      \"id\": 4,\n      \"name\": \"Jane Smith\"\n    },\n    \"created_at\": \"2026-06-03T15:20:00.000000Z\",\n    \"updated_at\": \"2026-06-03T15:20:00.000000Z\"\n  }\n}"}]},{"name":"Toggle Warehouse Transfer Note Pin","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/notes/{noteId}/toggle-pin","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","notes",":noteId","toggle-pin"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"},{"key":"noteId","value":"15","description":"Note ID"}]},"description":"Pins or unpins a note on the warehouse transfer. Pinned notes surface at the top of the notes list.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/notes/{noteId}/toggle-pin","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","notes",":noteId","toggle-pin"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"},{"key":"noteId","value":"15","description":"Note ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Note pinned\",\n  \"data\": {\n    \"id\": 15,\n    \"note\": \"Carrier pickup confirmed for Friday.\",\n    \"is_pinned\": true,\n    \"notable_type\": \"App\\\\Models\\\\WarehouseTransfer\",\n    \"notable_id\": 512,\n    \"user_id\": 4,\n    \"user\": {\n      \"id\": 4,\n      \"name\": \"Jane Smith\"\n    },\n    \"created_at\": \"2026-06-03T15:20:00.000000Z\",\n    \"updated_at\": \"2026-06-03T15:20:00.000000Z\"\n  }\n}"}]},{"name":"Delete Warehouse Transfer Note","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/notes/{noteId}","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","notes",":noteId"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"},{"key":"noteId","value":"15","description":"Note ID"}]},"description":"Deletes a note from the warehouse transfer.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/notes/{noteId}","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","notes",":noteId"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"},{"key":"noteId","value":"15","description":"Note ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"note 15 deleted successfully.\"\n}"}]},{"name":"Create Warehouse Transfer Bill","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/bills","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","bills"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]},"description":"Creates a supplier bill linked to the warehouse transfer, capturing third-party costs such as freight.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: supplier_id (required), invoice_number (required, unique), invoice_date (required), currency_id, currency_rate, lines (array of {description, quantity, amount, nominal_code_id, tax_rate_id, cost_category_id}), allocations (array of {warehouse_transfer_line_id, allocation_amount, proration_strategy} - must not exceed the bill total) and proration_strategy (required - how unallocated cost is distributed: straight_line, front_loaded, back_loaded, revenue_based, cost_based, weight_based, volume_based, quantity_based, specific_line, manual).\n\nAuthentication: Requires Bearer token.\n\nLine descriptions must be unique within a bill. Two lines on the same bill cannot share a description, and the comparison ignores letter case — \"Freight\" and \"freight\" collide. A duplicate is rejected with 422 before anything is saved.","body":{"mode":"raw","raw":"{\n  \"supplier_id\": 3,\n  \"invoice_number\": \"INV-2026-0142\",\n  \"invoice_date\": \"2026-06-05\",\n  \"currency_id\": 1,\n  \"currency_rate\": 1,\n  \"proration_strategy\": \"quantity_based\",\n  \"lines\": [\n    {\n      \"description\": \"Ocean freight\",\n      \"quantity\": 1,\n      \"amount\": 350,\n      \"nominal_code_id\": 15\n    }\n  ],\n  \"allocations\": [\n    {\n      \"warehouse_transfer_line_id\": 9001,\n      \"allocation_amount\": 350\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/bills","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","bills"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 77,\n    \"supplier_id\": 3,\n    \"invoice_number\": \"INV-2026-0142\",\n    \"invoice_date\": \"2026-06-05\",\n    \"link_type\": \"App\\\\Models\\\\WarehouseTransfer\",\n    \"link_id\": 512,\n    \"currency_id\": 1,\n    \"currency_rate\": 1,\n    \"proration_strategy\": \"quantity_based\",\n    \"total\": 350,\n    \"created_at\": \"2026-06-05T12:00:00.000000Z\",\n    \"updated_at\": \"2026-06-05T12:00:00.000000Z\"\n  }\n}"}]},{"name":"Get Warehouse Transfer Bill","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/bills/{bill}","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","bills",":bill"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"},{"key":"bill","value":"77","description":"Bill ID"}]},"description":"Returns a bill linked to the warehouse transfer. Responds 404 when the bill does not belong to this transfer.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/bills/{bill}","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","bills",":bill"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"},{"key":"bill","value":"77","description":"Bill ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 77,\n    \"supplier_id\": 3,\n    \"invoice_number\": \"INV-2026-0142\",\n    \"invoice_date\": \"2026-06-05\",\n    \"link_type\": \"App\\\\Models\\\\WarehouseTransfer\",\n    \"link_id\": 512,\n    \"currency_id\": 1,\n    \"currency_rate\": 1,\n    \"proration_strategy\": \"quantity_based\",\n    \"total\": 350,\n    \"created_at\": \"2026-06-05T12:00:00.000000Z\",\n    \"updated_at\": \"2026-06-05T12:00:00.000000Z\"\n  }\n}"}]},{"name":"Update Warehouse Transfer Bill","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/bills/{bill}","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","bills",":bill"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"},{"key":"bill","value":"77","description":"Bill ID"}]},"description":"Updates a bill linked to the warehouse transfer.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: invoice_number (required), invoice_date (required), supplier_id (required), currency_id, currency_rate, proration_strategy and allocations (array of {warehouse_transfer_line_id, allocation_amount, proration_strategy} - must not exceed the bill total). Responds 404 when the bill does not belong to this transfer. Omit `currency_id`/`currency_rate` to keep the bill's current currency; send them to change it.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"supplier_id\": 3,\n  \"invoice_number\": \"INV-2026-0142\",\n  \"invoice_date\": \"2026-06-05\",\n  \"currency_id\": 1,\n  \"currency_rate\": 1,\n  \"proration_strategy\": \"quantity_based\",\n  \"allocations\": [\n    {\n      \"warehouse_transfer_line_id\": 9001,\n      \"allocation_amount\": 350\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/warehouses/transfers/{transfer}/bills/{bill}","host":["{{baseUrl}}"],"path":["api","warehouses","transfers",":transfer","bills",":bill"],"variable":[{"key":"transfer","value":"512","description":"Warehouse transfer ID"},{"key":"bill","value":"77","description":"Bill ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 77,\n    \"supplier_id\": 3,\n    \"invoice_number\": \"INV-2026-0142\",\n    \"invoice_date\": \"2026-06-05\",\n    \"link_type\": \"App\\\\Models\\\\WarehouseTransfer\",\n    \"link_id\": 512,\n    \"currency_id\": 1,\n    \"currency_rate\": 1,\n    \"proration_strategy\": \"quantity_based\",\n    \"total\": 350,\n    \"created_at\": \"2026-06-05T12:00:00.000000Z\",\n    \"updated_at\": \"2026-06-05T12:00:00.000000Z\"\n  }\n}"}]},{"name":"List Location Transfers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/location-transfers","host":["{{baseUrl}}"],"path":["api","v2","location-transfers"]},"description":"Paginated history of location transfers (put-aways and relocations).\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nSearch: filter[search] matches transfer number, notes, warehouse name, and performing user's name; narrow with search_columns.\n\nOperator-aware filters: filter[number], filter[notes] (text), filter[id] (numeric), filter[completed_at], filter[created_at] (datetime). Each supports a bare implicit-is form (filter[number]=LT-000012) and an operator form (filter[number.contains]=LT). Text operators: is, is_not, contains, does_not_contain, starts_with, does_not_start_with, ends_with, does_not_end_with, is_one_of, is_not_one_of, is_empty, is_not_empty. Numeric operators: is, is_not, greater_than, less_than, greater_than_or_equal, less_than_or_equal, between, is_one_of, is_not_one_of, is_empty, is_not_empty. Datetime operators: is, is_not, before, after, on_or_before, on_or_after, between, is_empty, is_not_empty.\n\nExact filters: filter[warehouse_id], filter[reason] (put_away | relocation), filter[product_id] (matches transfers containing a line for the product), filter[performed_by_user_id], filter[date_from] / filter[date_to] (date range on completion date, Y-m-d).\n\nfilter_groups accepts a base64-encoded JSON tree of AND/OR advanced filter groups and takes precedence over individual filter params.\n\nAllowed sorts: id, number, reason, completed_at, created_at (default -completed_at).\n\nEach row includes the warehouse, the performing user, and a lines_count. Line detail is returned by the single-transfer endpoint."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/location-transfers","host":["{{baseUrl}}"],"path":["api","v2","location-transfers"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 12,\n      \"number\": \"LT-000012\",\n      \"warehouse_id\": 1,\n      \"warehouse\": {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      },\n      \"reason\": \"put_away\",\n      \"reason_label\": \"Put Away\",\n      \"notes\": \"Morning put-away from receiving dock\",\n      \"performed_by\": {\n        \"id\": 5,\n        \"name\": \"Jane Smith\"\n      },\n      \"completed_at\": \"2026-07-07T15:04:12.000000Z\",\n      \"lines_count\": 2,\n      \"created_at\": \"2026-07-07T15:04:12.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/v2/location-transfers?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/v2/location-transfers?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"{{protocol}}{{domain}}/api/v2/location-transfers\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"Create Location Transfer","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/location-transfers","host":["{{baseUrl}}"],"path":["api","v2","location-transfers"]},"description":"Execute a location transfer atomically: the document is created, completed, and paired reclassification inventory movements are written per line (-quantity at the from-location, +quantity at the to-location) in a single transaction. Location transfers never affect costing or warehouse-level availability.\n\n**Required scope: `warehouses:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nRules:\n- warehouse_id (required, integer): warehouse with locations enabled — returns 422 otherwise\n- reason (required, string): put_away or relocation\n- notes (optional, string)\n- lines (required, array, min 1)\n- lines[].product_id (required, integer)\n- lines[].from_warehouse_location_id (required, integer): must belong to the warehouse\n- lines[].to_warehouse_location_id (required, integer): must belong to the warehouse, differ from the from-location, and not be deprecated\n- lines[].quantity (required, number, > 0): the from-location balance must cover the total requested quantity across all lines drawing from the same product and location — returns 422 naming the available quantity otherwise\n\n**Requires permission:** `inventory.transfer`","body":{"mode":"raw","raw":"{\n  \"warehouse_id\": 1,\n  \"reason\": \"put_away\",\n  \"notes\": \"Morning put-away from receiving dock\",\n  \"lines\": [\n    {\n      \"product_id\": 42,\n      \"from_warehouse_location_id\": 7,\n      \"to_warehouse_location_id\": 15,\n      \"quantity\": 100\n    },\n    {\n      \"product_id\": 58,\n      \"from_warehouse_location_id\": 7,\n      \"to_warehouse_location_id\": 18,\n      \"quantity\": 24\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/location-transfers","host":["{{baseUrl}}"],"path":["api","v2","location-transfers"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"number\": \"LT-000012\",\n    \"warehouse_id\": 1,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"reason\": \"put_away\",\n    \"reason_label\": \"Put Away\",\n    \"notes\": \"Morning put-away from receiving dock\",\n    \"performed_by\": {\n      \"id\": 5,\n      \"name\": \"Jane Smith\"\n    },\n    \"completed_at\": \"2026-07-07T15:04:12.000000Z\",\n    \"lines_count\": 2,\n    \"lines\": [\n      {\n        \"id\": 31,\n        \"product_id\": 42,\n        \"product\": {\n          \"id\": 42,\n          \"sku\": \"SKU-ABC-123\",\n          \"name\": \"Blue Widget 500ml\"\n        },\n        \"from_location\": {\n          \"id\": 7,\n          \"code\": \"RECEIVING\",\n          \"name\": \"Receiving\",\n          \"type\": \"staging\",\n          \"type_label\": \"Staging\"\n        },\n        \"to_location\": {\n          \"id\": 15,\n          \"code\": \"A-01-2-B\",\n          \"name\": null,\n          \"type\": \"storage\",\n          \"type_label\": \"Storage\"\n        },\n        \"quantity\": 100\n      },\n      {\n        \"id\": 32,\n        \"product_id\": 58,\n        \"product\": {\n          \"id\": 58,\n          \"sku\": \"SKU-DEF-456\",\n          \"name\": \"Red Widget 250ml\"\n        },\n        \"from_location\": {\n          \"id\": 7,\n          \"code\": \"RECEIVING\",\n          \"name\": \"Receiving\",\n          \"type\": \"staging\",\n          \"type_label\": \"Staging\"\n        },\n        \"to_location\": {\n          \"id\": 18,\n          \"code\": \"A-02-1-A\",\n          \"name\": null,\n          \"type\": \"storage\",\n          \"type_label\": \"Storage\"\n        },\n        \"quantity\": 24\n      }\n    ],\n    \"created_at\": \"2026-07-07T15:04:12.000000Z\"\n  },\n  \"message\": \"Location transfer LT-000012 completed.\"\n}"}]},{"name":"Get Location Transfer","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/location-transfers/{locationTransfer}","host":["{{baseUrl}}"],"path":["api","v2","location-transfers",":locationTransfer"],"variable":[{"key":"locationTransfer","value":"12","description":"Location transfer ID"}]},"description":"Retrieve a single location transfer with its lines. Each line includes the product and the from/to locations.\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/location-transfers/{locationTransfer}","host":["{{baseUrl}}"],"path":["api","v2","location-transfers",":locationTransfer"],"variable":[{"key":"locationTransfer","value":"12","description":"Location transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"number\": \"LT-000012\",\n    \"warehouse_id\": 1,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"reason\": \"put_away\",\n    \"reason_label\": \"Put Away\",\n    \"notes\": \"Morning put-away from receiving dock\",\n    \"performed_by\": {\n      \"id\": 5,\n      \"name\": \"Jane Smith\"\n    },\n    \"completed_at\": \"2026-07-07T15:04:12.000000Z\",\n    \"lines_count\": 2,\n    \"lines\": [\n      {\n        \"id\": 31,\n        \"product_id\": 42,\n        \"product\": {\n          \"id\": 42,\n          \"sku\": \"SKU-ABC-123\",\n          \"name\": \"Blue Widget 500ml\"\n        },\n        \"from_location\": {\n          \"id\": 7,\n          \"code\": \"RECEIVING\",\n          \"name\": \"Receiving\",\n          \"type\": \"staging\",\n          \"type_label\": \"Staging\"\n        },\n        \"to_location\": {\n          \"id\": 15,\n          \"code\": \"A-01-2-B\",\n          \"name\": null,\n          \"type\": \"storage\",\n          \"type_label\": \"Storage\"\n        },\n        \"quantity\": 100\n      },\n      {\n        \"id\": 32,\n        \"product_id\": 58,\n        \"product\": {\n          \"id\": 58,\n          \"sku\": \"SKU-DEF-456\",\n          \"name\": \"Red Widget 250ml\"\n        },\n        \"from_location\": {\n          \"id\": 7,\n          \"code\": \"RECEIVING\",\n          \"name\": \"Receiving\",\n          \"type\": \"staging\",\n          \"type_label\": \"Staging\"\n        },\n        \"to_location\": {\n          \"id\": 18,\n          \"code\": \"A-02-1-A\",\n          \"name\": null,\n          \"type\": \"storage\",\n          \"type_label\": \"Storage\"\n        },\n        \"quantity\": 24\n      }\n    ],\n    \"created_at\": \"2026-07-07T15:04:12.000000Z\"\n  }\n}"}]},{"name":"Get Put-Away Queue","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/put-away/queue","host":["{{baseUrl}}"],"path":["api","v2","put-away","queue"]},"description":"Paginated put-away queue: stock with a positive balance sitting in put-away-from locations (staging, returns, quarantine, damaged) — plus the Unassigned location when include_unassigned is truthy (default). Each row is decorated with the product's suggested destination bin (its preferred default storage location for the warehouse, when one is set and not deprecated).\n\n**Required scope: `warehouses:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nSearch: filter[search] matches product SKU, product name, and location code; narrow with search_columns.\n\nOperator-aware filters: filter[sku], filter[name], filter[location_code] (text) and filter[quantity] (numeric). Each supports a bare implicit-is form and an operator form (filter[quantity.greater_than]=50). Text operators: is, is_not, contains, does_not_contain, starts_with, does_not_start_with, ends_with, does_not_end_with, is_one_of, is_not_one_of, is_empty, is_not_empty. Numeric operators: is, is_not, greater_than, less_than, greater_than_or_equal, less_than_or_equal, between, is_one_of, is_not_one_of, is_empty, is_not_empty.\n\nfilter[warehouse_id] filters to one warehouse (exact match). include_unassigned toggles the Unassigned location. filter_groups accepts a base64-encoded JSON tree of AND/OR advanced filter groups and takes precedence over individual filter params.\n\nAllowed sorts: product (SKU), location (code), quantity. Default order: location code, then product SKU.\n\nCreate a location transfer with reason put_away to work rows off the queue."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/put-away/queue","host":["{{baseUrl}}"],"path":["api","v2","put-away","queue"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 42,\n      \"product\": {\n        \"id\": 42,\n        \"sku\": \"SKU-ABC-123\",\n        \"name\": \"Blue Widget 500ml\",\n        \"barcode\": \"8100000000031\"\n      },\n      \"warehouse_id\": 1,\n      \"warehouse_location_id\": 7,\n      \"location\": {\n        \"id\": 7,\n        \"code\": \"RECEIVING\",\n        \"name\": \"Receiving\",\n        \"type\": \"staging\",\n        \"type_label\": \"Staging\"\n      },\n      \"quantity\": 100,\n      \"suggested_location\": {\n        \"id\": 15,\n        \"code\": \"A-01-2-B\"\n      }\n    },\n    {\n      \"product_id\": 77,\n      \"product\": {\n        \"id\": 77,\n        \"sku\": \"SKU-GHI-789\",\n        \"name\": \"Green Gadget XL\",\n        \"barcode\": \"8100000000031\"\n      },\n      \"warehouse_id\": 1,\n      \"warehouse_location_id\": 2,\n      \"location\": {\n        \"id\": 2,\n        \"code\": \"UNASSIGNED\",\n        \"name\": \"Unassigned\",\n        \"type\": \"storage\",\n        \"type_label\": \"Storage\"\n      },\n      \"quantity\": 950,\n      \"suggested_location\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/v2/put-away/queue?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/v2/put-away/queue?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"{{protocol}}{{domain}}/api/v2/put-away/queue\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 2,\n  \"total\": 2\n}"}]}]},{"name":"Stock Takes","item":[{"name":"List Stock Takes","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/list?filter[status]=open&filter[mode]=full_count&filter[warehouse]=1&filter[archived]=none&filter[variance_direction]=positive&filter[is_initial_count]=&filter[date_count]=&filter[value_change]=&filter[notes]=&filter[id]=&search=&sort=-id&per_page=10&page=1","host":["{{baseUrl}}"],"path":["api","stock-takes","list"],"query":[{"key":"filter[status]","value":"open","description":"Optional. Filter by status: draft, open, closed.","disabled":true},{"key":"filter[mode]","value":"full_count","description":"Optional. Filter by mode: full_count, adjustment.","disabled":true},{"key":"filter[warehouse]","value":"1","description":"Optional. Filter by warehouse ID.","disabled":true},{"key":"filter[archived]","value":"none","description":"Optional. archived filter: none (default, non-archived only), only (archived only), all (all records).","disabled":true},{"key":"filter[variance_direction]","value":"positive","description":"Optional. Filter by variance direction: positive, negative, neutral.","disabled":true},{"key":"filter[is_initial_count]","value":"","description":"Optional. Filter by initial count (1 or 0).","disabled":true},{"key":"filter[date_count]","value":"","description":"Optional. Filter by count date.","disabled":true},{"key":"filter[value_change]","value":"","description":"Optional. Filter by value change amount.","disabled":true},{"key":"filter[notes]","value":"","description":"Optional. Filter by notes (partial match).","disabled":true},{"key":"filter[id]","value":"","description":"Optional. Filter by ID.","disabled":true},{"key":"search","value":"","description":"Optional. Search by ID, notes, or warehouse name.","disabled":true},{"key":"sort","value":"-id","description":"Optional. Sort field (prefix - for descending). Allowed: id, date_count, status, value_change, variance_direction, created_at, updated_at.","disabled":true},{"key":"per_page","value":"10","description":"Optional. Items per page (default 10).","disabled":true},{"key":"page","value":"1","description":"Optional. Page number.","disabled":true}]},"description":"Get paginated stock takes with filtering, sorting and search. Uses Spatie QueryBuilder + GroupedFilterSupport.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFilter columns available: id, status, mode, warehouse (ID), is_initial_count, variance_direction, notes, date_count, created_at, updated_at, value_change.\n\nArchived filter: none=non-archived only (default), only=archived only, all=all records.\n\nAllowed sorts: id, date_count, status, value_change, variance_direction, created_at, updated_at. Default sort: -id.\n\n**Requires permission:** `inventory.count`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/stock-takes/list?filter[status]=open&filter[mode]=full_count&filter[warehouse]=1&filter[archived]=none&filter[variance_direction]=positive&filter[is_initial_count]=&filter[date_count]=&filter[value_change]=&filter[notes]=&filter[id]=&search=&sort=-id&per_page=10&page=1","host":["{{baseUrl}}"],"path":["api","stock-takes","list"],"query":[{"key":"filter[status]","value":"open","description":"Optional. Filter by status: draft, open, closed.","disabled":true},{"key":"filter[mode]","value":"full_count","description":"Optional. Filter by mode: full_count, adjustment.","disabled":true},{"key":"filter[warehouse]","value":"1","description":"Optional. Filter by warehouse ID.","disabled":true},{"key":"filter[archived]","value":"none","description":"Optional. archived filter: none (default, non-archived only), only (archived only), all (all records).","disabled":true},{"key":"filter[variance_direction]","value":"positive","description":"Optional. Filter by variance direction: positive, negative, neutral.","disabled":true},{"key":"filter[is_initial_count]","value":"","description":"Optional. Filter by initial count (1 or 0).","disabled":true},{"key":"filter[date_count]","value":"","description":"Optional. Filter by count date.","disabled":true},{"key":"filter[value_change]","value":"","description":"Optional. Filter by value change amount.","disabled":true},{"key":"filter[notes]","value":"","description":"Optional. Filter by notes (partial match).","disabled":true},{"key":"filter[id]","value":"","description":"Optional. Filter by ID.","disabled":true},{"key":"search","value":"","description":"Optional. Search by ID, notes, or warehouse name.","disabled":true},{"key":"sort","value":"-id","description":"Optional. Sort field (prefix - for descending). Allowed: id, date_count, status, value_change, variance_direction, created_at, updated_at.","disabled":true},{"key":"per_page","value":"10","description":"Optional. Items per page (default 10).","disabled":true},{"key":"page","value":"1","description":"Optional. Page number.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 42,\n      \"date_count\": \"2024-10-10T00:00:00+00:00\",\n      \"is_initial_count\": false,\n      \"condition\": null,\n      \"warehouse\": {\n        \"id\": 2,\n        \"name\": \"Main Warehouse\",\n        \"type\": \"standard\",\n        \"country_code\": \"US\"\n      },\n      \"mode\": \"full_count\",\n      \"status\": \"open\",\n      \"value_change\": null,\n      \"variance_direction\": null,\n      \"notes\": \"Q4 annual count\",\n      \"created_at\": \"2024-10-08T09:00:00+00:00\",\n      \"updated_at\": \"2024-10-10T14:00:00+00:00\",\n      \"archived_at\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 3,\n  \"per_page\": 10,\n  \"total\": 28\n}"}]},{"name":"Search Stock Takes","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/list","host":["{{baseUrl}}"],"path":["api","stock-takes","list"]},"description":"Returns the same paginated list of stock takes as the GET version of this endpoint, but accepts filters as a JSON request body instead of the query string. Use this POST variant when the filter set is too large or too complex to fit in a URL.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nProvide advanced filters under `filter_groups`, a tree of conditions combined with a `conjunction` of `and` or `or`. Each condition names a `column`, an `operator`, and a `value`. A free-text `search` term matches against the stock take id, notes, and warehouse name.\n\nText columns (`status`, `mode`, `variance_direction`, `notes`, `warehouse`) accept: is, is_not, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, is_one_of, is_not_one_of.\nNumeric, boolean and date columns (`id`, `value_change`, `is_initial_count`, `date_count`, `created_at`, `updated_at`, `archived`) accept: is, is_not, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between, is_empty, is_not_empty.\n\nSortable columns (via `sort`, prefix with `-` for descending): id, date_count, status, value_change, variance_direction, created_at, updated_at. Results default to `-id`.","body":{"mode":"raw","raw":"{\n  \"per_page\": 25,\n  \"page\": 1,\n  \"sort\": \"-date_count\",\n  \"search\": \"\",\n  \"filter_groups\": {\n    \"conjunction\": \"and\",\n    \"children\": [\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"status\",\n          \"operator\": \"is\",\n          \"value\": \"completed\"\n        }\n      },\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"variance_direction\",\n          \"operator\": \"is\",\n          \"value\": \"negative\"\n        }\n      }\n    ]\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/list","host":["{{baseUrl}}"],"path":["api","stock-takes","list"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 812,\n      \"date_count\": \"2026-06-28\",\n      \"is_initial_count\": false,\n      \"condition\": \"new\",\n      \"warehouse\": {\n        \"id\": 2,\n        \"name\": \"Main Warehouse\",\n        \"type\": \"standard\",\n        \"country_code\": \"US\"\n      },\n      \"mode\": \"full\",\n      \"status\": \"completed\",\n      \"value_change\": -184.5,\n      \"variance_direction\": \"negative\",\n      \"notes\": \"Quarterly cycle count\",\n      \"created_at\": \"2026-06-28T09:12:00.000000Z\",\n      \"updated_at\": \"2026-06-28T15:41:00.000000Z\",\n      \"archived_at\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"per_page\": 25,\n  \"from\": 1,\n  \"to\": 1,\n  \"last_page\": 4,\n  \"total\": 92,\n  \"next_page_url\": \"https://api.example.com/api/stock-takes/list?page=2\",\n  \"prev_page_url\": null\n}"}]},{"name":"Get Stock Take","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stock_take}","host":["{{baseUrl}}"],"path":["api","stock-takes",":stock_take"],"variable":[{"key":"stock_take","value":"42","description":"Stock take ID"}]},"description":"Get a single stock take with full details including items, warehouse, parent and child stock takes.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nLoads: warehouse, stockTakeItems (with inventoryMovements.fifoLayer for closed takes), parentStockTake, childStockTakes.\n\nResponse includes `deletable` (boolean) and `delete_blocked_reasons` (string[]): a draft/open stock take is always deletable; a closed stock take is deletable only when none of its FIFO cost layers have been consumed (otherwise `deletable` is false and `delete_blocked_reasons` lists the blocking stock take items).\n\nThe stock take exposes `is_blind`. For a blind count, items that have not yet been counted return null `snapshot_inventory` / `snapshot_available` / `snapshot_reserved` / `snapshot_committed_to_fulfillment` until they are counted. Each item also exposes its `variance_reason` (root-cause code, or null).\n\nOn a closed take each item also returns `item_realized_unit_cost`: the per-unit cost the count actually realized, derived from the inventory movements it wrote. Negative (removal) lines report the weighted average cost of the FIFO layers they consumed; positive (addition) lines report the cost of the layer they opened. It is stated on the same basis as the FIFO layer's own COGS per unit — the cost as booked, plus every cost change recognized against that layer since — so a landed cost or other correction back-dated behind the count is reflected here, in the layer and in inventory valuation alike. `item_unit_cost` is the cost entered on the line (null on removal lines) and does not move.\n\n**Serial fields.** Each item includes `tracks_serials` (boolean), `serial_generation_mode` (`none`, `manual` or `auto`) and `serials` (the serials of the found or missing units, applied at finalize)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/stock-takes/{stock_take}","host":["{{baseUrl}}"],"path":["api","stock-takes",":stock_take"],"variable":[{"key":"stock_take","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"date_count\": \"2024-10-10T00:00:00+00:00\",\n    \"is_initial_count\": false,\n    \"is_blind\": false,\n    \"condition\": null,\n    \"warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\",\n      \"type\": \"standard\",\n      \"country_code\": \"US\"\n    },\n    \"mode\": \"full_count\",\n    \"status\": \"closed\",\n    \"deletable\": true,\n    \"delete_blocked_reasons\": [],\n    \"value_change\": -266.94,\n    \"variance_direction\": \"negative\",\n    \"notes\": \"Q4 annual count\",\n    \"parent_stock_take\": null,\n    \"child_stock_takes\": [],\n    \"items\": [\n      {\n        \"id\": 101,\n        \"item_product_id\": 55,\n        \"item_sku\": \"PROD-55\",\n        \"item_name\": \"Widget\",\n        \"warehouse_id\": 2,\n        \"snapshot_inventory\": 100,\n        \"qty_counted\": 98,\n        \"variance_reason\": \"shrinkage\",\n        \"item_unit_cost\": 12.5,\n        \"item_realized_unit_cost\": 13.347,\n        \"is_lot_tracked\": true,\n        \"lot_tracking_method\": \"fefo\",\n        \"shelf_life_days\": 365,\n        \"near_expiry_threshold_days\": 30,\n        \"min_remaining_shelf_life_days\": 90,\n        \"lot_allocation\": [\n          {\n            \"fifo_layer_id\": 7101,\n            \"quantity\": 2\n          }\n        ],\n        \"lots\": [\n          {\n            \"fifo_layer_id\": 7101,\n            \"batch_number\": \"LOT-2026-0042\",\n            \"expiry_date\": \"2026-07-04\",\n            \"manufacture_date\": \"2026-01-04\",\n            \"lot_notes\": null,\n            \"quantity\": 50\n          }\n        ]\n      }\n    ],\n    \"during_count_movement_count\": 3,\n    \"created_at\": \"2024-10-08T09:00:00+00:00\",\n    \"updated_at\": \"2024-10-10T14:00:00+00:00\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Update Stock Take","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stock_take}","host":["{{baseUrl}}"],"path":["api","stock-takes",":stock_take"],"variable":[{"key":"stock_take","value":"42","description":"Stock take ID"}]},"description":"Update a stock take and its line items. Handles adding, updating, and removing items.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nitems: each item accepts product_id (required, must exist), qty_counted (numeric, min 0), unit_cost (optional numeric, min 0 — an explicit 0 is valid and preserved), to_delete (boolean, removes the line).\n\nitems[].lots (array, optional): lot/batch rows for newly found (POSITIVE variance) stock on a lot-tracked product — each row becomes its own FIFO layer. For lot-tracked products every supplied lot must carry batch_number and expiry_date. Fields: batch_number, expiry_date, manufacture_date, quantity, lot_notes.\n\nitems[].lot_allocation (array, optional): per-item FEFO override for a NEGATIVE variance (a consume — counted qty lower than the system snapshot) on a lot-tracked product. When supplied, the named FIFO layers are drawn down at finalize instead of automatic FEFO/FIFO; when omitted the consume falls back to automatic FEFO exactly as before. Each row is `{ fifo_layer_id (integer, must exist in fifo_layers), quantity (numeric, > 0) }`, and the allocation quantities must sum to abs(variance) (the snapshot − counted shortfall). The tenant setting `inventory.lot_override_policy` (off | warn | enforce) governs the override — under enforce an allocation selecting expired or non-FEFO (out-of-order) layers is rejected.\n\nitems[].serials (array of strings, optional): serials for a serialized product. On a found (positive) variance they are the serials of the extra units and are received at finalize; an Auto-generation product generates any not entered. On a shortfall (negative) variance they are the serials of the missing units, which are set to Scrapped with the reason \"Stock take shortfall\"; without them the oldest serials in stock at the warehouse are scrapped. A supplied list overwrites the item's serials, and omitting it keeps them. Reversing the stock take undoes both. Returned on each item as `serials`.\n\ndate_count (date, optional): re-dating a finalized (closed) stock take moves its initial positive inventory movement and FIFO cost-layer date; on the next accounting sync this also re-dates the journal entry (TxnDate). Initial counts cannot be re-dated.\n\nreallocate_usages (boolean, optional): when true AND the date changed, dispatches a tracked FIFO reallocation that replays historical consumption against the re-ordered layers so each past usage consumes the correct layers in FIFO order, recomputes COGS, and propagates it to documents and accounting. When dispatched, the response includes `reallocation_tracked_job_id` to follow via the tracked job log endpoints. Preview the impact first via the reallocation-preview endpoint.\n\nReturns 400 if: the stock take is closed and the request attempts to change quantities; an adjustment-mode item cannot be modified; the new (or current) date_count falls on or before the accounting lock date; or the reallocation would change COGS on a consumption dated inside a locked accounting period.\n\nResponse includes `deletable` (boolean) and `delete_blocked_reasons` (string[]).\n\n**Requires permission:** `inventory.count`\n\nEach item may carry `variance_reason` — a root-cause code for the count variance. One of: receiving_error, picking_error, damage, mislabel, shrinkage, unknown.","body":{"mode":"raw","raw":"{\n  \"notes\": \"Updated notes for Q4 count\",\n  \"date_count\": \"2024-10-11\",\n  \"reallocate_usages\": true,\n  \"items\": [\n    {\n      \"product_id\": 55,\n      \"qty_counted\": 99,\n      \"snapshot_inventory\": 100,\n      \"unit_cost\": 12.5,\n      \"lots\": [\n        {\n          \"batch_number\": \"LOT-2026-0042\",\n          \"manufacture_date\": \"2026-01-04\",\n          \"expiry_date\": \"2026-07-04\",\n          \"quantity\": 98,\n          \"lot_notes\": \"Newly found lot\"\n        }\n      ]\n    },\n    {\n      \"product_id\": 56,\n      \"qty_counted\": 25,\n      \"snapshot_inventory\": 25\n    },\n    {\n      \"product_id\": 57,\n      \"qty_counted\": 40,\n      \"snapshot_inventory\": 45,\n      \"lot_allocation\": [\n        {\n          \"fifo_layer_id\": 7101,\n          \"quantity\": 3\n        },\n        {\n          \"fifo_layer_id\": 7102,\n          \"quantity\": 2\n        }\n      ],\n      \"variance_reason\": \"damage\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/stock-takes/{stock_take}","host":["{{baseUrl}}"],"path":["api","stock-takes",":stock_take"],"variable":[{"key":"stock_take","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"notes\": \"Updated notes for Q4 count\",\n    \"status\": \"draft\",\n    \"deletable\": true,\n    \"delete_blocked_reasons\": [],\n    \"items\": [\n      {\n        \"id\": 103,\n        \"item_product_id\": 57,\n        \"item_sku\": \"PROD-57\",\n        \"warehouse_id\": 2,\n        \"snapshot_inventory\": 45,\n        \"qty_counted\": 40,\n        \"variance_reason\": \"damage\",\n        \"is_lot_tracked\": true,\n        \"lot_tracking_method\": \"fefo\",\n        \"lot_allocation\": [\n          {\n            \"fifo_layer_id\": 7101,\n            \"quantity\": 3\n          },\n          {\n            \"fifo_layer_id\": 7102,\n            \"quantity\": 2\n          }\n        ]\n      }\n    ],\n    \"updated_at\": \"2024-10-09T11:00:00+00:00\"\n  }\n}"}]},{"name":"Delete Stock Take","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stock_take}","host":["{{baseUrl}}"],"path":["api","stock-takes",":stock_take"],"variable":[{"key":"stock_take","value":"43","description":"Stock take ID"}]},"description":"Delete a single stock take.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 400 if the stock take has linked inventory adjustments or FIFO layers that prevent deletion.\n\n**Requires permission:** `inventory.count`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/stock-takes/{stock_take}","host":["{{baseUrl}}"],"path":["api","stock-takes",":stock_take"],"variable":[{"key":"stock_take","value":"43","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"stock take 43 deleted successfully\"\n}"}]},{"name":"Search Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/search-products?q=PROD&warehouse_id=2&in_stock_only=0&limit=50","host":["{{baseUrl}}"],"path":["api","stock-takes","search-products"],"query":[{"key":"q","value":"PROD","description":"Optional. Search term (matches SKU, name, barcode).","disabled":true},{"key":"warehouse_id","value":"2","description":"Optional. Warehouse ID to load inventory data for.","disabled":true},{"key":"in_stock_only","value":"0","description":"Optional. 0=all products, 1=only products with stock in warehouse.","disabled":true},{"key":"limit","value":"50","description":"Optional. Max results (1-50, default 50).","disabled":true}]},"description":"Search products for adding to a stock take. Returns standard and kit products only (excludes archived).\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nResults are ordered by exact SKU match, then SKU prefix, then barcode, then name.\n\nIf warehouse_id is provided, inventory levels for that warehouse are included."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/stock-takes/search-products?q=PROD&warehouse_id=2&in_stock_only=0&limit=50","host":["{{baseUrl}}"],"path":["api","stock-takes","search-products"],"query":[{"key":"q","value":"PROD","description":"Optional. Search term (matches SKU, name, barcode).","disabled":true},{"key":"warehouse_id","value":"2","description":"Optional. Warehouse ID to load inventory data for.","disabled":true},{"key":"in_stock_only","value":"0","description":"Optional. 0=all products, 1=only products with stock in warehouse.","disabled":true},{"key":"limit","value":"50","description":"Optional. Max results (1-50, default 50).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 55,\n      \"sku\": \"PROD-001\",\n      \"name\": \"Widget A\",\n      \"barcode\": \"123456789\",\n      \"type\": \"standard\",\n      \"unit_cost\": 12.5,\n      \"inventory_available\": 95,\n      \"inventory_allocated\": 5,\n      \"inventory_on_hold\": 0,\n      \"inventory_total\": 100\n    }\n  ]\n}"}]},{"name":"Resolve SKUs","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/resolve-skus","host":["{{baseUrl}}"],"path":["api","stock-takes","resolve-skus"]},"description":"Bulk resolve a list of SKUs or barcodes to products in a single query. Used for importing large stock count sheets.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nMatches against both SKU and barcode columns (case-insensitive). Deduplicates results.\n\nMax 5000 SKUs per request.\n\nReturns found, not_found, and skipped (wrong product type) arrays.\n\n**Requires permission:** `inventory.count`","body":{"mode":"raw","raw":"{\n  \"skus\": [\n    \"PROD-001\",\n    \"PROD-002\",\n    \"123456789\"\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/resolve-skus","host":["{{baseUrl}}"],"path":["api","stock-takes","resolve-skus"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"found\": [\n    {\n      \"id\": 55,\n      \"sku\": \"PROD-001\",\n      \"name\": \"Widget A\",\n      \"barcode\": \"111111111\",\n      \"type\": \"standard\",\n      \"unit_cost\": 12.5,\n      \"case_quantity\": 12,\n      \"input_sku\": \"PROD-001\",\n      \"is_lot_tracked\": true,\n      \"lot_tracking_method\": \"fifo\",\n      \"shelf_life_days\": 180,\n      \"near_expiry_threshold_days\": 60,\n      \"min_remaining_shelf_life_days\": 105\n    }\n  ],\n  \"not_found\": [\n    \"PROD-999\"\n  ],\n  \"skipped\": []\n}"}]},{"name":"Create Stock Take","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes","host":["{{baseUrl}}"],"path":["api","stock-takes"]},"description":"Create a new stock take.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nstatus: defaults to draft if not provided.\n\ndate_count: defaults to today if not provided.\n\nmode: full_count (default) or adjustment.\n\nis_initial_count: marks this as the first ever count for the warehouse.\n\ncondition: optional condition label (e.g., 'new', 'used').\n\nitems: optional array of stock take line items. Can be added later. Each item: product_id (required, must exist), qty_counted (numeric, min 0), unit_cost (optional numeric, min 0 — an explicit 0 is valid and preserved, it does not fall back to the existing cost).\n\nwarehouse_id must be a standard (non-virtual) warehouse.\n\nResponse includes `deletable` (boolean) and `delete_blocked_reasons` (string[]): a draft/open stock take is always deletable; a closed stock take is deletable only when none of its FIFO cost layers have been consumed (otherwise `deletable` is false and `delete_blocked_reasons` lists the blocking stock take items).\n\n**Requires permission:** `inventory.count`\n\n`is_blind` (optional boolean, default false): a blind cycle count hides the system on-hand for items that have not yet been counted — their `snapshot_inventory`, `snapshot_available`, `snapshot_reserved`, and `snapshot_committed_to_fulfillment` are returned as null so the counter records what they find without anchoring to the expected quantity; once an item is counted the snapshots are revealed for variance review.\n\nEach item may also carry `variance_reason` — a root-cause code for why the counted quantity differs from the system on-hand. One of: receiving_error, picking_error, damage, mislabel, shrinkage, unknown.","body":{"mode":"raw","raw":"{\n  \"warehouse_id\": 2,\n  \"status\": \"draft\",\n  \"date_count\": \"2024-10-10\",\n  \"mode\": \"full_count\",\n  \"is_initial_count\": false,\n  \"is_blind\": false,\n  \"condition\": null,\n  \"notes\": \"Q4 annual count\",\n  \"items\": [\n    {\n      \"product_id\": 55,\n      \"qty_counted\": 98,\n      \"snapshot_inventory\": 100,\n      \"unit_cost\": 12.5,\n      \"lots\": [\n        {\n          \"batch_number\": \"LOT-2026-0042\",\n          \"manufacture_date\": \"2026-01-04\",\n          \"expiry_date\": \"2026-07-04\",\n          \"quantity\": 98,\n          \"lot_notes\": \"Newly found lot\"\n        }\n      ],\n      \"variance_reason\": \"shrinkage\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes","host":["{{baseUrl}}"],"path":["api","stock-takes"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 43,\n    \"date_count\": \"2024-10-10T00:00:00+00:00\",\n    \"is_blind\": false,\n    \"warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\"\n    },\n    \"mode\": \"full_count\",\n    \"status\": \"draft\",\n    \"deletable\": true,\n    \"delete_blocked_reasons\": [],\n    \"notes\": \"Q4 annual count\",\n    \"items\": [],\n    \"created_at\": \"2024-10-08T09:00:00+00:00\"\n  }\n}"}]},{"name":"Bulk Delete Stock Takes","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes","host":["{{baseUrl}}"],"path":["api","stock-takes"]},"description":"Bulk delete multiple stock takes.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nids: required unless apply_to_all is true.\n\n**Requires permission:** `inventory.count`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/stock-takes","host":["{{baseUrl}}"],"path":["api","stock-takes"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Bulk delete completed\",\n  \"deleted_count\": 2\n}"}]},{"name":"Create Stock Take from Products","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/bulk-insert","host":["{{baseUrl}}"],"path":["api","stock-takes","bulk-insert"]},"description":"Create a new stock take and bulk-insert product IDs as line items in one operation.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nids: array of product IDs to add. Required (min 1) — this endpoint has no filter-resolution path, so a body without `ids` is rejected with a 422.\n\nmode: full_count (default) or adjustment.\n\nis_initial_count: optional boolean; forces full_count and flags the take as the warehouse's initial count.\n\nUnit cost: for adjustment mode, each item's unit_cost is pre-seeded from the product's best-available cost at the warehouse (FIFO / weighted-average / catalog unit cost, via GetBestAvailableCogs) so the Unit Cost column opens with a basis rather than blank; null when no cost exists. Overridable per row, and negative adjustments ignore it (cost is taken from consumed FIFO layers at finalization). Full counts are not seeded here — they capture cost at count initiation.\n\nReturns the new stock_take_id.\n\n**Requires permission:** `inventory.count`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    55,\n    56,\n    57\n  ],\n  \"warehouse_id\": 2,\n  \"mode\": \"full_count\",\n  \"is_initial_count\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/bulk-insert","host":["{{baseUrl}}"],"path":["api","stock-takes","bulk-insert"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"stock take 44 created successfully\",\n  \"stock_take_id\": 44\n}"}]},{"name":"Check Initial Uniqueness","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/check-initial-uniqueness","host":["{{baseUrl}}"],"path":["api","stock-takes","check-initial-uniqueness"]},"description":"Check if an initial stock take already exists for a warehouse + condition combination.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.count`","body":{"mode":"raw","raw":"{\n  \"warehouse_id\": 2,\n  \"condition\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/check-initial-uniqueness","host":["{{baseUrl}}"],"path":["api","stock-takes","check-initial-uniqueness"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"exists\": true,\n  \"stock_take_id\": 10\n}"}]},{"name":"Initiate Count","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/initiate","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","initiate"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Transition a stock take from Draft to Open status. Takes an inventory snapshot for all items.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nexclude_product_ids: optional array of product IDs to remove before initiating.\n\nis_integrity_fix: if true, marks as integrity fix count.\n\ndate_count: optional retroactive count date (Y-m-d, app timezone, must be today or earlier). Use it when the physical count was performed earlier than it is being entered - the resulting inventory movements, FIFO layers and accounting journal are dated to it (so a historical 'as of' inventory valuation reflects the count), while the snapshot cutoff for reconciling movements made during the count stays at the real initiate moment. Omit to date the count as of now.\n\nReturns 400 if stock take is in adjustment mode (use finalize-adjustment instead), if all items are excluded, or if date_count falls within a locked accounting period. Returns 422 if date_count is in the future.\n\n**Requires permission:** `inventory.count`","body":{"mode":"raw","raw":"{\n  \"exclude_product_ids\": [],\n  \"is_integrity_fix\": false,\n  \"date_count\": \"2026-06-29\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/initiate","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","initiate"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"status\": \"open\",\n    \"date_count\": \"2024-10-10T09:00:00+00:00\",\n    \"items\": [\n      {\n        \"id\": 101,\n        \"product_id\": 55,\n        \"snapshot_inventory\": 100,\n        \"qty_counted\": 100\n      }\n    ]\n  }\n}"}]},{"name":"Re-Snapshot Inventory","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/re-snapshot","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","re-snapshot"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Re-take inventory snapshot for an open stock take. Updates all items' snapshot_inventory to current levels.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.count`"},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/re-snapshot","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","re-snapshot"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"status\": \"open\"\n  }\n}"}]},{"name":"Zero Out Counted Quantities","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/zero-out","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","zero-out"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Set all counted quantities to zero (or to allocated quantities). Only works for open, non-adjustment stock takes.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nmode values:\n- zero_all: set all items to 0\n- keep_submitted: keep quantities allocated to submitted fulfillments\n- keep_all_allocated: keep quantities reserved for all fulfillments\n\n**Requires permission:** `inventory.count`","body":{"mode":"raw","raw":"{\n  \"mode\": \"zero_all\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/zero-out","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","zero-out"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"status\": \"open\",\n    \"items\": [\n      {\n        \"id\": 101,\n        \"qty_counted\": 0\n      }\n    ]\n  }\n}"}]},{"name":"Finalize Stock Take","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/finalize","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","finalize"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Finalize an open stock take. Creates inventory adjustments for all variances and transitions to Closed status.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 400 if protected inventory conflicts exist, insufficient stock, or stock take is not open.\n\n**Requires permission:** `inventory.count`\n\n- `rechain_affected` (optional bool, default false) — when the entry is dated earlier than existing consumption in an affected product/warehouse pool, opt in to re-anchor those later transactions to the oldest available stock (true FIFO) after the save commits. Runs as a background job; pools in a locked accounting period or with unsupported shapes are skipped and reported.","body":{"mode":"raw","raw":"{\n  \"rechain_affected\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/finalize","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","finalize"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"status\": \"closed\",\n    \"value_change\": -125,\n    \"variance_direction\": \"negative\",\n    \"updated_at\": \"2024-10-10T16:00:00+00:00\"\n  }\n}"}]},{"name":"Finalize Adjustment","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/finalize-adjustment","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","finalize-adjustment"],"variable":[{"key":"stockTake","value":"43","description":"Stock take ID (must be adjustment mode)"}]},"description":"Finalize an adjustment mode stock take (goes directly from Draft to Closed, bypassing Open status).\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 400 if stock take is not in adjustment mode.\n\n**Requires permission:** `inventory.count`\n\n- `rechain_affected` (optional bool, default false) — when the entry is dated earlier than existing consumption in an affected product/warehouse pool, opt in to re-anchor those later transactions to the oldest available stock (true FIFO) after the save commits. Runs as a background job; pools in a locked accounting period or with unsupported shapes are skipped and reported.","body":{"mode":"raw","raw":"{\n  \"rechain_affected\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/finalize-adjustment","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","finalize-adjustment"],"variable":[{"key":"stockTake","value":"43","description":"Stock take ID (must be adjustment mode)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 43,\n    \"status\": \"closed\",\n    \"mode\": \"adjustment\",\n    \"updated_at\": \"2024-10-10T16:00:00+00:00\"\n  }\n}"}]},{"name":"Revert to Draft","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/revert-to-draft","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","revert-to-draft"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Revert an open stock take back to draft status.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.count`"},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/revert-to-draft","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","revert-to-draft"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"status\": \"draft\",\n    \"updated_at\": \"2024-10-09T12:00:00+00:00\"\n  }\n}"}]},{"name":"Reverse Stock Take","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/reverse","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","reverse"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID (must be closed)"}]},"description":"Reverse a closed stock take, undoing all inventory adjustments.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 400 if reversal is not possible (e.g., FIFO layers already consumed).\n\n**Requires permission:** `inventory.count`"},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/reverse","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","reverse"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID (must be closed)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Stock take reversed successfully.\",\n  \"data\": {\n    \"id\": 42,\n    \"status\": \"closed\"\n  }\n}"}]},{"name":"Get Reconciliation Preview","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/reconciliation-preview","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","reconciliation-preview"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Get a preview of what reconciliation will do, including items with conflicts and open fulfillments.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/reconciliation-preview","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","reconciliation-preview"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"has_conflicts\": true,\n  \"items\": [\n    {\n      \"stock_take_item_id\": 101,\n      \"product_sku\": \"PROD-001\",\n      \"variance\": -2,\n      \"open_fulfillments\": [\n        {\n          \"fulfillment_id\": 500,\n          \"quantity\": 3\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"Execute Reconciliation","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/reconcile","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","reconcile"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Execute reconciliation for an open stock take, handling fulfillment conflicts and applying adjustments.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nfulfillment_ids_to_cancel: optional array of fulfillment IDs to cancel before reconciling.\n\nproceed_despite_failures: if true, continue even if some items fail.\n\nitem_actions: per-item action overrides.\n\nReturns 400 if protected inventory conflicts exist or insufficient inventory.\n\n**Requires permission:** `inventory.count`\n\ndowngraded_allocations: open Allocated reservations that were downgraded to Planned because the counted reduction exceeded the pool's unallocated stock (reconciliation treats the 3PL count as authoritative). Each entry is also written to the reconciliation logs with action `allocation_downgraded`.\n\n- `rechain_affected` (optional bool, default false) — when the entry is dated earlier than existing consumption in an affected product/warehouse pool, opt in to re-anchor those later transactions to the oldest available stock (true FIFO) after the save commits. Runs as a background job; pools in a locked accounting period or with unsupported shapes are skipped and reported.","body":{"mode":"raw","raw":"{\n  \"fulfillment_ids_to_cancel\": [\n    500\n  ],\n  \"proceed_despite_failures\": false,\n  \"item_actions\": [\n    {\n      \"stock_take_item_id\": 101,\n      \"action\": \"adjust\"\n    }\n  ],\n  \"rechain_affected\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/reconcile","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","reconcile"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"stock_take_finalized\": true,\n  \"cancelled_fulfillments\": [\n    {\n      \"fulfillment_id\": 500,\n      \"sales_order_reference\": \"SO-1042\"\n    }\n  ],\n  \"failed_cancellations\": [],\n  \"finalization_error\": null,\n  \"total_value_change\": -125.5,\n  \"adjusted_items\": [\n    {\n      \"stock_take_item_id\": 101,\n      \"product_sku\": \"WIDGET-1\",\n      \"old_qty_counted\": 10,\n      \"new_qty_counted\": 8\n    }\n  ],\n  \"removed_items\": [],\n  \"backordered_items\": [\n    {\n      \"product_id\": 55,\n      \"product_sku\": \"WIDGET-1\",\n      \"backordered_lines\": 2\n    }\n  ],\n  \"downgraded_allocations\": [\n    {\n      \"allocation_id\": 9912,\n      \"product_id\": 55,\n      \"quantity\": 6,\n      \"link_type\": \"App\\\\Models\\\\SalesOrderLine\",\n      \"link_id\": 33412\n    }\n  ]\n}"}]},{"name":"Get Reconciliation Logs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/reconciliation-logs","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","reconciliation-logs"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Get reconciliation log entries for a stock take.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/reconciliation-logs","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","reconciliation-logs"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"[\n  {\n    \"id\": 1,\n    \"stock_take_id\": 42,\n    \"stock_take_item_id\": 101,\n    \"action\": \"adjusted\",\n    \"qty_adjusted\": -2,\n    \"created_at\": \"2024-10-10T16:00:00+00:00\"\n  }\n]"}]},{"name":"Cancel Fulfillment","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/cancel-fulfillment","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","cancel-fulfillment"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Cancel a single fulfillment as part of reconciliation preparation. Called line-by-line before executeReconciliation.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 200 with success true/false, or 422 if cancellation fails.\n\n**Requires permission:** `inventory.count`","body":{"mode":"raw","raw":"{\n  \"fulfillment_id\": 500\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/cancel-fulfillment","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","cancel-fulfillment"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"fulfillment_id\": 500,\n  \"message\": \"Fulfillment cancelled successfully.\"\n}"}]},{"name":"Preview Adjustment Feasibility","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/adjustment-preview","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","adjustment-preview"],"variable":[{"key":"stockTake","value":"43","description":"Stock take ID (must be adjustment mode)"}]},"description":"Get a feasibility preview for each item before finalizing an adjustment mode stock take.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nReturns 400 if not an adjustment mode stock take."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/adjustment-preview","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","adjustment-preview"],"variable":[{"key":"stockTake","value":"43","description":"Stock take ID (must be adjustment mode)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"items\": [\n    {\n      \"stock_take_item_id\": 201,\n      \"product_sku\": \"PROD-005\",\n      \"adjustment_qty\": -5,\n      \"available_qty\": 20,\n      \"feasible\": true\n    }\n  ]\n}"}]},{"name":"Analyze Reversibility","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/reverse-analysis","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","reverse-analysis"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID (must be closed)"}]},"description":"Analyze whether a closed stock take can be reversed. Shows which items have consumed FIFO layers.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/reverse-analysis","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","reverse-analysis"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID (must be closed)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"can_reverse\": true,\n  \"blocking_items\": [],\n  \"reversible_items\": [\n    {\n      \"stock_take_item_id\": 101,\n      \"product_sku\": \"PROD-001\"\n    }\n  ]\n}"}]},{"name":"Create Compensating Adjustment","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/compensating-adjustment","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","compensating-adjustment"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID (must be closed)"}]},"description":"Create a compensating adjustment stock take for a closed stock take whose FIFO layers have been consumed.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nitems: optional array of item quantity overrides. If omitted, uses original variance quantities.\n\nReturns 400 if compensating adjustment cannot be created.\n\n**Requires permission:** `inventory.count`","body":{"mode":"raw","raw":"{\n  \"items\": [\n    {\n      \"stock_take_item_id\": 101,\n      \"adjustment_qty\": -2\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/compensating-adjustment","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","compensating-adjustment"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID (must be closed)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Compensating adjustment created successfully.\",\n  \"data\": {\n    \"id\": 45,\n    \"mode\": \"adjustment\",\n    \"status\": \"draft\"\n  }\n}"}]},{"name":"Preview Cost Correction","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/cost-correction-preview","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","cost-correction-preview"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID (must be closed)"}]},"description":"Preview the impact of applying cost corrections to a closed stock take's FIFO layers.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.count`","body":{"mode":"raw","raw":"{\n  \"items\": [\n    {\n      \"stock_take_item_id\": 101,\n      \"new_unit_cost\": 13\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/cost-correction-preview","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","cost-correction-preview"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID (must be closed)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"items\": [\n    {\n      \"stock_take_item_id\": 101,\n      \"product_sku\": \"PROD-001\",\n      \"old_unit_cost\": 12.5,\n      \"new_unit_cost\": 13,\n      \"cost_difference\": 0.5,\n      \"affected_layers\": 3\n    }\n  ]\n}"}]},{"name":"Preview FIFO Reallocation","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/reallocation-preview","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","reallocation-preview"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Dry-run the FIFO reallocation that re-dating this stock take to the proposed date would cause, without writing anything. Returns the impact summary: how many outbound usages would be reassigned to different cost layers, the net COGS delta, a per-product breakdown, and whether the change would touch a locked accounting period (in which case the actual re-date is blocked).\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody: date_count (date, required) — the proposed new count date.\n\n**Requires permission:** `inventory.count`","body":{"mode":"raw","raw":"{\n  \"date_count\": \"2024-04-11\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/reallocation-preview","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","reallocation-preview"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"date_count\": \"2024-04-11\",\n    \"locked_period_blocked\": false,\n    \"locked_period_reasons\": [],\n    \"total_reassigned_usages\": 37,\n    \"net_cogs_delta\": 1284.5,\n    \"products\": [\n      {\n        \"product_id\": 55,\n        \"sku\": \"ABC-123\",\n        \"warehouse_id\": 1,\n        \"reassigned_usages\": 22,\n        \"cogs_delta\": 812,\n        \"has_fba_layers\": false\n      }\n    ]\n  }\n}"}]},{"name":"Apply Cost Correction","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/apply-cost-correction","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","apply-cost-correction"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID (must be closed)"}]},"description":"Apply cost corrections to a closed stock take's FIFO layers.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.count`","body":{"mode":"raw","raw":"{\n  \"items\": [\n    {\n      \"stock_take_item_id\": 101,\n      \"new_unit_cost\": 13\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/apply-cost-correction","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","apply-cost-correction"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID (must be closed)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Cost corrections applied successfully.\",\n  \"data\": {\n    \"id\": 42,\n    \"status\": \"closed\"\n  }\n}"}]},{"name":"Get FIFO Layers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/fifo-layers","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","fifo-layers"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Get FIFO inventory layers connected to this stock take's items.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/fifo-layers","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","fifo-layers"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 300,\n      \"product_id\": 55,\n      \"warehouse_id\": 2,\n      \"quantity\": 98,\n      \"unit_cost\": 12.5,\n      \"link_type\": \"App\\\\Models\\\\StockTakeItem\",\n      \"link_id\": 101,\n      \"created_at\": \"2024-10-10T16:00:00+00:00\"\n    }\n  ]\n}"}]},{"name":"Get Inventory Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/inventory-movements","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","inventory-movements"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Get all inventory movements linked to this stock take.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/inventory-movements","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","inventory-movements"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 2001,\n      \"product_id\": 55,\n      \"warehouse_id\": 2,\n      \"quantity\": -2,\n      \"movement_type\": \"stock_take_adjustment\",\n      \"link_type\": \"App\\\\Models\\\\StockTakeItem\",\n      \"link_id\": 101,\n      \"created_at\": \"2024-10-10T16:00:00+00:00\"\n    }\n  ]\n}"}]},{"name":"Get During-Count Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/during-count-movements","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","during-count-movements"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Get inventory movements that occurred during the stock take counting period (after snapshot was taken), excluding movements belonging to this stock take itself.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/during-count-movements","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","during-count-movements"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 2005,\n      \"product_id\": 55,\n      \"warehouse_id\": 2,\n      \"quantity\": 10,\n      \"movement_type\": \"purchase_order_receipt\",\n      \"created_at\": \"2024-10-10T12:00:00+00:00\"\n    }\n  ]\n}"}]},{"name":"Get Product Cost Options","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/product-cost-options?product_id=55&warehouse_id=2","host":["{{baseUrl}}"],"path":["api","stock-takes","product-cost-options"],"query":[{"key":"product_id","value":"55","description":"Required. Product ID.","disabled":true},{"key":"warehouse_id","value":"2","description":"Required. Warehouse ID.","disabled":true}]},"description":"Get cost options for a product in a warehouse. Returns the best available COGS (derived from FIFO cost history) and the product's default unit cost. Both values are per stock unit and returned with up to 4 decimal places of precision — products stocked in small units (oz, g, ml) routinely have per-unit costs below $0.01.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/stock-takes/product-cost-options?product_id=55&warehouse_id=2","host":["{{baseUrl}}"],"path":["api","stock-takes","product-cost-options"],"query":[{"key":"product_id","value":"55","description":"Required. Product ID.","disabled":true},{"key":"warehouse_id","value":"2","description":"Required. Warehouse ID.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"best_available_cogs\": {\n    \"value\": 0.2344,\n    \"source\": \"oldest_active_fifo\",\n    \"available\": true\n  },\n  \"unit_cost\": {\n    \"value\": 0.23,\n    \"available\": true\n  }\n}"}]},{"name":"Get Inventory Start Date","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/inventory-start-date","host":["{{baseUrl}}"],"path":["api","stock-takes","inventory-start-date"]},"description":"Get the inventory start date from application settings. Used to validate initial count dates.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/stock-takes/inventory-start-date","host":["{{baseUrl}}"],"path":["api","stock-takes","inventory-start-date"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"inventory_start_date\": \"2024-01-01\"\n}"}]},{"name":"Import Lines from CSV","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/import-lines","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","import-lines"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Import stock take lines from a previously uploaded CSV file. The CSV must have columns: sku or id (product identifier), and optionally qty_counted, snapshot_inventory, unit_cost.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nfile: path to an already uploaded file (not a direct upload).\n\nProducts matched by SKU or barcode. Duplicate products are summed. Unmatched SKUs generate warnings.\n\nReturns parsed line items (not yet saved — save via the update endpoint).\n\n**Requires permission:** `inventory.count`","body":{"mode":"raw","raw":"{\n  \"file\": \"uploads/stock_take_lines_20241010.csv\",\n  \"separator\": \",\",\n  \"escape\": \"\\\"\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/import-lines","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","import-lines"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 55,\n      \"sku\": \"PROD-001\",\n      \"name\": \"Widget A\",\n      \"qty_counted\": 98,\n      \"snapshot_inventory\": 0,\n      \"unit_cost\": null\n    }\n  ],\n  \"warnings\": [\n    {\n      \"code\": \"HAS_UNMAPPED_PRODUCTS\",\n      \"message\": \"2 of SKUs did not match any product\"\n    }\n  ]\n}"}]},{"name":"Archive Stock Take","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/archive","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","archive"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Archive a single stock take.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.count`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/archive","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","archive"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"stock take 42 archived successfully\",\n  \"data\": {\n    \"id\": 42,\n    \"archived_at\": \"2024-10-15T09:00:00+00:00\"\n  }\n}"}]},{"name":"Unarchive Stock Take","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/unarchived","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","unarchived"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Unarchive a single stock take.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.count`"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/unarchived","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","unarchived"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"stock take 42 unarchived successfully\",\n  \"data\": {\n    \"id\": 42,\n    \"archived_at\": null\n  }\n}"}]},{"name":"Bulk Archive Stock Takes","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/archive","host":["{{baseUrl}}"],"path":["api","stock-takes","archive"]},"description":"Bulk archive multiple stock takes.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nids: array of stock take IDs. Required unless apply_to_all is true.\n\napply_to_all: if true, archives all stock takes matching filters.\n\nfilters: optional filter conditions when apply_to_all is true.\n\n**Requires permission:** `inventory.count`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    40,\n    41,\n    42\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/stock-takes/archive","host":["{{baseUrl}}"],"path":["api","stock-takes","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Bulk archive completed\",\n  \"archived_count\": 3\n}"}]},{"name":"Bulk Unarchive Stock Takes","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/unarchive","host":["{{baseUrl}}"],"path":["api","stock-takes","unarchive"]},"description":"Bulk unarchive multiple stock takes.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.count`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    40,\n    41\n  ],\n  \"apply_to_all\": false,\n  \"filters\": {}\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/stock-takes/unarchive","host":["{{baseUrl}}"],"path":["api","stock-takes","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Bulk unarchive completed\"\n}"}]},{"name":"Check Deletable Stock Takes","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/deletable","host":["{{baseUrl}}"],"path":["api","stock-takes","deletable"]},"description":"Check whether stock takes can be deleted. Returns deletable status and reason for each.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires permission:** `inventory.count`","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    42,\n    43,\n    44\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/deletable","host":["{{baseUrl}}"],"path":["api","stock-takes","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 42,\n      \"deletable\": false,\n      \"reason\": \"Stock take has linked inventory movements\"\n    },\n    {\n      \"id\": 43,\n      \"deletable\": true,\n      \"reason\": null\n    }\n  ]\n}"}]},{"name":"List Stock Take Attachments","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/documents","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","documents"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Lists every document attached to the stock take, newest first, including who uploaded each one.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\n**Requires scope:** `inventory:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/documents","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","documents"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 318,\n      \"file_name\": \"varennes-count-sheet.jpg\",\n      \"mime_type\": \"image/jpeg\",\n      \"is_image\": true,\n      \"file_size\": 412887,\n      \"source\": \"upload\",\n      \"description\": \"Photo of the handwritten count sheet from the Varennes warehouse, signed 30 Jul 2026.\",\n      \"file_url\": \"{{protocol}}{{domain}}/api/documents/318/file\",\n      \"uploaded_by\": \"Otis Reed\",\n      \"linked_records\": [\n        {\n          \"type\": \"App\\\\Models\\\\StockTake\",\n          \"id\": 42\n        }\n      ],\n      \"created_at\": \"2026-07-31T14:02:11.000000Z\"\n    }\n  ]\n}"}]},{"name":"Upload Stock Take Attachment","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/documents","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","documents"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]},"description":"Attaches a file to the stock take. Send as `multipart/form-data`.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAccepted file types: pdf, jpg, jpeg, png, gif, webp, heic, doc, docx, xls, xlsx, csv, txt, html, htm, eml, msg, zip. Maximum size: 20 MB.\n\nThe file type is decided by the **filename extension**, not by sniffing the file's contents — so a text or CSV export whose contents happen to look like another format is still accepted. Files whose contents are an executable or script (PE/ELF/Mach-O binaries, shell/PHP/Perl/Python scripts, JARs, MSIs) are rejected whatever they are named, with `That file contains a program or script and cannot be attached.`\n\nThe optional `description` is what makes an attachment useful months later — record what the file is and where it came from.\n\nAuthentication: Requires Bearer token.\n\n**Requires scope:** `inventory:write`\n\n**Requires permission:** `inventory.count`","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]},{"key":"description","type":"text","value":""}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/documents","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","documents"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 318,\n    \"file_name\": \"varennes-count-sheet.jpg\",\n    \"mime_type\": \"image/jpeg\",\n    \"is_image\": true,\n    \"file_size\": 412887,\n    \"source\": \"upload\",\n    \"description\": \"Photo of the handwritten count sheet from the Varennes warehouse, signed 30 Jul 2026.\",\n    \"file_url\": \"{{protocol}}{{domain}}/api/documents/318/file\",\n    \"uploaded_by\": \"Otis Reed\",\n    \"linked_records\": [\n      {\n        \"type\": \"App\\\\Models\\\\StockTake\",\n        \"id\": 42\n      }\n    ],\n    \"created_at\": \"2026-07-31T14:02:11.000000Z\"\n  }\n}"}]},{"name":"Update Stock Take Attachment Description","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/documents/{document}","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","documents",":document"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"},{"key":"document","value":"318","description":"Attachment (document) ID"}]},"description":"Updates an attachment's description. The description is the only editable field — the stored file itself is immutable, so upload a new attachment to replace it.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`description` is required in the body and may be `null` to clear it. Maximum 2000 characters.\n\nReturns 404 if the document is not attached to the given stock take.\n\nAuthentication: Requires Bearer token.\n\n**Requires scope:** `inventory:write`\n\n**Requires permission:** `inventory.count`","body":{"mode":"raw","raw":"{\n  \"description\": \"Anomaly report v2 — reissued after the warehouse confirmed the 14 damaged masks are scrap.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/documents/{document}","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","documents",":document"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"},{"key":"document","value":"318","description":"Attachment (document) ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 318,\n    \"file_name\": \"varennes-count-sheet.jpg\",\n    \"mime_type\": \"image/jpeg\",\n    \"is_image\": true,\n    \"file_size\": 412887,\n    \"source\": \"upload\",\n    \"description\": \"Anomaly report v2 — reissued after the warehouse confirmed the 14 damaged masks are scrap.\",\n    \"file_url\": \"{{protocol}}{{domain}}/api/documents/318/file\",\n    \"uploaded_by\": \"Otis Reed\",\n    \"linked_records\": [\n      {\n        \"type\": \"App\\\\Models\\\\StockTake\",\n        \"id\": 42\n      }\n    ],\n    \"created_at\": \"2026-07-31T14:02:11.000000Z\"\n  }\n}"}]},{"name":"Delete Stock Take Attachment","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/documents/{document}","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","documents",":document"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"},{"key":"document","value":"318","description":"Attachment (document) ID"}]},"description":"Detaches the document from the stock take and permanently deletes the stored file along with every link to it.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns 404 if the document is not attached to the given stock take.\n\nAuthentication: Requires Bearer token.\n\n**Requires scope:** `inventory:write`\n\n**Requires permission:** `inventory.count`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/documents/{document}","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","documents",":document"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"},{"key":"document","value":"318","description":"Attachment (document) ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Attachment deleted.\",\n  \"data\": null\n}"}]},{"name":"Stream Stock Take Attachment File","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/stock-takes/{stockTake}/documents/{document}/file","host":["{{baseUrl}}"],"path":["api","stock-takes",":stockTake","documents",":document","file"],"variable":[{"key":"stockTake","value":"42","description":"Stock take ID"},{"key":"document","value":"318","description":"Attachment (document) ID"}]},"description":"Streams the attachment's file inline. The response is the raw file, not JSON — `Content-Type` reflects the stored file's own type (e.g. `image/jpeg`, `application/pdf`, `text/html`).\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns 404 if the document is not attached to the given stock take, or if the stored file is missing.\n\nAuthentication: Requires Bearer token.\n\n**Requires scope:** `inventory:read`"}}]},{"name":"Assemblies","item":[{"name":"List Assemblies","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/assemblies?page=1&per_page=15&search=&archived=0&sort=id","host":["{{baseUrl}}"],"path":["api","assemblies"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"15","description":"Items per page (default: 15)","disabled":true},{"key":"search","value":"","description":"Search string to filter assemblies","disabled":true},{"key":"archived","value":"0","description":"Include archived records: 0 = active only (default), 1 = archived only","disabled":true},{"key":"sort","value":"id","description":"Sort field. Prefix with - for descending. Allowed: id, action_date, created_at, updated_at","disabled":true}]},"description":"Returns a paginated list of inventory assemblies. Supports pagination, search, and archive filtering.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nResponse fields:\n- id: Assembly ID\n- warehouse: Warehouse details {id, name}\n- action_date: Date of the assembly/disassembly action\n- action: 'assemble' or 'disassemble'\n- items: Assembly lines with product details\n- created_at: Creation timestamp\n- archived_at: Archive timestamp (null if not archived)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/assemblies?page=1&per_page=15&search=&archived=0&sort=id","host":["{{baseUrl}}"],"path":["api","assemblies"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"15","description":"Items per page (default: 15)","disabled":true},{"key":"search","value":"","description":"Search string to filter assemblies","disabled":true},{"key":"archived","value":"0","description":"Include archived records: 0 = active only (default), 1 = archived only","disabled":true},{"key":"sort","value":"id","description":"Sort field. Prefix with - for descending. Allowed: id, action_date, created_at, updated_at","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 42,\n      \"warehouse\": {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      },\n      \"action_date\": \"2024-01-15\",\n      \"action\": \"assemble\",\n      \"items\": [\n        {\n          \"id\": 10,\n          \"product_id\": 100,\n          \"quantity\": 5,\n          \"product_type\": \"kit\"\n        }\n      ],\n      \"created_at\": \"2024-01-15T10:30:00.000000Z\",\n      \"archived_at\": null\n    }\n  ],\n  \"last_page\": 4,\n  \"per_page\": 15,\n  \"total\": 57,\n  \"from\": 1,\n  \"to\": 15\n}"}]},{"name":"Create Assembly","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/assemblies","host":["{{baseUrl}}"],"path":["api","assemblies"]},"description":"Creates a new inventory assembly or disassembly record, adjusting inventory accordingly.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nRequest body:\n- action_date (date, required): Date of the assembly/disassembly action (e.g. '2024-01-15')\n- product_id (integer, required): ID of the kit product to assemble/disassemble\n- warehouse_id (integer, required): ID of the warehouse where the action takes place. Warehouse must have a default location unless warehouse_location_id is provided.\n- warehouse_location_id (integer, optional): ID of the warehouse location. If omitted, uses the warehouse's default location.\n- quantity (numeric, required): Quantity to assemble or disassemble. Must be non-zero.\n- action (string, required): Type of action — 'assemble' (build kits from components) or 'disassemble' (break kits into components)\n- component_lot_allocations (array, optional, ASSEMBLE only): FEFO suggest-with-override for lot-tracked components. Each entry is `{ product_id, lot_allocation: [{ fifo_layer_id, quantity }] }`. Omit to consume components by automatic FEFO/FIFO. When supplied, the allocation for each component must reconcile to that component's total consumption (its per-kit quantity × assembly quantity), reference active FIFO layers of that component product + the selected warehouse, and respect the tenant's lot-override policy (under `enforce`, expired or later-expiry FEFO picks are hard-blocked). Validation errors are returned per row, e.g. `component_lot_allocations.0.lot_allocation.1.fifo_layer_id`.","body":{"mode":"raw","raw":"{\n  \"action_date\": \"2024-01-15\",\n  \"product_id\": 100,\n  \"warehouse_id\": 1,\n  \"warehouse_location_id\": null,\n  \"quantity\": 5,\n  \"action\": \"assemble\",\n  \"component_lot_allocations\": [\n    {\n      \"product_id\": 201,\n      \"lot_allocation\": [\n        {\n          \"fifo_layer_id\": 901,\n          \"quantity\": 8\n        },\n        {\n          \"fifo_layer_id\": 902,\n          \"quantity\": 2\n        }\n      ]\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/assemblies","host":["{{baseUrl}}"],"path":["api","assemblies"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Inventory assembled successfully.\",\n  \"data\": {\n    \"id\": 42,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"action_date\": \"2024-01-15\",\n    \"action\": \"assemble\",\n    \"items\": [\n      {\n        \"id\": 10,\n        \"product_id\": 100,\n        \"quantity\": 5,\n        \"product_type\": \"kit\"\n      }\n    ],\n    \"created_at\": \"2024-01-15T10:30:00.000000Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Bulk Delete Assemblies","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/assemblies","host":["{{baseUrl}}"],"path":["api","assemblies"]},"description":"Deletes multiple inventory assemblies and reverses their inventory movements.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nRequest body:\n- ids (array, required unless apply_to_all is true): Array of assembly IDs to delete\n- apply_to_all (boolean, optional): If true, deletes all assemblies matching the provided filters\n- filters (array, optional): Filter criteria when apply_to_all is true"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/assemblies","host":["{{baseUrl}}"],"path":["api","assemblies"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 inventory assemblies deleted successfully.\"\n}"}]},{"name":"Get Assembly","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/assemblies/{assembly}","host":["{{baseUrl}}"],"path":["api","assemblies",":assembly"],"variable":[{"key":"assembly","value":"42","description":"Assembly ID"}]},"description":"Returns the full details of a single inventory assembly, including assembly lines, inventory movements, and FIFO layer data.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nResponse includes:\n- id, warehouse, action_date, action\n- items: Assembly lines with product details and primary image\n- inventory_impact: Grouped movements per line with FIFO layer data\n- created_at, updated_at, archived_at"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/assemblies/{assembly}","host":["{{baseUrl}}"],"path":["api","assemblies",":assembly"],"variable":[{"key":"assembly","value":"42","description":"Assembly ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"warehouse\": {\n      \"id\": 1,\n      \"name\": \"Main Warehouse\"\n    },\n    \"action_date\": \"2024-01-15\",\n    \"action\": \"assemble\",\n    \"items\": [\n      {\n        \"id\": 10,\n        \"product_id\": 100,\n        \"quantity\": 5,\n        \"product_type\": \"kit\"\n      }\n    ],\n    \"inventory_impact\": [\n      {\n        \"line_id\": 10,\n        \"product_id\": 100,\n        \"product\": {\n          \"sku\": \"KIT-001\",\n          \"name\": \"Starter Kit\"\n        },\n        \"quantity\": 5,\n        \"product_type\": \"kit\",\n        \"movements\": [\n          {\n            \"id\": 201,\n            \"product_id\": 100,\n            \"warehouse_id\": 1,\n            \"warehouse\": {\n              \"name\": \"Main Warehouse\"\n            },\n            \"quantity_delta\": 5,\n            \"quantity_delta_reserved\": 0,\n            \"movement_date\": \"2024-01-15\",\n            \"inventory_status\": \"on_hand\",\n            \"layer\": {\n              \"id\": 55,\n              \"fifo_layer_date\": \"2024-01-15\",\n              \"original_quantity\": 5,\n              \"fulfilled_quantity\": 0,\n              \"available_quantity\": 5,\n              \"avg_cost\": 12.5,\n              \"total_cost\": 62.5,\n              \"link_type\": \"assembly\",\n              \"link_reference\": \"42\",\n              \"product\": {\n                \"sku\": \"KIT-001\",\n                \"name\": \"Starter Kit\"\n              },\n              \"warehouse\": {\n                \"name\": \"Main Warehouse\"\n              }\n            }\n          }\n        ]\n      }\n    ],\n    \"created_at\": \"2024-01-15T10:30:00.000000Z\",\n    \"updated_at\": \"2024-01-15T10:30:00.000000Z\",\n    \"archived_at\": null\n  }\n}"}]},{"name":"Delete Assembly","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/assemblies/{assembly}","host":["{{baseUrl}}"],"path":["api","assemblies",":assembly"],"variable":[{"key":"assembly","value":"42","description":"Assembly ID"}]},"description":"Deletes the specified inventory assembly and reverses its inventory movements.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nNote: Deletion reverses the inventory impact of the assembly (FIFO layers are unwound)."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/assemblies/{assembly}","host":["{{baseUrl}}"],"path":["api","assemblies",":assembly"],"variable":[{"key":"assembly","value":"42","description":"Assembly ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"inventory assembly 42 deleted successfully.\"\n}"}]},{"name":"Check Assemblies Deletable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/assemblies/deletable","host":["{{baseUrl}}"],"path":["api","assemblies","deletable"]},"description":"Checks whether the specified assemblies can be deleted. All inventory assemblies are deletable (deletion reverses inventory movements).\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nRequest body:\n- ids (array, required): Array of assembly IDs to check (min 1)","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    42,\n    43,\n    44\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/assemblies/deletable","host":["{{baseUrl}}"],"path":["api","assemblies","deletable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 42,\n      \"deletable\": true,\n      \"reason\": null\n    },\n    {\n      \"id\": 43,\n      \"deletable\": true,\n      \"reason\": null\n    }\n  ]\n}"}]},{"name":"Archive Assembly","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/assemblies/{assembly}/archive","host":["{{baseUrl}}"],"path":["api","assemblies",":assembly","archive"],"variable":[{"key":"assembly","value":"42","description":"Assembly ID"}]},"description":"Archives the specified inventory assembly by setting its archived_at timestamp.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/assemblies/{assembly}/archive","host":["{{baseUrl}}"],"path":["api","assemblies",":assembly","archive"],"variable":[{"key":"assembly","value":"42","description":"Assembly ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"inventory assembly 42 archived successfully.\",\n  \"data\": {\n    \"id\": 42,\n    \"archived_at\": \"2024-01-15T10:30:00.000000Z\"\n  }\n}"}]},{"name":"Unarchive Assembly","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/assemblies/{assembly}/unarchived","host":["{{baseUrl}}"],"path":["api","assemblies",":assembly","unarchived"],"variable":[{"key":"assembly","value":"42","description":"Assembly ID"}]},"description":"Restores (unarchives) the specified inventory assembly by clearing its archived_at timestamp.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token"},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/assemblies/{assembly}/unarchived","host":["{{baseUrl}}"],"path":["api","assemblies",":assembly","unarchived"],"variable":[{"key":"assembly","value":"42","description":"Assembly ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"inventory assembly 42 unarchived successfully.\",\n  \"data\": {\n    \"id\": 42,\n    \"archived_at\": null\n  }\n}"}]},{"name":"Bulk Archive Assemblies","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/assemblies/archive","host":["{{baseUrl}}"],"path":["api","assemblies","archive"]},"description":"Archives multiple inventory assemblies.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nRequest body:\n- ids (array, required unless apply_to_all is true): Array of assembly IDs to archive\n- apply_to_all (boolean, optional): If true, archives all assemblies matching the provided filters\n- filters (array, optional): Filter criteria when apply_to_all is true","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    42,\n    43,\n    44\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/assemblies/archive","host":["{{baseUrl}}"],"path":["api","assemblies","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 inventory assemblies archived successfully.\"\n}"}]},{"name":"Bulk Unarchive Assemblies","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/assemblies/unarchive","host":["{{baseUrl}}"],"path":["api","assemblies","unarchive"]},"description":"Restores (unarchives) multiple inventory assemblies.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nRequest body:\n- ids (array, required unless apply_to_all is true): Array of assembly IDs to unarchive\n- apply_to_all (boolean, optional): If true, unarchives all assemblies matching the provided filters\n- filters (array, optional): Filter criteria when apply_to_all is true","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    42,\n    43,\n    44\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/assemblies/unarchive","host":["{{baseUrl}}"],"path":["api","assemblies","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 inventory assemblies unarchived successfully.\"\n}"}]},{"name":"List Kit Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/assemblies/browse-kits?warehouse_id=1&query=starter&limit=15","host":["{{baseUrl}}"],"path":["api","assemblies","browse-kits"],"query":[{"key":"warehouse_id","value":"1","description":"Warehouse ID to calculate inventory quantities for (optional). If omitted, qty_on_hand and qty_possible will be 0.","disabled":true},{"key":"query","value":"starter","description":"Search string to filter kit products by SKU or name","disabled":true},{"key":"limit","value":"15","description":"Items per page (default: 15)","disabled":true}]},"description":"Returns a paginated list of kit products with inventory availability data for the assembly form. Shows how many kits can currently be assembled based on available component stock.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nResponse fields per kit:\n- id: Product ID\n- sku: Product SKU\n- name: Product name\n- qty_on_hand: Current assembled kit quantity in the warehouse\n- qty_possible: Maximum number of kits that can be assembled from available component stock"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/assemblies/browse-kits?warehouse_id=1&query=starter&limit=15","host":["{{baseUrl}}"],"path":["api","assemblies","browse-kits"],"query":[{"key":"warehouse_id","value":"1","description":"Warehouse ID to calculate inventory quantities for (optional). If omitted, qty_on_hand and qty_possible will be 0.","disabled":true},{"key":"query","value":"starter","description":"Search string to filter kit products by SKU or name","disabled":true},{"key":"limit","value":"15","description":"Items per page (default: 15)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 100,\n      \"sku\": \"KIT-001\",\n      \"name\": \"Starter Kit\",\n      \"qty_on_hand\": 12,\n      \"qty_possible\": 8\n    }\n  ],\n  \"last_page\": 2,\n  \"per_page\": 15,\n  \"total\": 18\n}"}]},{"name":"Get Kit & Component Availability","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/assemblies/inventory-data?product_id=100","host":["{{baseUrl}}"],"path":["api","assemblies","inventory-data"],"query":[{"key":"product_id","value":"100","description":"Kit product ID (required). Returns inventory data for this kit and all its component products.","disabled":true}]},"description":"Returns inventory availability data for a kit product and its components across all warehouses. Used in the assembly form to show per-warehouse stock levels.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token\n\nResponse:\n- kit_inventory: Map of warehouse_id => {warehouse_id, warehouse_name, quantity} for the kit product\n- component_inventory: Map of warehouse_id => {product_id: available_qty} for each component"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/assemblies/inventory-data?product_id=100","host":["{{baseUrl}}"],"path":["api","assemblies","inventory-data"],"query":[{"key":"product_id","value":"100","description":"Kit product ID (required). Returns inventory data for this kit and all its component products.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"kit_inventory\": {\n      \"1\": {\n        \"warehouse_id\": 1,\n        \"warehouse_name\": \"Main Warehouse\",\n        \"quantity\": 12\n      },\n      \"2\": {\n        \"warehouse_id\": 2,\n        \"warehouse_name\": \"East Warehouse\",\n        \"quantity\": 5\n      }\n    },\n    \"component_inventory\": {\n      \"1\": {\n        \"101\": 50,\n        \"102\": 30\n      },\n      \"2\": {\n        \"101\": 20,\n        \"102\": 10\n      }\n    }\n  }\n}"}]}]},{"name":"Initial Inventory","item":[{"name":"Import Initial Inventory (CSV)","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/initial-inventory/import","host":["{{baseUrl}}"],"path":["api","initial-inventory","import"]},"description":"Imports initial inventory records from a CSV file using the DataImport system.\nAuthentication: Requires Bearer token.\nContent-Type: multipart/form-data\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe import first runs a preview/validation pass. If there are required field differences, the preview response is returned. Otherwise, the import is executed and records are created.","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]},{"key":"import_type","type":"text","value":""}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/initial-inventory/import","host":["{{baseUrl}}"],"path":["api","initial-inventory","import"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"imported\": 42,\n    \"failed\": 0,\n    \"errors\": []\n  },\n  \"message\": \"Import completed successfully.\",\n  \"status\": \"success\"\n}"}]},{"name":"List Initial Inventory","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/initial-inventory?page=1&limit=25&sort=id&search=","host":["{{baseUrl}}"],"path":["api","initial-inventory"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"limit","value":"25","description":"Items per page","disabled":true},{"key":"sort","value":"id","description":"Sort field. Prefix with - for descending (e.g. -id)","disabled":true},{"key":"search","value":"","description":"Search across SKU, product name","disabled":true}]},"description":"Returns a paginated list of initial inventory records (FIFO layers representing opening stock).\nAuthentication: Requires Bearer token.\nUses standard list query parameters for filtering, sorting, and pagination."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/initial-inventory?page=1&limit=25&sort=id&search=","host":["{{baseUrl}}"],"path":["api","initial-inventory"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"limit","value":"25","description":"Items per page","disabled":true},{"key":"sort","value":"id","description":"Sort field. Prefix with - for descending (e.g. -id)","disabled":true},{"key":"search","value":"","description":"Search across SKU, product name","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"product_id\": 42,\n      \"sku\": \"WIDGET-001\",\n      \"product_name\": \"Blue Widget\",\n      \"warehouse_id\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"quantity\": 200,\n      \"unit_cost\": 10,\n      \"fifo_layer_date\": \"2023-01-01\",\n      \"created_at\": \"2023-01-01T00:00:00Z\",\n      \"updated_at\": \"2023-01-01T00:00:00Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 2,\n  \"per_page\": 25,\n  \"total\": 42\n}"}]},{"name":"Create Initial Inventory","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/initial-inventory","host":["{{baseUrl}}"],"path":["api","initial-inventory"]},"description":"Creates an initial inventory record (opening stock FIFO layer) for a product in a warehouse.\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"product_id\": 42,\n  \"warehouse_id\": 1,\n  \"quantity\": 200,\n  \"unit_cost\": 10,\n  \"fifo_layer_date\": \"2023-01-01\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/initial-inventory","host":["{{baseUrl}}"],"path":["api","initial-inventory"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 5,\n    \"product_id\": 42,\n    \"warehouse_id\": 1,\n    \"quantity\": 200,\n    \"unit_cost\": 10,\n    \"fifo_layer_date\": \"2023-01-01\",\n    \"created_at\": \"2024-01-15T10:00:00Z\",\n    \"updated_at\": \"2024-01-15T10:00:00Z\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Get Initial Inventory","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/initial-inventory/{id}","host":["{{baseUrl}}"],"path":["api","initial-inventory",":id"],"variable":[{"key":"id","value":"1","description":"Initial inventory record ID"}]},"description":"Returns a single initial inventory record.\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/initial-inventory/{id}","host":["{{baseUrl}}"],"path":["api","initial-inventory",":id"],"variable":[{"key":"id","value":"1","description":"Initial inventory record ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"product_id\": 42,\n    \"sku\": \"WIDGET-001\",\n    \"product_name\": \"Blue Widget\",\n    \"warehouse_id\": 1,\n    \"warehouse_name\": \"Main Warehouse\",\n    \"quantity\": 200,\n    \"unit_cost\": 10,\n    \"fifo_layer_date\": \"2023-01-01\",\n    \"created_at\": \"2023-01-01T00:00:00Z\",\n    \"updated_at\": \"2023-01-01T00:00:00Z\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Update Initial Inventory","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/initial-inventory/{id}","host":["{{baseUrl}}"],"path":["api","initial-inventory",":id"],"variable":[{"key":"id","value":"1","description":"Initial inventory record ID"}]},"description":"Updates an existing initial inventory record.\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"quantity\": 250,\n  \"unit_cost\": 11\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/initial-inventory/{id}","host":["{{baseUrl}}"],"path":["api","initial-inventory",":id"],"variable":[{"key":"id","value":"1","description":"Initial inventory record ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"quantity\": 250,\n    \"unit_cost\": 11,\n    \"updated_at\": \"2024-01-16T09:00:00Z\"\n  },\n  \"status\": \"success\"\n}"}]},{"name":"Delete Initial Inventory","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/initial-inventory/{id}","host":["{{baseUrl}}"],"path":["api","initial-inventory",":id"],"variable":[{"key":"id","value":"1","description":"Initial inventory record ID"}]},"description":"Deletes an initial inventory record and reverses its associated FIFO layers.\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/initial-inventory/{id}","host":["{{baseUrl}}"],"path":["api","initial-inventory",":id"],"variable":[{"key":"id","value":"1","description":"Initial inventory record ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Initial inventory record deleted successfully.\",\n  \"status\": \"success\"\n}"}]}]},{"name":"Costing","item":[{"name":"List Cost Entries","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-entries","host":["{{baseUrl}}"],"path":["api","cost-entries"]},"description":"Returns a paginated list of cost entries, newest cost date first by default.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAllowed sorts: id, description, reference_number, status, cost_date, amount, amount_in_tenant_currency, created_at, updated_at, supplier_name, cost_entry_type_name, allocations_total, unallocated_total. Prefix with - for descending.\n\nfilter[search] matches description, reference number, ID, supplier name and cost entry type name. filter[source.is]=bill returns only entries created from bills; filter[source.is_not]=bill returns manual entries. filter[has_amortization.is]=true/false filters by presence of an amortization schedule.\n\nColumn filters accept operator suffixes in the form filter[column.operator], e.g. filter[name.contains]=main or filter[created_at.between]=2026-01-01,2026-06-30. Text operators: is, is_not, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, is_one_of. Numeric operators: is, is_not, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between, is_empty, is_not_empty. Date operators: is, is_not, on_or_before, on_or_after, between, is_empty, is_not_empty.\n\nThe response includes an unfiltered_total field with the total number of cost entries regardless of filters.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-entries","host":["{{baseUrl}}"],"path":["api","cost-entries"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 42,\n      \"supplier_id\": 3,\n      \"supplier_name\": \"Global Freight Co\",\n      \"reference_number\": \"FRT-2026-0142\",\n      \"description\": \"June ocean freight\",\n      \"cost_date\": \"2026-06-01\",\n      \"amount\": 2500,\n      \"amount_in_tenant_currency\": 2500,\n      \"currency_id\": 1,\n      \"currency_code\": \"USD\",\n      \"currency_rate\": 1,\n      \"nominal_code_id\": 15,\n      \"nominal_code_name\": \"Freight & Duty\",\n      \"cost_entry_type_id\": 2,\n      \"cost_entry_type\": {\n        \"id\": 2,\n        \"name\": \"Freight\"\n      },\n      \"status\": \"pending_allocation\",\n      \"bill_id\": null,\n      \"bill_line_id\": null,\n      \"source_type\": \"manual\",\n      \"source_description\": null,\n      \"source_link_type\": null,\n      \"source_id\": null,\n      \"source_label\": null,\n      \"source_period\": null,\n      \"reconciliation\": null,\n      \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n      \"updated_at\": \"2026-06-01T10:00:00.000000Z\",\n      \"totals\": {\n        \"allocated\": 1000,\n        \"unallocated\": 1500,\n        \"is_fully_allocated\": false\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1,\n  \"unfiltered_total\": 128\n}"}]},{"name":"Create Cost Entry","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-entries","host":["{{baseUrl}}"],"path":["api","cost-entries"]},"description":"Creates a cost entry.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequired fields: description, cost_date (Y-m-d), amount (in the entry's currency) and amount_in_tenant_currency.\n\nOptional fields: cost_entry_type_id, status (draft, pending_allocation, partially_allocated or allocated; default draft), supplier_id, reference_number, currency_id, currency_rate, nominal_code_id, bill_id and bill_line_id (link the entry to a bill), source_type, source_id and source_period (identify an originating document and the period the cost applies to).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"description\": \"June ocean freight\",\n  \"cost_date\": \"2026-06-01\",\n  \"amount\": 2500,\n  \"amount_in_tenant_currency\": 2500,\n  \"cost_entry_type_id\": 2,\n  \"status\": \"pending_allocation\",\n  \"supplier_id\": 3,\n  \"reference_number\": \"FRT-2026-0142\",\n  \"currency_id\": 1,\n  \"currency_rate\": 1,\n  \"nominal_code_id\": 15\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-entries","host":["{{baseUrl}}"],"path":["api","cost-entries"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"supplier_id\": 3,\n    \"supplier_name\": \"Global Freight Co\",\n    \"reference_number\": \"FRT-2026-0142\",\n    \"description\": \"June ocean freight\",\n    \"cost_date\": \"2026-06-01\",\n    \"amount\": 2500,\n    \"amount_in_tenant_currency\": 2500,\n    \"currency_id\": 1,\n    \"currency_code\": \"USD\",\n    \"currency_rate\": 1,\n    \"nominal_code_id\": 15,\n    \"nominal_code_name\": \"Freight & Duty\",\n    \"cost_entry_type_id\": 2,\n    \"cost_entry_type\": {\n      \"id\": 2,\n      \"name\": \"Freight\"\n    },\n    \"status\": \"pending_allocation\",\n    \"bill_id\": null,\n    \"bill_line_id\": null,\n    \"source_type\": \"manual\",\n    \"source_description\": null,\n    \"source_link_type\": null,\n    \"source_id\": null,\n    \"source_label\": null,\n    \"source_period\": null,\n    \"reconciliation\": null,\n    \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"updated_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"totals\": {\n      \"allocated\": 1000,\n      \"unallocated\": 1500,\n      \"is_fully_allocated\": false\n    }\n  }\n}"}]},{"name":"Get Cost Entry","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"}]},"description":"Returns a cost entry with its allocations and amortization schedule (including periods and the resolved allocation target).\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe totals object reports the allocated amount, the remaining unallocated amount and whether the entry is fully allocated. For entries auto-generated from a source document, source_type is \"source\" and the source_* fields identify the origin record; manual entries have source_type \"manual\". The reconciliation field holds settlement reconciliation results ({actual_amount, variance, variance_pct, settlement_report_ids, reconciled_at}) or null.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"supplier_id\": 3,\n    \"supplier_name\": \"Global Freight Co\",\n    \"reference_number\": \"FRT-2026-0142\",\n    \"description\": \"June ocean freight\",\n    \"cost_date\": \"2026-06-01\",\n    \"amount\": 2500,\n    \"amount_in_tenant_currency\": 2500,\n    \"currency_id\": 1,\n    \"currency_code\": \"USD\",\n    \"currency_rate\": 1,\n    \"nominal_code_id\": 15,\n    \"nominal_code_name\": \"Freight & Duty\",\n    \"cost_entry_type_id\": 2,\n    \"cost_entry_type\": {\n      \"id\": 2,\n      \"name\": \"Freight\"\n    },\n    \"status\": \"pending_allocation\",\n    \"bill_id\": null,\n    \"bill_line_id\": null,\n    \"source_type\": \"manual\",\n    \"source_description\": null,\n    \"source_link_type\": null,\n    \"source_id\": null,\n    \"source_label\": null,\n    \"source_period\": null,\n    \"reconciliation\": null,\n    \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"updated_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"totals\": {\n      \"allocated\": 1000,\n      \"unallocated\": 1500,\n      \"is_fully_allocated\": false\n    },\n    \"allocations\": [\n      {\n        \"id\": 88,\n        \"cost_entry_id\": 42,\n        \"allocatable_type\": \"App\\\\Models\\\\Product\",\n        \"allocatable_type_label\": \"Product\",\n        \"allocatable_id\": 101,\n        \"allocatable\": {\n          \"id\": 101,\n          \"name\": \"Widget\",\n          \"sku\": \"WIDGET-001\"\n        },\n        \"allocation_date\": \"2026-06-01\",\n        \"period_start\": \"2026-06-01\",\n        \"period_end\": \"2026-06-30\",\n        \"amount\": 1000,\n        \"amount_in_tenant_currency\": 1000,\n        \"proration_strategy\": \"specific_line\",\n        \"proration_weight\": null,\n        \"amortization_period_id\": null,\n        \"is_amortized\": false,\n        \"created_at\": \"2026-06-01T10:05:00.000000Z\",\n        \"updated_at\": \"2026-06-01T10:05:00.000000Z\"\n      }\n    ],\n    \"amortization_schedule\": {\n      \"id\": 7,\n      \"cost_entry_id\": 42,\n      \"start_date\": \"2026-07-01\",\n      \"end_date\": \"2027-06-30\",\n      \"total_periods\": 12,\n      \"frequency\": \"monthly\",\n      \"method\": \"straight_line\",\n      \"total_amount\": 2500,\n      \"amount_per_period\": 208.33,\n      \"is_fully_processed\": false,\n      \"pending_periods_count\": 12,\n      \"processed_periods_count\": 0,\n      \"allocation_config\": {\n        \"allocatable_type\": \"product\",\n        \"allocatable_id\": 101,\n        \"allocatable_name\": \"Widget\",\n        \"proration_strategy\": \"specific_line\"\n      },\n      \"created_at\": \"2026-06-01T10:10:00.000000Z\",\n      \"updated_at\": \"2026-06-01T10:10:00.000000Z\",\n      \"periods\": [\n        {\n          \"id\": 601,\n          \"period_number\": 1,\n          \"period_start\": \"2026-07-01\",\n          \"period_end\": \"2026-07-31\",\n          \"amount\": 208.33,\n          \"is_processed\": false\n        }\n      ]\n    }\n  }\n}"}]},{"name":"Update Cost Entry","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"}]},"description":"Updates a cost entry. All fields are optional; only provided fields are changed.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: description, cost_date, amount, amount_in_tenant_currency, cost_entry_type_id, status (draft, pending_allocation, partially_allocated, allocated), supplier_id, reference_number, currency_id, currency_rate and nominal_code_id.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"description\": \"June ocean freight (revised)\",\n  \"amount\": 2650,\n  \"amount_in_tenant_currency\": 2650\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"supplier_id\": 3,\n    \"supplier_name\": \"Global Freight Co\",\n    \"reference_number\": \"FRT-2026-0142\",\n    \"description\": \"June ocean freight\",\n    \"cost_date\": \"2026-06-01\",\n    \"amount\": 2500,\n    \"amount_in_tenant_currency\": 2500,\n    \"currency_id\": 1,\n    \"currency_code\": \"USD\",\n    \"currency_rate\": 1,\n    \"nominal_code_id\": 15,\n    \"nominal_code_name\": \"Freight & Duty\",\n    \"cost_entry_type_id\": 2,\n    \"cost_entry_type\": {\n      \"id\": 2,\n      \"name\": \"Freight\"\n    },\n    \"status\": \"pending_allocation\",\n    \"bill_id\": null,\n    \"bill_line_id\": null,\n    \"source_type\": \"manual\",\n    \"source_description\": null,\n    \"source_link_type\": null,\n    \"source_id\": null,\n    \"source_label\": null,\n    \"source_period\": null,\n    \"reconciliation\": null,\n    \"created_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"updated_at\": \"2026-06-01T10:00:00.000000Z\",\n    \"totals\": {\n      \"allocated\": 1000,\n      \"unallocated\": 1500,\n      \"is_fully_allocated\": false\n    }\n  }\n}"}]},{"name":"Delete Cost Entry","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"}]},"description":"Deletes a cost entry and its allocations.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Cost entry deleted successfully\"\n}"}]},{"name":"List Cost Entry Allocations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocations","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocations"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"}]},"description":"Returns all allocations recorded against the cost entry, including the allocated target (product, brand, supplier, sales channel or sales order line) and any amortization linkage.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocations","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocations"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 88,\n      \"cost_entry_id\": 42,\n      \"allocatable_type\": \"App\\\\Models\\\\Product\",\n      \"allocatable_type_label\": \"Product\",\n      \"allocatable_id\": 101,\n      \"allocatable\": {\n        \"id\": 101,\n        \"name\": \"Widget\",\n        \"sku\": \"WIDGET-001\"\n      },\n      \"allocation_date\": \"2026-06-01\",\n      \"period_start\": \"2026-06-01\",\n      \"period_end\": \"2026-06-30\",\n      \"amount\": 1000,\n      \"amount_in_tenant_currency\": 1000,\n      \"proration_strategy\": \"specific_line\",\n      \"proration_weight\": null,\n      \"amortization_period_id\": null,\n      \"is_amortized\": false,\n      \"created_at\": \"2026-06-01T10:05:00.000000Z\",\n      \"updated_at\": \"2026-06-01T10:05:00.000000Z\"\n    }\n  ]\n}"}]},{"name":"Delete Cost Entry Allocations","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocations","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocations"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"}]},"description":"Deletes all allocations for the cost entry, returning its full amount to unallocated.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocations","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocations"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Deleted 3 allocations\"\n}"}]},{"name":"Create Amortization Schedule","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/amortization-schedule","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","amortization-schedule"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"}]},"description":"Creates an amortization schedule for a cost entry, spreading its amount across future periods. Each period, when processed, allocates its share to the configured target. A cost entry can have only one schedule.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequired fields: start_date (Y-m-d), periods (1-120), frequency (daily, weekly, monthly, quarterly, annually), method (straight_line, front_loaded, back_loaded), allocatable_type (product, brand, supplier or sales_channel) and allocatable_id.\n\nOptional: proration_strategy (default specific_line).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"start_date\": \"2026-07-01\",\n  \"periods\": 12,\n  \"frequency\": \"monthly\",\n  \"method\": \"straight_line\",\n  \"allocatable_type\": \"product\",\n  \"allocatable_id\": 101,\n  \"proration_strategy\": \"specific_line\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/amortization-schedule","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","amortization-schedule"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 7,\n    \"cost_entry_id\": 42,\n    \"start_date\": \"2026-07-01\",\n    \"end_date\": \"2027-06-30\",\n    \"total_periods\": 12,\n    \"frequency\": \"monthly\",\n    \"method\": \"straight_line\",\n    \"total_amount\": 2500,\n    \"amount_per_period\": 208.33,\n    \"is_fully_processed\": false,\n    \"pending_periods_count\": 12,\n    \"processed_periods_count\": 0,\n    \"allocation_config\": {\n      \"allocatable_type\": \"product\",\n      \"allocatable_id\": 101,\n      \"allocatable_name\": \"Widget\",\n      \"proration_strategy\": \"specific_line\"\n    },\n    \"created_at\": \"2026-06-01T10:10:00.000000Z\",\n    \"updated_at\": \"2026-06-01T10:10:00.000000Z\",\n    \"periods\": [\n      {\n        \"id\": 601,\n        \"period_number\": 1,\n        \"period_start\": \"2026-07-01\",\n        \"period_end\": \"2026-07-31\",\n        \"amount\": 208.33,\n        \"is_processed\": false\n      }\n    ]\n  }\n}"}]},{"name":"List Cost Allocations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-allocations","host":["{{baseUrl}}"],"path":["api","cost-allocations"]},"description":"Returns a paginated list of cost allocations. A cost allocation assigns part (or all) of a cost entry's amount to a target entity - a product, brand, supplier, sales channel or sales order line - for landed cost and profitability reporting.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach allocation includes the target type, a human-readable type label, a summary of the target entity (allocatable), the allocated amount in the cost entry's currency and in the account currency (amount_in_tenant_currency), the proration strategy that produced it, and whether it was generated by an amortization schedule (is_amortized).\n\nFiltering uses plain query parameters (not the filter[...] syntax used elsewhere in this API): cost_entry_id, allocatable_type, allocatable_id, date_from, date_to and proration_strategy - see each parameter's description. Results are always sorted by allocation date, newest first; there is no sort parameter.\n\nPagination: page (default 1) and per_page (default 15, max 100).\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-allocations","host":["{{baseUrl}}"],"path":["api","cost-allocations"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 88,\n      \"cost_entry_id\": 42,\n      \"allocatable_type\": \"App\\\\Models\\\\Product\",\n      \"allocatable_type_label\": \"Product\",\n      \"allocatable_id\": 101,\n      \"allocatable\": {\n        \"id\": 101,\n        \"name\": \"Widget\",\n        \"sku\": \"WIDGET-001\"\n      },\n      \"allocation_date\": \"2026-06-01\",\n      \"period_start\": \"2026-06-01\",\n      \"period_end\": \"2026-06-30\",\n      \"amount\": 1000,\n      \"amount_in_tenant_currency\": 1000,\n      \"proration_strategy\": \"manual\",\n      \"proration_weight\": null,\n      \"amortization_period_id\": null,\n      \"is_amortized\": false,\n      \"created_at\": \"2026-06-01T10:05:00.000000Z\",\n      \"updated_at\": \"2026-06-01T10:05:00.000000Z\"\n    },\n    {\n      \"id\": 91,\n      \"cost_entry_id\": 45,\n      \"allocatable_type\": \"App\\\\Models\\\\Supplier\",\n      \"allocatable_type_label\": \"Supplier\",\n      \"allocatable_id\": 3,\n      \"allocatable\": {\n        \"id\": 3,\n        \"name\": \"Global Freight Co\"\n      },\n      \"allocation_date\": \"2026-05-15\",\n      \"period_start\": null,\n      \"period_end\": null,\n      \"amount\": 450,\n      \"amount_in_tenant_currency\": 450,\n      \"proration_strategy\": \"cost_based\",\n      \"proration_weight\": 0.45,\n      \"amortization_period_id\": null,\n      \"is_amortized\": false,\n      \"created_at\": \"2026-05-15T08:20:00.000000Z\",\n      \"updated_at\": \"2026-05-15T08:20:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/cost-allocations?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/cost-allocations?page=1\",\n  \"links\": [\n    {\n      \"url\": null,\n      \"label\": \"&laquo; Previous\",\n      \"active\": false\n    },\n    {\n      \"url\": \"{{protocol}}{{domain}}/api/cost-allocations?page=1\",\n      \"label\": \"1\",\n      \"active\": true\n    },\n    {\n      \"url\": null,\n      \"label\": \"Next &raquo;\",\n      \"active\": false\n    }\n  ],\n  \"next_page_url\": null,\n  \"path\": \"{{protocol}}{{domain}}/api/cost-allocations\",\n  \"per_page\": 15,\n  \"prev_page_url\": null,\n  \"to\": 2,\n  \"total\": 2\n}"}]},{"name":"Create Cost Allocation","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-allocations","host":["{{baseUrl}}"],"path":["api","cost-allocations"]},"description":"Creates a single cost allocation directly, assigning part (or all) of a cost entry's amount to a target entity. This is the low-level create: the caller supplies both currency amounts and the proration metadata explicitly. For most integrations the allocate endpoints under /api/cost-entries/{cost_entry}/allocate/... are simpler - they compute the account-currency amount and proration weights automatically.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields:\n- cost_entry_id (required, integer): ID of the cost entry to allocate from. Unknown IDs return 404.\n- allocatable_type (required, string): fully qualified target type. One of: App\\Models\\Product, App\\Models\\ProductBrand, App\\Models\\Supplier, App\\Models\\SalesChannel, App\\Models\\SalesOrderLine.\n- allocatable_id (required, integer): ID of the target entity.\n- allocation_date (required, date YYYY-MM-DD): ledger date of the allocation.\n- amount (required, number): amount to allocate, in the cost entry's currency.\n- amount_in_tenant_currency (required, number): the same amount expressed in the account currency.\n- proration_strategy (required, string): one of revenue_based, cost_based, weight_based, volume_based, quantity_based, specific_line, manual. Use manual for a direct allocation.\n- period_start, period_end (optional, date YYYY-MM-DD): the period the cost applies to.\n- proration_weight (optional, number): the weight used when the allocation is part of a prorated distribution.\n- amortization_period_id (optional, integer): links the allocation to an amortization period. Leave null for manual allocations - amortization-linked allocations are normally created by the amortization processor.\n\nThe allocated amount is validated against the cost entry's remaining unallocated amount (taking into account amounts reserved by a pending amortization schedule); exceeding it returns 422. Creating an allocation also recalculates the parent cost entry's allocation status (pending_allocation, partially_allocated or allocated).\n\nNote that this endpoint returns HTTP 200 (not 201) on success.\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write).","body":{"mode":"raw","raw":"{\n  \"cost_entry_id\": 42,\n  \"allocatable_type\": \"App\\\\Models\\\\Product\",\n  \"allocatable_id\": 101,\n  \"allocation_date\": \"2026-06-01\",\n  \"amount\": 1000,\n  \"amount_in_tenant_currency\": 1000,\n  \"proration_strategy\": \"manual\",\n  \"period_start\": \"2026-06-01\",\n  \"period_end\": \"2026-06-30\",\n  \"proration_weight\": null,\n  \"amortization_period_id\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-allocations","host":["{{baseUrl}}"],"path":["api","cost-allocations"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 88,\n    \"cost_entry_id\": 42,\n    \"allocatable_type\": \"App\\\\Models\\\\Product\",\n    \"allocatable_type_label\": \"Product\",\n    \"allocatable_id\": 101,\n    \"allocatable\": {\n      \"id\": 101,\n      \"name\": \"Widget\",\n      \"sku\": \"WIDGET-001\"\n    },\n    \"allocation_date\": \"2026-06-01\",\n    \"period_start\": \"2026-06-01\",\n    \"period_end\": \"2026-06-30\",\n    \"amount\": 1000,\n    \"amount_in_tenant_currency\": 1000,\n    \"proration_strategy\": \"manual\",\n    \"proration_weight\": null,\n    \"amortization_period_id\": null,\n    \"is_amortized\": false,\n    \"created_at\": \"2026-06-01T10:05:00.000000Z\",\n    \"updated_at\": \"2026-06-01T10:05:00.000000Z\"\n  }\n}"}]},{"name":"Get Cost Allocation","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-allocations/{costAllocation}","host":["{{baseUrl}}"],"path":["api","cost-allocations",":costAllocation"],"variable":[{"key":"costAllocation","value":"88","description":"Cost allocation ID"}]},"description":"Returns a single cost allocation by ID, including a summary of the target entity (allocatable). The summary fields depend on the target type: products include name and sku; brands, suppliers and sales channels include name; sales order lines include sales_order_id, product_id, product_name and quantity. amount is in the parent cost entry's currency and amount_in_tenant_currency is the equivalent in the account currency.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-allocations/{costAllocation}","host":["{{baseUrl}}"],"path":["api","cost-allocations",":costAllocation"],"variable":[{"key":"costAllocation","value":"88","description":"Cost allocation ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 88,\n    \"cost_entry_id\": 42,\n    \"allocatable_type\": \"App\\\\Models\\\\Product\",\n    \"allocatable_type_label\": \"Product\",\n    \"allocatable_id\": 101,\n    \"allocatable\": {\n      \"id\": 101,\n      \"name\": \"Widget\",\n      \"sku\": \"WIDGET-001\"\n    },\n    \"allocation_date\": \"2026-06-01\",\n    \"period_start\": \"2026-06-01\",\n    \"period_end\": \"2026-06-30\",\n    \"amount\": 1000,\n    \"amount_in_tenant_currency\": 1000,\n    \"proration_strategy\": \"manual\",\n    \"proration_weight\": null,\n    \"amortization_period_id\": null,\n    \"is_amortized\": false,\n    \"created_at\": \"2026-06-01T10:05:00.000000Z\",\n    \"updated_at\": \"2026-06-01T10:05:00.000000Z\"\n  }\n}"}]},{"name":"Delete Cost Allocation","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/cost-allocations/{costAllocation}","host":["{{baseUrl}}"],"path":["api","cost-allocations",":costAllocation"],"variable":[{"key":"costAllocation","value":"88","description":"Cost allocation ID"}]},"description":"Deletes a cost allocation and recalculates the parent cost entry's allocation status, freeing the amount to be allocated again.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAllocations that were created by an amortization schedule cannot be deleted individually - the request fails with HTTP 422. Delete the amortization schedule instead (DELETE /api/amortization-schedules/{amortization_schedule}).\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/cost-allocations/{costAllocation}","host":["{{baseUrl}}"],"path":["api","cost-allocations",":costAllocation"],"variable":[{"key":"costAllocation","value":"88","description":"Cost allocation ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Allocation deleted successfully\"\n}"}]},{"name":"Allocate Cost Entry to Product","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/product/{product}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","product",":product"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"product","value":"101","description":"Product ID"}]},"description":"Allocates part of a cost entry to a specific product. The allocation feeds into landed cost and profitability reporting.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: amount (required, the amount to allocate), allocation_date (the ledger date for the allocation, defaults to today), period_start and period_end (optional period the cost applies to).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"amount\": 1000,\n  \"allocation_date\": \"2026-06-15\",\n  \"period_start\": \"2026-06-01\",\n  \"period_end\": \"2026-06-30\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/product/{product}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","product",":product"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"product","value":"101","description":"Product ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 88,\n    \"cost_entry_id\": 42,\n    \"allocatable_type\": \"App\\\\Models\\\\Product\",\n    \"allocatable_type_label\": \"Product\",\n    \"allocatable_id\": 101,\n    \"allocatable\": {\n      \"id\": 101,\n      \"name\": \"Widget\",\n      \"sku\": \"WIDGET-001\"\n    },\n    \"allocation_date\": \"2026-06-01\",\n    \"period_start\": \"2026-06-01\",\n    \"period_end\": \"2026-06-30\",\n    \"amount\": 1000,\n    \"amount_in_tenant_currency\": 1000,\n    \"proration_strategy\": \"specific_line\",\n    \"proration_weight\": null,\n    \"amortization_period_id\": null,\n    \"is_amortized\": false,\n    \"created_at\": \"2026-06-01T10:05:00.000000Z\",\n    \"updated_at\": \"2026-06-01T10:05:00.000000Z\"\n  }\n}"}]},{"name":"Allocate Cost Entry to Brand","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/brand/{brand}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","brand",":brand"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"brand","value":"7","description":"Product brand ID"}]},"description":"Allocates part of a cost entry to a product brand.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: amount (required, the amount to allocate), allocation_date (the ledger date for the allocation, defaults to today), period_start and period_end (optional period the cost applies to).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"amount\": 500,\n  \"allocation_date\": \"2026-06-15\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/brand/{brand}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","brand",":brand"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"brand","value":"7","description":"Product brand ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 88,\n    \"cost_entry_id\": 42,\n    \"allocatable_type\": \"App\\\\Models\\\\Product\",\n    \"allocatable_type_label\": \"Product\",\n    \"allocatable_id\": 101,\n    \"allocatable\": {\n      \"id\": 101,\n      \"name\": \"Widget\",\n      \"sku\": \"WIDGET-001\"\n    },\n    \"allocation_date\": \"2026-06-01\",\n    \"period_start\": \"2026-06-01\",\n    \"period_end\": \"2026-06-30\",\n    \"amount\": 1000,\n    \"amount_in_tenant_currency\": 1000,\n    \"proration_strategy\": \"specific_line\",\n    \"proration_weight\": null,\n    \"amortization_period_id\": null,\n    \"is_amortized\": false,\n    \"created_at\": \"2026-06-01T10:05:00.000000Z\",\n    \"updated_at\": \"2026-06-01T10:05:00.000000Z\"\n  }\n}"}]},{"name":"Allocate Cost Entry to Supplier","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/supplier/{supplier}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","supplier",":supplier"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"supplier","value":"3","description":"Supplier ID"}]},"description":"Allocates part of a cost entry to a supplier.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: amount (required, the amount to allocate), allocation_date (the ledger date for the allocation, defaults to today), period_start and period_end (optional period the cost applies to).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"amount\": 500,\n  \"allocation_date\": \"2026-06-15\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/supplier/{supplier}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","supplier",":supplier"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"supplier","value":"3","description":"Supplier ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 88,\n    \"cost_entry_id\": 42,\n    \"allocatable_type\": \"App\\\\Models\\\\Product\",\n    \"allocatable_type_label\": \"Product\",\n    \"allocatable_id\": 101,\n    \"allocatable\": {\n      \"id\": 101,\n      \"name\": \"Widget\",\n      \"sku\": \"WIDGET-001\"\n    },\n    \"allocation_date\": \"2026-06-01\",\n    \"period_start\": \"2026-06-01\",\n    \"period_end\": \"2026-06-30\",\n    \"amount\": 1000,\n    \"amount_in_tenant_currency\": 1000,\n    \"proration_strategy\": \"specific_line\",\n    \"proration_weight\": null,\n    \"amortization_period_id\": null,\n    \"is_amortized\": false,\n    \"created_at\": \"2026-06-01T10:05:00.000000Z\",\n    \"updated_at\": \"2026-06-01T10:05:00.000000Z\"\n  }\n}"}]},{"name":"Allocate Cost Entry to Sales Channel","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/sales-channel/{salesChannel}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","sales-channel",":salesChannel"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"salesChannel","value":"2","description":"Sales channel ID"}]},"description":"Allocates part of a cost entry to a sales channel.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: amount (required, the amount to allocate), allocation_date (the ledger date for the allocation, defaults to today), period_start and period_end (optional period the cost applies to).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"amount\": 750,\n  \"allocation_date\": \"2026-06-15\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/sales-channel/{salesChannel}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","sales-channel",":salesChannel"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"salesChannel","value":"2","description":"Sales channel ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 88,\n    \"cost_entry_id\": 42,\n    \"allocatable_type\": \"App\\\\Models\\\\Product\",\n    \"allocatable_type_label\": \"Product\",\n    \"allocatable_id\": 101,\n    \"allocatable\": {\n      \"id\": 101,\n      \"name\": \"Widget\",\n      \"sku\": \"WIDGET-001\"\n    },\n    \"allocation_date\": \"2026-06-01\",\n    \"period_start\": \"2026-06-01\",\n    \"period_end\": \"2026-06-30\",\n    \"amount\": 1000,\n    \"amount_in_tenant_currency\": 1000,\n    \"proration_strategy\": \"specific_line\",\n    \"proration_weight\": null,\n    \"amortization_period_id\": null,\n    \"is_amortized\": false,\n    \"created_at\": \"2026-06-01T10:05:00.000000Z\",\n    \"updated_at\": \"2026-06-01T10:05:00.000000Z\"\n  }\n}"}]},{"name":"Allocate Cost Entry to Sales Order Line","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/sales-order-line/{salesOrderLine}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","sales-order-line",":salesOrderLine"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"salesOrderLine","value":"5501","description":"Sales order line ID"}]},"description":"Allocates part of a cost entry to a specific sales order line.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: amount (required, the amount to allocate), allocation_date (the ledger date for the allocation, defaults to today), period_start and period_end (optional period the cost applies to).\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"amount\": 120,\n  \"allocation_date\": \"2026-06-15\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/sales-order-line/{salesOrderLine}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","sales-order-line",":salesOrderLine"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"salesOrderLine","value":"5501","description":"Sales order line ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 88,\n    \"cost_entry_id\": 42,\n    \"allocatable_type\": \"App\\\\Models\\\\Product\",\n    \"allocatable_type_label\": \"Product\",\n    \"allocatable_id\": 101,\n    \"allocatable\": {\n      \"id\": 101,\n      \"name\": \"Widget\",\n      \"sku\": \"WIDGET-001\"\n    },\n    \"allocation_date\": \"2026-06-01\",\n    \"period_start\": \"2026-06-01\",\n    \"period_end\": \"2026-06-30\",\n    \"amount\": 1000,\n    \"amount_in_tenant_currency\": 1000,\n    \"proration_strategy\": \"specific_line\",\n    \"proration_weight\": null,\n    \"amortization_period_id\": null,\n    \"is_amortized\": false,\n    \"created_at\": \"2026-06-01T10:05:00.000000Z\",\n    \"updated_at\": \"2026-06-01T10:05:00.000000Z\"\n  }\n}"}]},{"name":"Allocate Cost Entry by Proration","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/proration","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","proration"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"}]},"description":"Allocates a cost entry across a custom list of products, splitting the amount by the chosen proration strategy.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequired fields: strategy (straight_line, front_loaded, back_loaded, revenue_based, cost_based, weight_based, volume_based, quantity_based, specific_line, manual) and items (array of {product_id, quantity, revenue, cost, weight, volume} - the strategy determines which measure drives each product's share).\n\nOptional: amount (defaults to the entry's unallocated amount), allocation_date (defaults to today), period_start, period_end.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"strategy\": \"quantity_based\",\n  \"amount\": 1500,\n  \"allocation_date\": \"2026-06-15\",\n  \"items\": [\n    {\n      \"product_id\": 101,\n      \"quantity\": 100,\n      \"revenue\": 0,\n      \"cost\": 0,\n      \"weight\": 0,\n      \"volume\": 0\n    },\n    {\n      \"product_id\": 102,\n      \"quantity\": 50,\n      \"revenue\": 0,\n      \"cost\": 0,\n      \"weight\": 0,\n      \"volume\": 0\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/proration","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","proration"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 88,\n      \"cost_entry_id\": 42,\n      \"allocatable_type\": \"App\\\\Models\\\\Product\",\n      \"allocatable_type_label\": \"Product\",\n      \"allocatable_id\": 101,\n      \"allocatable\": {\n        \"id\": 101,\n        \"name\": \"Widget\",\n        \"sku\": \"WIDGET-001\"\n      },\n      \"allocation_date\": \"2026-06-01\",\n      \"period_start\": \"2026-06-01\",\n      \"period_end\": \"2026-06-30\",\n      \"amount\": 1000,\n      \"amount_in_tenant_currency\": 1000,\n      \"proration_strategy\": \"specific_line\",\n      \"proration_weight\": null,\n      \"amortization_period_id\": null,\n      \"is_amortized\": false,\n      \"created_at\": \"2026-06-01T10:05:00.000000Z\",\n      \"updated_at\": \"2026-06-01T10:05:00.000000Z\"\n    }\n  ]\n}"}]},{"name":"Allocate Cost Entry to Purchase Order","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/purchase-order/{purchaseOrder}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","purchase-order",":purchaseOrder"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"purchaseOrder","value":"88","description":"Purchase order ID"}]},"description":"Allocates the cost entry across the lines of a purchase order, creating one allocation per product using the chosen proration strategy (default cost_based).\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: strategy (optional proration strategy: straight_line, front_loaded, back_loaded, revenue_based, cost_based, weight_based, volume_based, quantity_based, specific_line, manual), amount (optional - defaults to the cost entry's unallocated amount), allocation_date (defaults to today), period_start and period_end.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"strategy\": \"cost_based\",\n  \"amount\": 1500,\n  \"allocation_date\": \"2026-06-15\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/purchase-order/{purchaseOrder}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","purchase-order",":purchaseOrder"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"purchaseOrder","value":"88","description":"Purchase order ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"message\": \"Allocated to 3 products from the lines of a purchase order\",\n    \"allocations\": [\n      {\n        \"id\": 88,\n        \"cost_entry_id\": 42,\n        \"allocatable_type\": \"App\\\\Models\\\\Product\",\n        \"allocatable_type_label\": \"Product\",\n        \"allocatable_id\": 101,\n        \"allocatable\": {\n          \"id\": 101,\n          \"name\": \"Widget\",\n          \"sku\": \"WIDGET-001\"\n        },\n        \"allocation_date\": \"2026-06-01\",\n        \"period_start\": \"2026-06-01\",\n        \"period_end\": \"2026-06-30\",\n        \"amount\": 1000,\n        \"amount_in_tenant_currency\": 1000,\n        \"proration_strategy\": \"specific_line\",\n        \"proration_weight\": null,\n        \"amortization_period_id\": null,\n        \"is_amortized\": false,\n        \"created_at\": \"2026-06-01T10:05:00.000000Z\",\n        \"updated_at\": \"2026-06-01T10:05:00.000000Z\"\n      }\n    ]\n  }\n}"}]},{"name":"Allocate Cost Entry to Sales Order","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/sales-order/{salesOrder}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","sales-order",":salesOrder"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"salesOrder","value":"1204","description":"Sales order ID"}]},"description":"Allocates the cost entry across the lines of a sales order, creating one allocation per product using the chosen proration strategy (default revenue_based).\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: strategy (optional proration strategy: straight_line, front_loaded, back_loaded, revenue_based, cost_based, weight_based, volume_based, quantity_based, specific_line, manual), amount (optional - defaults to the cost entry's unallocated amount), allocation_date (defaults to today), period_start and period_end.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"strategy\": \"revenue_based\",\n  \"amount\": 1500,\n  \"allocation_date\": \"2026-06-15\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/sales-order/{salesOrder}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","sales-order",":salesOrder"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"salesOrder","value":"1204","description":"Sales order ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"message\": \"Allocated to 3 products from the lines of a sales order\",\n    \"allocations\": [\n      {\n        \"id\": 88,\n        \"cost_entry_id\": 42,\n        \"allocatable_type\": \"App\\\\Models\\\\Product\",\n        \"allocatable_type_label\": \"Product\",\n        \"allocatable_id\": 101,\n        \"allocatable\": {\n          \"id\": 101,\n          \"name\": \"Widget\",\n          \"sku\": \"WIDGET-001\"\n        },\n        \"allocation_date\": \"2026-06-01\",\n        \"period_start\": \"2026-06-01\",\n        \"period_end\": \"2026-06-30\",\n        \"amount\": 1000,\n        \"amount_in_tenant_currency\": 1000,\n        \"proration_strategy\": \"specific_line\",\n        \"proration_weight\": null,\n        \"amortization_period_id\": null,\n        \"is_amortized\": false,\n        \"created_at\": \"2026-06-01T10:05:00.000000Z\",\n        \"updated_at\": \"2026-06-01T10:05:00.000000Z\"\n      }\n    ]\n  }\n}"}]},{"name":"Allocate Cost Entry to Warehouse Transfer","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/warehouse-transfer/{warehouseTransfer}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","warehouse-transfer",":warehouseTransfer"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"warehouseTransfer","value":"512","description":"Warehouse transfer ID"}]},"description":"Allocates the cost entry across the lines of a warehouse transfer, creating one allocation per product using the chosen proration strategy (default quantity_based).\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: strategy (optional proration strategy: straight_line, front_loaded, back_loaded, revenue_based, cost_based, weight_based, volume_based, quantity_based, specific_line, manual), amount (optional - defaults to the cost entry's unallocated amount), allocation_date (defaults to today), period_start and period_end.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"strategy\": \"quantity_based\",\n  \"amount\": 1500,\n  \"allocation_date\": \"2026-06-15\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/warehouse-transfer/{warehouseTransfer}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","warehouse-transfer",":warehouseTransfer"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"warehouseTransfer","value":"512","description":"Warehouse transfer ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"message\": \"Allocated to 3 products from the lines of a warehouse transfer\",\n    \"allocations\": [\n      {\n        \"id\": 88,\n        \"cost_entry_id\": 42,\n        \"allocatable_type\": \"App\\\\Models\\\\Product\",\n        \"allocatable_type_label\": \"Product\",\n        \"allocatable_id\": 101,\n        \"allocatable\": {\n          \"id\": 101,\n          \"name\": \"Widget\",\n          \"sku\": \"WIDGET-001\"\n        },\n        \"allocation_date\": \"2026-06-01\",\n        \"period_start\": \"2026-06-01\",\n        \"period_end\": \"2026-06-30\",\n        \"amount\": 1000,\n        \"amount_in_tenant_currency\": 1000,\n        \"proration_strategy\": \"specific_line\",\n        \"proration_weight\": null,\n        \"amortization_period_id\": null,\n        \"is_amortized\": false,\n        \"created_at\": \"2026-06-01T10:05:00.000000Z\",\n        \"updated_at\": \"2026-06-01T10:05:00.000000Z\"\n      }\n    ]\n  }\n}"}]},{"name":"Allocate Cost Entry to Inventory Assembly","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/inventory-assembly/{inventoryAssembly}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","inventory-assembly",":inventoryAssembly"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"inventoryAssembly","value":"33","description":"Inventory assembly ID"}]},"description":"Allocates the cost entry across the component lines of an inventory assembly, creating one allocation per product using the chosen proration strategy (default cost_based).\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: strategy (optional proration strategy: straight_line, front_loaded, back_loaded, revenue_based, cost_based, weight_based, volume_based, quantity_based, specific_line, manual), amount (optional - defaults to the cost entry's unallocated amount), allocation_date (defaults to today), period_start and period_end.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"strategy\": \"cost_based\",\n  \"amount\": 1500,\n  \"allocation_date\": \"2026-06-15\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/inventory-assembly/{inventoryAssembly}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","inventory-assembly",":inventoryAssembly"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"inventoryAssembly","value":"33","description":"Inventory assembly ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"message\": \"Allocated to 3 products from the component lines of an inventory assembly\",\n    \"allocations\": [\n      {\n        \"id\": 88,\n        \"cost_entry_id\": 42,\n        \"allocatable_type\": \"App\\\\Models\\\\Product\",\n        \"allocatable_type_label\": \"Product\",\n        \"allocatable_id\": 101,\n        \"allocatable\": {\n          \"id\": 101,\n          \"name\": \"Widget\",\n          \"sku\": \"WIDGET-001\"\n        },\n        \"allocation_date\": \"2026-06-01\",\n        \"period_start\": \"2026-06-01\",\n        \"period_end\": \"2026-06-30\",\n        \"amount\": 1000,\n        \"amount_in_tenant_currency\": 1000,\n        \"proration_strategy\": \"specific_line\",\n        \"proration_weight\": null,\n        \"amortization_period_id\": null,\n        \"is_amortized\": false,\n        \"created_at\": \"2026-06-01T10:05:00.000000Z\",\n        \"updated_at\": \"2026-06-01T10:05:00.000000Z\"\n      }\n    ]\n  }\n}"}]},{"name":"Allocate Cost Entry to Stock Take","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/stock-take/{stockTake}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","stock-take",":stockTake"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"stockTake","value":"19","description":"Stock take ID"}]},"description":"Allocates the cost entry across the items of a stock take, creating one allocation per product using the chosen proration strategy (default quantity_based).\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields: strategy (optional proration strategy: straight_line, front_loaded, back_loaded, revenue_based, cost_based, weight_based, volume_based, quantity_based, specific_line, manual), amount (optional - defaults to the cost entry's unallocated amount), allocation_date (defaults to today), period_start and period_end.\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"strategy\": \"quantity_based\",\n  \"amount\": 1500,\n  \"allocation_date\": \"2026-06-15\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-entries/{costEntry}/allocate/stock-take/{stockTake}","host":["{{baseUrl}}"],"path":["api","cost-entries",":costEntry","allocate","stock-take",":stockTake"],"variable":[{"key":"costEntry","value":"42","description":"Cost entry ID"},{"key":"stockTake","value":"19","description":"Stock take ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"message\": \"Allocated to 3 products from the items of a stock take\",\n    \"allocations\": [\n      {\n        \"id\": 88,\n        \"cost_entry_id\": 42,\n        \"allocatable_type\": \"App\\\\Models\\\\Product\",\n        \"allocatable_type_label\": \"Product\",\n        \"allocatable_id\": 101,\n        \"allocatable\": {\n          \"id\": 101,\n          \"name\": \"Widget\",\n          \"sku\": \"WIDGET-001\"\n        },\n        \"allocation_date\": \"2026-06-01\",\n        \"period_start\": \"2026-06-01\",\n        \"period_end\": \"2026-06-30\",\n        \"amount\": 1000,\n        \"amount_in_tenant_currency\": 1000,\n        \"proration_strategy\": \"specific_line\",\n        \"proration_weight\": null,\n        \"amortization_period_id\": null,\n        \"is_amortized\": false,\n        \"created_at\": \"2026-06-01T10:05:00.000000Z\",\n        \"updated_at\": \"2026-06-01T10:05:00.000000Z\"\n      }\n    ]\n  }\n}"}]},{"name":"List Cost Entry Types","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-entry-types","host":["{{baseUrl}}"],"path":["api","cost-entry-types"]},"description":"Returns all cost entry types, ordered alphabetically by name. Cost entry types are user-defined categories used to classify cost entries (for example Freight, Duty, Warehousing) for landed cost and profitability tracking.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThis endpoint is not paginated and accepts no filter or sort parameters - the complete list is always returned.\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-entry-types","host":["{{baseUrl}}"],"path":["api","cost-entry-types"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3,\n      \"name\": \"Duty\",\n      \"created_at\": \"2026-05-12T09:31:00.000000Z\",\n      \"updated_at\": \"2026-05-12T09:31:00.000000Z\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Freight\",\n      \"created_at\": \"2026-05-12T09:30:00.000000Z\",\n      \"updated_at\": \"2026-05-12T09:30:00.000000Z\"\n    },\n    {\n      \"id\": 5,\n      \"name\": \"Warehousing\",\n      \"created_at\": \"2026-05-20T14:05:00.000000Z\",\n      \"updated_at\": \"2026-05-20T14:05:00.000000Z\"\n    }\n  ]\n}"}]},{"name":"Create Cost Entry Type","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-entry-types","host":["{{baseUrl}}"],"path":["api","cost-entry-types"]},"description":"Creates a new cost entry type.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields:\n- name (required, string, max 100 characters): display name of the category, e.g. \"Freight\", \"Duty\" or \"Warehousing\".\n\nReturns the created cost entry type. Note that this endpoint returns HTTP 200 (not 201) on success.\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write).","body":{"mode":"raw","raw":"{\n  \"name\": \"Freight\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-entry-types","host":["{{baseUrl}}"],"path":["api","cost-entry-types"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 2,\n    \"name\": \"Freight\",\n    \"created_at\": \"2026-05-12T09:30:00.000000Z\",\n    \"updated_at\": \"2026-05-12T09:30:00.000000Z\"\n  }\n}"}]},{"name":"Get Cost Entry Type","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-entry-types/{costEntryType}","host":["{{baseUrl}}"],"path":["api","cost-entry-types",":costEntryType"],"variable":[{"key":"costEntryType","value":"2","description":"Cost entry type ID"}]},"description":"Returns a single cost entry type by ID.\n\n**Required scope: `purchase-orders:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-entry-types/{costEntryType}","host":["{{baseUrl}}"],"path":["api","cost-entry-types",":costEntryType"],"variable":[{"key":"costEntryType","value":"2","description":"Cost entry type ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 2,\n    \"name\": \"Freight\",\n    \"created_at\": \"2026-05-12T09:30:00.000000Z\",\n    \"updated_at\": \"2026-05-12T09:30:00.000000Z\"\n  }\n}"}]},{"name":"Update Cost Entry Type","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-entry-types/{costEntryType}","host":["{{baseUrl}}"],"path":["api","cost-entry-types",":costEntryType"],"variable":[{"key":"costEntryType","value":"2","description":"Cost entry type ID"}]},"description":"Updates the name of an existing cost entry type. Cost entries already assigned to the type keep their assignment and reflect the new name.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFields:\n- name (required, string, max 100 characters): the new display name.\n\nReturns the updated cost entry type.\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write).","body":{"mode":"raw","raw":"{\n  \"name\": \"Ocean Freight\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/cost-entry-types/{costEntryType}","host":["{{baseUrl}}"],"path":["api","cost-entry-types",":costEntryType"],"variable":[{"key":"costEntryType","value":"2","description":"Cost entry type ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 2,\n    \"name\": \"Ocean Freight\",\n    \"created_at\": \"2026-05-12T09:30:00.000000Z\",\n    \"updated_at\": \"2026-06-15T11:42:00.000000Z\"\n  }\n}"}]},{"name":"Delete Cost Entry Type","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/cost-entry-types/{costEntryType}","host":["{{baseUrl}}"],"path":["api","cost-entry-types",":costEntryType"],"variable":[{"key":"costEntryType","value":"5","description":"Cost entry type ID"}]},"description":"Deletes a cost entry type.\n\n**Required scope: `purchase-orders:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA type that is currently assigned to one or more cost entries cannot be deleted - the request fails with HTTP 422. Reassign or delete the cost entries using the type first.\n\nAuthentication: Requires Bearer token. Scope: `purchase-orders` (read/write)."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/cost-entry-types/{costEntryType}","host":["{{baseUrl}}"],"path":["api","cost-entry-types",":costEntryType"],"variable":[{"key":"costEntryType","value":"5","description":"Cost entry type ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Cost entry type deleted successfully.\"\n}"}]},{"name":"Get FIFO Layer","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/fifo-layers/{id}","host":["{{baseUrl}}"],"path":["api","fifo-layers",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns a single FIFO layer by ID with related product and warehouse data.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFor layers produced by an outsourced manufacturing order, `origin.outsourced_purchase_order` carries the backing purchase order (`id`, `purchase_order_number`) the goods physically arrived through — the layer itself stays linked to the manufacturing output for production genealogy."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/fifo-layers/{id}","host":["{{baseUrl}}"],"path":["api","fifo-layers",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"product_id\": 10,\n    \"fifo_layer_date\": \"2024-01-15\",\n    \"sku\": \"PROD-A-001\",\n    \"product_name\": \"Product A\",\n    \"product_barcode\": \"123456789\",\n    \"batch_number\": \"LOT-2026-0042\",\n    \"manufacture_date\": \"2026-01-04T00:00:00.000000Z\",\n    \"expiry_date\": \"2026-07-04T00:00:00.000000Z\",\n    \"lot_notes\": \"Original shipped lot\",\n    \"days_to_expiry\": 18,\n    \"original_quantity\": 100,\n    \"fulfilled_quantity\": 40,\n    \"avg_cost\": 5.5,\n    \"total_cost\": 550,\n    \"origin\": {\n      \"link_type\": \"purchase_order\",\n      \"link_id\": 42,\n      \"name\": \"PO-2024-001\",\n      \"parent_link_id\": null,\n      \"detail_link_id\": null,\n      \"reference\": \"PO-2024-001\",\n      \"manufacturing_order\": null\n    },\n    \"warehouse\": {\n      \"id\": 2,\n      \"name\": \"Main Warehouse\"\n    },\n    \"is_fba\": false,\n    \"is_consignment\": false,\n    \"condition\": \"new\",\n    \"integration_unique_id\": null,\n    \"allocated_quantity\": 20,\n    \"available_quantity\": 60,\n    \"archived_at\": null,\n    \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n    \"updated_at\": \"2024-01-15T10:00:00.000000Z\"\n  }\n}"}]},{"name":"Archive FIFO Layer","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/fifo-layers/{id}/archive","host":["{{baseUrl}}"],"path":["api","fifo-layers",":id","archive"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Archives a single FIFO layer. Only fully utilized layers (available_quantity === 0) can be archived.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/fifo-layers/{id}/archive","host":["{{baseUrl}}"],"path":["api","fifo-layers",":id","archive"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"fifo layer 1 archived successfully.\",\n  \"data\": {\n    \"id\": 1,\n    \"available_quantity\": 0,\n    \"archived_at\": \"2024-06-01T12:00:00.000000Z\"\n  }\n}"}]},{"name":"Unarchive FIFO Layer","request":{"method":"PUT","header":[],"url":{"raw":"{{baseUrl}}/api/fifo-layers/{id}/unarchived","host":["{{baseUrl}}"],"path":["api","fifo-layers",":id","unarchived"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Unarchives a previously archived FIFO layer.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/fifo-layers/{id}/unarchived","host":["{{baseUrl}}"],"path":["api","fifo-layers",":id","unarchived"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"fifo layer 1 unarchived successfully.\",\n  \"data\": {\n    \"id\": 1,\n    \"archived_at\": null\n  }\n}"}]},{"name":"Bulk Archive FIFO Layers","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/fifo-layers/archive","host":["{{baseUrl}}"],"path":["api","fifo-layers","archive"]},"description":"Archives multiple FIFO layers by ID. Only fully utilized layers are archived; partially available layers are skipped.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody:\n- ids (required): Array of FIFO layer IDs to archive","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/fifo-layers/archive","host":["{{baseUrl}}"],"path":["api","fifo-layers","archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 of 3 fifo layers archived successfully.\"\n}"}]},{"name":"Bulk Unarchive FIFO Layers","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/fifo-layers/unarchive","host":["{{baseUrl}}"],"path":["api","fifo-layers","unarchive"]},"description":"Unarchives multiple FIFO layers by ID.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody:\n- ids (required): Array of FIFO layer IDs to unarchive","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/fifo-layers/unarchive","host":["{{baseUrl}}"],"path":["api","fifo-layers","unarchive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"3 of 3 fifo layers unarchived successfully.\"\n}"}]},{"name":"Check FIFO Layers Archivable","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/fifo-layers/archivable","host":["{{baseUrl}}"],"path":["api","fifo-layers","archivable"]},"description":"Checks whether each specified FIFO layer can be archived (i.e., available_quantity === 0). Returns a per-layer result with archivable flag and reason.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody:\n- ids (required): Array of FIFO layer IDs to check (min 1, must exist)","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/fifo-layers/archivable","host":["{{baseUrl}}"],"path":["api","fifo-layers","archivable"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"archivable\": true,\n      \"reason\": null\n    },\n    {\n      \"id\": 2,\n      \"archivable\": false,\n      \"reason\": {\n        \"unfulfilledQuantity\": \"fifo layer 2 cannot be archived because it is not fully fulfilled.\"\n      }\n    }\n  ]\n}"}]},{"name":"Get FIFO Layer Inventory Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/fifo-layers/{id}/inventory-movements?page=1&per_page=15","host":["{{baseUrl}}"],"path":["api","fifo-layers",":id","inventory-movements"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"per_page","value":"15","description":"Items per page","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns a paginated list of inventory movements linked to a specific FIFO layer.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/fifo-layers/{id}/inventory-movements?page=1&per_page=15","host":["{{baseUrl}}"],"path":["api","fifo-layers",":id","inventory-movements"],"query":[{"key":"page","value":"1","description":"Page number","disabled":true},{"key":"per_page","value":"15","description":"Items per page","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 100,\n      \"inventory_movement_date\": \"2024-01-15\",\n      \"product_id\": 10,\n      \"type\": {\n        \"name\": \"purchase_receipt\",\n        \"link_type\": \"purchase_order\",\n        \"link_id\": 42,\n        \"parent_link_id\": null,\n        \"detail_link_id\": null,\n        \"manufacturing_order\": null\n      },\n      \"inventory_status\": \"on_hand\",\n      \"quantity\": 100,\n      \"warehouse\": {\n        \"id\": 2,\n        \"name\": \"Main Warehouse\"\n      },\n      \"layer\": {\n        \"fifo_layer_id\": 1,\n        \"name\": \"PO-2024-001\"\n      },\n      \"fifo_layer_id\": 1,\n      \"reference\": \"PO-2024-001\",\n      \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2024-01-15T10:00:00.000000Z\"\n    }\n  ],\n  \"per_page\": 15,\n  \"total\": 1\n}"}]},{"name":"Get FIFO Layer Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/fifo-layers/{id}/activity-log?per_page=15&filter[search]=archive","host":["{{baseUrl}}"],"path":["api","fifo-layers",":id","activity-log"],"query":[{"key":"per_page","value":"15","description":"Items per page (default: 15)","disabled":true},{"key":"filter[search]","value":"archive","description":"Search in causer name, properties, event, or subject_type","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns a paginated activity log for a specific FIFO layer. Shows who changed the layer and what was changed.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/fifo-layers/{id}/activity-log?per_page=15&filter[search]=archive","host":["{{baseUrl}}"],"path":["api","fifo-layers",":id","activity-log"],"query":[{"key":"per_page","value":"15","description":"Items per page (default: 15)","disabled":true},{"key":"filter[search]","value":"archive","description":"Search in causer name, properties, event, or subject_type","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 500,\n      \"description\": \"updated\",\n      \"event\": \"updated\",\n      \"subject_type\": \"FifoLayer\",\n      \"subject_id\": 1,\n      \"properties\": {\n        \"attributes\": {\n          \"archived_at\": \"2024-06-01 12:00:00\"\n        },\n        \"old\": {\n          \"archived_at\": null\n        }\n      },\n      \"causer_name\": \"Dev User\",\n      \"created_at\": \"2024-06-01T12:00:00.000000Z\"\n    }\n  ],\n  \"per_page\": 15,\n  \"total\": 1\n}"}]},{"name":"Recalculate Total Costs","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/fifo-layers/recalculate-total-costs","host":["{{baseUrl}}"],"path":["api","fifo-layers","recalculate-total-costs"]},"description":"Re-derive the cost of the given FIFO layers from their source documents, through the cost-change review process.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody:\n- ids (required, array of integers, min 1): FIFO layer IDs to recalculate (each must exist)\n- note (optional, string, max 500): Recorded on the resulting cost change as the reason for the recalculation\n\nLayers that were received against a purchase order are grouped by order. For each order whose layers would move, one Data Fix cost change is raised and your cost-change policy for Data Fix decides what happens: with the default review rule the change is staged as a pending cost change and nothing is written until it is resolved (choose Restate to date each layer's revaluation at its own receipt, or Going forward to date it today); with an auto rule the change applies at once. Orders whose layers already carry the cost their documents imply report `no_movement` and nothing is staged. Layers with no purchase order (opening balances, assemblies, transfers) are recalculated directly and listed in `unattached_layer_ids`.\n\nResponse:\n- orders[]: one entry per purchase order — `purchase_order_id`, `purchase_order_number`, `layer_count`, `delta_total` (in your base currency), `outcome` (`staged`, `applied`, or `no_movement`) and `cost_change_set_id` (set when staged)\n- unattached_layer_ids[]: layers recalculated directly\n- message: a one-line summary of the outcome","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    901431,\n    901432,\n    901433\n  ],\n  \"note\": \"Re-cost after the settlement discount was applied\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/fifo-layers/recalculate-total-costs","host":["{{baseUrl}}"],"path":["api","fifo-layers","recalculate-total-costs"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"orders\": [\n      {\n        \"purchase_order_id\": 24304,\n        \"purchase_order_number\": \"PO-22583\",\n        \"layer_count\": 3,\n        \"delta_total\": -2.0007,\n        \"outcome\": \"staged\",\n        \"cost_change_set_id\": 1612\n      }\n    ],\n    \"unattached_layer_ids\": []\n  },\n  \"message\": \"1 cost change staged for review in the Cost Change Inbox.\"\n}"}]},{"name":"Get FIFO Layer Parents","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/fifo-layers/{id}/parents","host":["{{baseUrl}}"],"path":["api","fifo-layers",":id","parents"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns the immediate (depth-1) parent FIFO layers consumed to produce the given output layer, with the aggregated quantity consumed from each parent. Used by the FIFO Layer detail page's 'Consumed From' panel to render the upstream lots that were assembled into this batch.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFor standard MO output layers, parents are resolved by walking inventory_movements where movement.fifo_layer_id points at a consumption (component) movement and the resulting upstream FIFO layer is the parent. For Simple Mode (assemble/disassemble) outputs, the same walk works because the consumption movements link back to component FIFO layers identically.\n\nReturns an empty `data` array for raw layers (e.g. purchase receipts) with no upstream consumption movements.\n\nResults are sorted: most recently produced parents first, then by largest quantity consumed.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/fifo-layers/{id}/parents","host":["{{baseUrl}}"],"path":["api","fifo-layers",":id","parents"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": \"parent-3\",\n      \"fifo_layer_id\": 3,\n      \"entity_type\": \"purchase_order\",\n      \"reference\": \"PO-2024-014\",\n      \"batch_number\": \"LBL-2024-014\",\n      \"product_id\": 51,\n      \"product_name\": \"Adhesive Label 60x40\",\n      \"sku\": \"LBL-60x40\",\n      \"manufacture_date\": \"2024-05-01\",\n      \"fifo_layer_date\": \"2024-05-12\",\n      \"quantity_consumed\": 2,\n      \"lot_notes\": \"Original shipped lot\"\n    },\n    {\n      \"id\": \"parent-2\",\n      \"fifo_layer_id\": 2,\n      \"entity_type\": \"purchase_order\",\n      \"reference\": \"PO-2024-011\",\n      \"batch_number\": \"POUCH-2024-011\",\n      \"product_id\": 47,\n      \"product_name\": \"Empty 250g Pouch\",\n      \"sku\": \"POUCH-250\",\n      \"manufacture_date\": \"2024-04-28\",\n      \"fifo_layer_date\": \"2024-05-10\",\n      \"quantity_consumed\": 2,\n      \"lot_notes\": \"Original shipped lot\"\n    }\n  ]\n}"}]},{"name":"List FIFO Layers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/fifo-layers?page=1&limit=15&sort=fifo_layer_date&search=&archived=0&included[]=&excluded[]=","host":["{{baseUrl}}"],"path":["api","fifo-layers"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"15","description":"Items per page (default: 10)","disabled":true},{"key":"sort","value":"fifo_layer_date","description":"Sort field. Prefix with - for descending (e.g. -fifo_layer_date). Allowed: id, fifo_layer_date, avg_cost, total_cost, original_quantity, fulfilled_quantity, created_at, updated_at","disabled":true},{"key":"search","value":"","description":"Search term across SKU, product name, reference","disabled":true},{"key":"archived","value":"0","description":"0 = exclude archived, 1 = include archived","disabled":true},{"key":"included[]","value":"","description":"Column names to include in response","disabled":true},{"key":"excluded[]","value":"","description":"Column names to exclude from response","disabled":true}]},"description":"Returns a paginated list of FIFO layers. Supports filtering, sorting, and column inclusion/exclusion.\n\nAuthentication: Requires Bearer token.\n\nAllowed sorts: id, fifo_layer_date, avg_cost, total_cost, original_quantity, fulfilled_quantity, created_at, updated_at"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/fifo-layers?page=1&limit=15&sort=fifo_layer_date&search=&archived=0&included[]=&excluded[]=","host":["{{baseUrl}}"],"path":["api","fifo-layers"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"limit","value":"15","description":"Items per page (default: 10)","disabled":true},{"key":"sort","value":"fifo_layer_date","description":"Sort field. Prefix with - for descending (e.g. -fifo_layer_date). Allowed: id, fifo_layer_date, avg_cost, total_cost, original_quantity, fulfilled_quantity, created_at, updated_at","disabled":true},{"key":"search","value":"","description":"Search term across SKU, product name, reference","disabled":true},{"key":"archived","value":"0","description":"0 = exclude archived, 1 = include archived","disabled":true},{"key":"included[]","value":"","description":"Column names to include in response","disabled":true},{"key":"excluded[]","value":"","description":"Column names to exclude from response","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"product_id\": 10,\n      \"fifo_layer_date\": \"2024-01-15\",\n      \"sku\": \"PROD-A-001\",\n      \"product_name\": \"Product A\",\n      \"product_barcode\": \"123456789\",\n      \"original_quantity\": 100,\n      \"fulfilled_quantity\": 40,\n      \"avg_cost\": 5.5,\n      \"total_cost\": 550,\n      \"origin\": {\n        \"link_type\": \"purchase_order\",\n        \"link_id\": 42,\n        \"name\": \"PO-2024-001\",\n        \"parent_link_id\": null,\n        \"detail_link_id\": null,\n        \"reference\": \"PO-2024-001\",\n        \"manufacturing_order\": null\n      },\n      \"warehouse\": {\n        \"id\": 2,\n        \"name\": \"Main Warehouse\"\n      },\n      \"is_fba\": false,\n      \"condition\": \"new\",\n      \"integration_unique_id\": null,\n      \"allocated_quantity\": 20,\n      \"available_quantity\": 60,\n      \"archived_at\": null,\n      \"created_at\": \"2024-01-15T10:00:00.000000Z\",\n      \"updated_at\": \"2024-01-15T10:00:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1\n}"}]},{"name":"List SKU COGS Health","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs/sku-health?page=1&per_page=50&sort_by=sku&sort_direction=asc&search=WIDGET&variation_min=5&variation_max=50","host":["{{baseUrl}}"],"path":["api","cogs","sku-health"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"50","description":"Items per page (default: 50)","disabled":true},{"key":"sort_by","value":"sku","description":"Sort field. Options: sku, product_name, unit_cost, variation_pct, total_quantity","disabled":true},{"key":"sort_direction","value":"asc","description":"Sort direction: asc or desc","disabled":true},{"key":"search","value":"WIDGET","description":"Search by SKU or product name","disabled":true},{"key":"variation_min","value":"5","description":"Minimum COGS variation percentage filter","disabled":true},{"key":"variation_max","value":"50","description":"Maximum COGS variation percentage filter","disabled":true}]},"description":"Returns paginated SKU COGS health metrics. Each item includes unit cost, initial COGS, statistics (min/max/median/avg/variation), and inventory totals.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nSort fields: sku, product_name, unit_cost, variation_pct, total_quantity (default: sku asc)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs/sku-health?page=1&per_page=50&sort_by=sku&sort_direction=asc&search=WIDGET&variation_min=5&variation_max=50","host":["{{baseUrl}}"],"path":["api","cogs","sku-health"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"50","description":"Items per page (default: 50)","disabled":true},{"key":"sort_by","value":"sku","description":"Sort field. Options: sku, product_name, unit_cost, variation_pct, total_quantity","disabled":true},{"key":"sort_direction","value":"asc","description":"Sort direction: asc or desc","disabled":true},{"key":"search","value":"WIDGET","description":"Search by SKU or product name","disabled":true},{"key":"variation_min","value":"5","description":"Minimum COGS variation percentage filter","disabled":true},{"key":"variation_max","value":"50","description":"Maximum COGS variation percentage filter","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"product_id\": 101,\n      \"sku\": \"WIDGET-RED-L\",\n      \"product_name\": \"Red Widget Large\",\n      \"unit_cost\": 12.5,\n      \"initial_cogs\": 12,\n      \"cogs_statistics\": {\n        \"min\": 10,\n        \"max\": 15,\n        \"median\": 12.5,\n        \"avg\": 12.33,\n        \"variation_pct\": 16.67\n      },\n      \"inventory\": {\n        \"total_quantity\": 500,\n        \"total_value\": 6250,\n        \"active_layer_count\": 3\n      }\n    }\n  ],\n  \"per_page\": 50,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1,\n  \"last_page\": 1\n}"}]},{"name":"Search SKU COGS Health","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cogs/sku-health","host":["{{baseUrl}}"],"path":["api","cogs","sku-health"]},"description":"Returns the same paginated per-SKU COGS health table as the GET version of this endpoint, but accepts its parameters in a JSON request body. Each row aggregates a product's cost-of-goods statistics (min, max, median, average, and percentage variation) across its active FIFO cost layers, alongside current inventory totals.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nUnlike the other advanced list endpoints, this one does not accept a `filter_groups` tree. It reads a small, fixed set of fields:\n- `search`: matches against SKU or product name.\n- `variation_min` / `variation_max`: bound the COGS variation percentage.\n- `sort_by`: column to sort by (defaults to `sku`).\n- `sort_direction`: `asc` or `desc` (defaults to `asc`).\n- `page` / `per_page`: pagination (per_page defaults to 50).","body":{"mode":"raw","raw":"{\n  \"search\": \"WIDGET\",\n  \"variation_min\": 15,\n  \"variation_max\": 100,\n  \"sort_by\": \"variation_pct\",\n  \"sort_direction\": \"desc\",\n  \"page\": 1,\n  \"per_page\": 50\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cogs/sku-health","host":["{{baseUrl}}"],"path":["api","cogs","sku-health"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 3312,\n      \"sku\": \"WIDGET-BLUE-01\",\n      \"product_name\": \"Blue Widget\",\n      \"unit_cost\": 13.75,\n      \"initial_cogs\": 12.5,\n      \"cogs_statistics\": {\n        \"min\": 11.2,\n        \"max\": 15.9,\n        \"median\": 13.4,\n        \"avg\": 13.62,\n        \"variation_pct\": 34.8\n      },\n      \"inventory\": {\n        \"total_quantity\": 240,\n        \"total_value\": 3268.8,\n        \"active_layer_count\": 5\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"per_page\": 50,\n  \"from\": 1,\n  \"to\": 1,\n  \"last_page\": 3,\n  \"total\": 118,\n  \"next_page_url\": \"https://api.example.com/api/cogs/sku-health?page=2\",\n  \"prev_page_url\": null\n}"}]},{"name":"Get SKU COGS Health Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs/sku-health/summary?search=WIDGET&variation_min=5&variation_max=50","host":["{{baseUrl}}"],"path":["api","cogs","sku-health","summary"],"query":[{"key":"search","value":"WIDGET","description":"Filter by SKU or product name","disabled":true},{"key":"variation_min","value":"5","description":"Minimum variation % filter","disabled":true},{"key":"variation_max","value":"50","description":"Maximum variation % filter","disabled":true}]},"description":"Returns aggregate summary statistics for the COGS health dashboard — totals across all SKUs matching the given filters.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs/sku-health/summary?search=WIDGET&variation_min=5&variation_max=50","host":["{{baseUrl}}"],"path":["api","cogs","sku-health","summary"],"query":[{"key":"search","value":"WIDGET","description":"Filter by SKU or product name","disabled":true},{"key":"variation_min","value":"5","description":"Minimum variation % filter","disabled":true},{"key":"variation_max","value":"50","description":"Maximum variation % filter","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_products\": 120,\n    \"products_with_variation\": 35,\n    \"avg_variation_pct\": 8.42,\n    \"max_variation_pct\": 52.1,\n    \"total_inventory_value\": 485320.75,\n    \"products_at_risk\": 12\n  }\n}"}]},{"name":"Get SKU COGS Health Detail","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{id}","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns detailed COGS health metrics for a specific product by product ID.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Scope:** The top-level metrics (`cogs_avg`, `cogs_min`, `cogs_max`, `cogs_median`, `variation_pct`, `total_quantity`, `total_value`, `active_layer_count`, `init_cogs`) cover the **warehouse FIFO ledger only**. Amazon FBA stock — which uses a separate WAC ledger and has no `warehouse_id` — is intentionally excluded so this matches the cache-backed `inventory_avg_cost` displayed on the product Overview page.\n\n**Amazon FBA inventory** (if the product has any) is returned as a separate `amazon_summary` object with its own quantity-weighted average, min/max cost, layer count, and integration instance count. `amazon_summary` is `null` when the product has no Amazon FBA stock.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{id}","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product_id\": 101,\n    \"sku\": \"WIDGET-RED-L\",\n    \"product_name\": \"Red Widget Large\",\n    \"unit_cost\": 12.5,\n    \"init_cogs\": 12,\n    \"cogs_min\": 10,\n    \"cogs_max\": 15,\n    \"cogs_median\": 12.5,\n    \"cogs_avg\": 12.33,\n    \"cogs_stddev\": 1.85,\n    \"variation_pct\": 16.67,\n    \"total_quantity\": 500,\n    \"total_value\": 6250,\n    \"active_layer_count\": 3,\n    \"amazon_summary\": {\n      \"total_quantity\": 1010,\n      \"total_value\": 1304.92,\n      \"cogs_avg\": 1.292,\n      \"cogs_min\": 1.15,\n      \"cogs_max\": 1.42,\n      \"active_layer_count\": 4,\n      \"integration_instance_count\": 1\n    }\n  }\n}"}]},{"name":"Get All Value Sources","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs/sku-health/value-sources?page=1&per_page=50&sort_by=sku&sort_direction=asc&search=WIDGET","host":["{{baseUrl}}"],"path":["api","cogs","sku-health","value-sources"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"50","description":"Items per page (default: 50)","disabled":true},{"key":"sort_by","value":"sku","description":"Sort field","disabled":true},{"key":"sort_direction","value":"asc","description":"Sort direction: asc or desc","disabled":true},{"key":"search","value":"WIDGET","description":"Search by SKU or product name","disabled":true}]},"description":"Returns paginated COGS value sources across all products.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs/sku-health/value-sources?page=1&per_page=50&sort_by=sku&sort_direction=asc&search=WIDGET","host":["{{baseUrl}}"],"path":["api","cogs","sku-health","value-sources"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"50","description":"Items per page (default: 50)","disabled":true},{"key":"sort_by","value":"sku","description":"Sort field","disabled":true},{"key":"sort_direction","value":"asc","description":"Sort direction: asc or desc","disabled":true},{"key":"search","value":"WIDGET","description":"Search by SKU or product name","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"product_id\": 101,\n      \"sku\": \"WIDGET-RED-L\",\n      \"product_name\": \"Red Widget Large\",\n      \"value_source\": \"purchase_order\",\n      \"unit_cost\": 12.5\n    }\n  ],\n  \"per_page\": 50,\n  \"total\": 1,\n  \"last_page\": 1\n}"}]},{"name":"Get Warehouse Breakdown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{id}/warehouse-breakdown","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":id","warehouse-breakdown"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns the COGS warehouse breakdown for a specific product, showing how inventory and cost is distributed across warehouses.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{id}/warehouse-breakdown","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":id","warehouse-breakdown"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"warehouse_id\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"total_quantity\": 300,\n      \"total_value\": 3750,\n      \"avg_cost\": 12.5,\n      \"active_layer_count\": 2\n    },\n    {\n      \"warehouse_id\": 2,\n      \"warehouse_name\": \"Secondary Warehouse\",\n      \"total_quantity\": 200,\n      \"total_value\": 2500,\n      \"avg_cost\": 12.5,\n      \"active_layer_count\": 1\n    }\n  ]\n}"}]},{"name":"Get Initial Inventory","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{id}/initial-inventory","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":id","initial-inventory"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns the initial inventory breakdown by warehouse for a specific product.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{id}/initial-inventory","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":id","initial-inventory"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"warehouse_id\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"quantity\": 200,\n      \"unit_cost\": 12,\n      \"total_value\": 2400\n    }\n  ]\n}"}]},{"name":"Get FIFO Layers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{id}/fifo-layers?warehouse_id=1","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":id","fifo-layers"],"query":[{"key":"warehouse_id","value":"1","description":"Optional: filter by warehouse ID","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns current active FIFO layers for a specific product, optionally filtered by warehouse.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{id}/fifo-layers?warehouse_id=1","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":id","fifo-layers"],"query":[{"key":"warehouse_id","value":"1","description":"Optional: filter by warehouse ID","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 501,\n      \"fifo_layer_date\": \"2024-01-15T00:00:00.000000Z\",\n      \"original_quantity\": 100,\n      \"remaining_quantity\": 75,\n      \"unit_cost\": 12.5,\n      \"total_value\": 937.5,\n      \"warehouse_id\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"link_type\": \"App\\\\Models\\\\PurchaseOrderReceiving\",\n      \"link_id\": 201\n    }\n  ]\n}"}]},{"name":"Get FIFO Layers Timeline","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{id}/fifo-layers-timeline?page=1&per_page=20&warehouse_id=1&warehouse_type=main","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":id","fifo-layers-timeline"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"20","description":"Items per page (default: 20)","disabled":true},{"key":"warehouse_id","value":"1","description":"Optional: filter by warehouse ID","disabled":true},{"key":"warehouse_type","value":"main","description":"Optional: filter by warehouse type","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns paginated FIFO layer parent records (timeline) for a specific product. Used to show the historical sequence of inventory additions.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{id}/fifo-layers-timeline?page=1&per_page=20&warehouse_id=1&warehouse_type=main","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":id","fifo-layers-timeline"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"20","description":"Items per page (default: 20)","disabled":true},{"key":"warehouse_id","value":"1","description":"Optional: filter by warehouse ID","disabled":true},{"key":"warehouse_type","value":"main","description":"Optional: filter by warehouse type","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 501,\n      \"fifo_layer_date\": \"2024-01-15T00:00:00.000000Z\",\n      \"original_quantity\": 100,\n      \"unit_cost\": 12.5\n    },\n    {\n      \"id\": 502,\n      \"fifo_layer_date\": \"2024-02-01T00:00:00.000000Z\",\n      \"original_quantity\": 50,\n      \"unit_cost\": 13\n    }\n  ],\n  \"per_page\": 20,\n  \"total\": 2,\n  \"last_page\": 1\n}"}]},{"name":"Get Layer Consumptions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{id}/layers/{id2}/consumptions?page=1&per_page=20&layer_type=regular","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":id","layers",":id2","consumptions"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"20","description":"Items per page (default: 20)","disabled":true},{"key":"layer_type","value":"regular","description":"Layer type filter (default: regular)","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]},"description":"Returns paginated consumption records for a specific FIFO layer. Shows how the layer's quantity was consumed over time.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{id}/layers/{id2}/consumptions?page=1&per_page=20&layer_type=regular","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":id","layers",":id2","consumptions"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"20","description":"Items per page (default: 20)","disabled":true},{"key":"layer_type","value":"regular","description":"Layer type filter (default: regular)","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 1001,\n      \"quantity_consumed\": 10,\n      \"unit_cost\": 12.5,\n      \"consumed_at\": \"2024-01-20T14:30:00.000000Z\",\n      \"link_type\": \"App\\\\Models\\\\SalesOrder\",\n      \"link_id\": 301\n    }\n  ],\n  \"per_page\": 20,\n  \"total\": 1,\n  \"last_page\": 1\n}"}]},{"name":"Get Consumption Timeline","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{id}/consumption-timeline?page=1&per_page=20","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":id","consumption-timeline"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"20","description":"Items per page (default: 20)","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns paginated consumption timeline for a specific product — all historical consumption events in chronological order.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{id}/consumption-timeline?page=1&per_page=20","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":id","consumption-timeline"],"query":[{"key":"page","value":"1","description":"Page number (default: 1)","disabled":true},{"key":"per_page","value":"20","description":"Items per page (default: 20)","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"consumed_at\": \"2024-01-20T14:30:00.000000Z\",\n      \"quantity\": 10,\n      \"unit_cost\": 12.5,\n      \"total_cost\": 125\n    }\n  ],\n  \"per_page\": 20,\n  \"total\": 1,\n  \"last_page\": 1\n}"}]},{"name":"Recalculate SKU COGS","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{id}/recalculate","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":id","recalculate"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Queues a COGS recalculation for all FIFO layers of a specific product as a background job. The request returns immediately with a `tracked_job_log_id` you can use to track progress; the updated costs land once the job completes. Returns a message only (no `tracked_job_log_id`) when the product has no FIFO layers to recalculate.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cogs/sku-health/{id}/recalculate","host":["{{baseUrl}}"],"path":["api","cogs","sku-health",":id","recalculate"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 4821\n  },\n  \"message\": \"COGS recalculation started. Use the returned tracked_job_log_id to track progress.\"\n}"}]},{"name":"Export SKU COGS Health","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs/sku-health/export","host":["{{baseUrl}}"],"path":["api","cogs","sku-health","export"]},"description":"Export SKU COGS health metrics as spreadsheet-ready JSON rows. Unlike file-download exports, this endpoint returns the rows in the JSON response body (keyed by column heading) so the caller can save them as CSV/XLSX. Returns up to 10,000 products.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach row reports the cost-of-goods-sold consistency for one product: initial COGS, current unit cost, and the min / max / median / average COGS across its active cost layers, plus the variation percentage between layers. `Valuation Method` is WAC when the product carries Amazon FBA stock (valued on a weighted-average ledger), otherwise FIFO. Monetary values are formatted strings with thousands separators.\n\nFilters (all optional):\n- `search` — matches product SKU or product name (partial match).\n- `variation_min` / `variation_max` — only products whose COGS variation percentage is within the given bounds.\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs/sku-health/export","host":["{{baseUrl}}"],"path":["api","cogs","sku-health","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"data\": [\n      {\n        \"SKU\": \"WIDGET-001\",\n        \"Product Name\": \"Blue Widget\",\n        \"Valuation Method\": \"FIFO\",\n        \"Init COGS\": \"10.00\",\n        \"Unit Cost\": \"12.50\",\n        \"Variation %\": \"18.25\",\n        \"Low COGS\": \"10.00\",\n        \"High COGS\": \"14.75\",\n        \"Median COGS\": \"12.20\",\n        \"Avg COGS\": \"12.35\",\n        \"Total Qty\": 240,\n        \"Total Value\": \"2,964.00\",\n        \"Active Layers\": 3\n      },\n      {\n        \"SKU\": \"GADGET-002\",\n        \"Product Name\": \"Red Gadget\",\n        \"Valuation Method\": \"WAC\",\n        \"Init COGS\": \"22.00\",\n        \"Unit Cost\": \"21.40\",\n        \"Variation %\": \"4.10\",\n        \"Low COGS\": \"21.00\",\n        \"High COGS\": \"22.60\",\n        \"Median COGS\": \"21.40\",\n        \"Avg COGS\": \"21.55\",\n        \"Total Qty\": 88,\n        \"Total Value\": \"1,896.40\",\n        \"Active Layers\": 2\n      }\n    ]\n  },\n  \"status\": \"success\"\n}"}]},{"name":"List Inventory Revaluations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation","host":["{{baseUrl}}"],"path":["api","cogs-revaluation"]},"description":"List the inventory revaluation ledger (paginated). Every FIFO-layer cost change writes one dated row here.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Authentication:** Requires Bearer token (PAT).\n\n**Methods:** GET (query params) or POST (same params in the JSON body — use POST when a large `filter_groups` tree exceeds URL limits).\n\n**Search** — `filter[search]` spans: ID (exact), FIFO Layer ID (exact), product SKU, product name, warehouse name, and reason notes.\n\n**Flat filters** — every column below also supports operator-suffixed variants (e.g. `filter[new_unit_cost.greater_than]`, `filter[effective_at.between]`, `filter[reason.is_one_of]`):\n- Text (`reason`, `cogs_source`, `reason_notes`, `sku`, `product_name`): contains, does_not_contain, is, is_not, is_one_of, starts_with, does_not_start_with, ends_with, does_not_end_with, is_empty, is_not_empty\n- Numeric (`id`, `fifo_layer_id`, `old_unit_cost`, `new_unit_cost`, `units_on_hand_at_time`, `product_id`, `warehouse_id`, `warehouse`): is, is_not, is_one_of, greater_than, less_than, greater_than_or_equal, less_than_or_equal, between, is_empty, is_not_empty\n- Datetime (`effective_at`, `posted_at`, `created_at`): is, is_not, before, after, on_or_before, on_or_after, between, today, yesterday, past_week, past_month, past_year, days_ago, past_days, is_empty, is_not_empty (timezone-aware)\n- Legacy: `filter[effective_from]` / `filter[effective_to]` (date bounds on effective_at, kept for backward compatibility)\n\n**Grouped filters** — `filter_groups` accepts a base64-encoded JSON tree with AND/OR conjunctions and nested groups (the Advanced Filters modal format):\n```json\n{\n  \"conjunction\": \"and\",\n  \"children\": [\n    {\"type\": \"condition\", \"condition\": {\"column\": \"reason\", \"operator\": \"is\", \"value\": \"landed_cost_added\"}},\n    {\"type\": \"group\", \"group\": {\"conjunction\": \"or\", \"children\": [...]}}\n  ]\n}\n```\nVia POST, `filter_groups` may be sent as a plain (non-encoded) JSON object in the body. Unregistered columns return 400.\n\n**Reason values:** landed_cost_added, vendor_invoice_correction, market_value_adjustment, system_error, other.\n\n**Allowed sorts:** id, effective_at, posted_at, new_unit_cost, old_unit_cost, reason, cogs_source, created_at, product (by product SKU), warehouse (by warehouse name). Prefix with `-` for DESC. Default: `-effective_at`.\n\n**Pagination:** `page` + `per_page` (default 10).\n\n**Cross-links:** `detail_route` opens the revaluation itself; `fifo_layer_reference` / `fifo_layer_route` identify the revalued FIFO layer (its own id — not the document it came from); `origin` is the document that created that layer (purchase order, inbound receipt, assembly, stock take …), carrying `link_type` + `link_id` for the tooltip. `effective_at` is the value date (drives the valuation report); `posted_at` is the booking date.\n\n**Accounting** — each row carries a `ledger` block: `entry_count` / `has_entries` (journal entries this revaluation posted — one per accounting month the true-up is recognised in), `sync_status` (the aggregated accounting-provider sync state across those entries, worst-first; null keys when there are no entries), and `entries[]` with each entry's reference, totals, batch and its own sync status. Revaluation entries sync as a monthly batch, so a member entry's sync state is its batch's state. Filter on presence with `filter[has_ledger_entries]=true|false`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs-revaluation","host":["{{baseUrl}}"],"path":["api","cogs-revaluation"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 9001,\n      \"fifo_layer_id\": 501,\n      \"reason\": \"landed_cost_added\",\n      \"reason_label\": \"Landed Cost Added\",\n      \"reason_notes\": \"Freight + duties allocated from customs invoice CI-4471\",\n      \"cogs_source\": \"manual\",\n      \"cost_version_before\": 2,\n      \"cost_version_after\": 3,\n      \"old_unit_cost\": 10,\n      \"new_unit_cost\": 12.33,\n      \"unit_cost_delta\": 2.33,\n      \"old_total_cost\": 1000,\n      \"new_total_cost\": 1233,\n      \"total_cost_delta\": 233,\n      \"units_on_hand_at_time\": 80,\n      \"units_consumed_at_time\": 20,\n      \"effective_at\": \"2026-05-01T00:00:00.000000Z\",\n      \"posted_at\": \"2026-05-01T00:00:00.000000Z\",\n      \"created_at\": \"2026-05-01T14:22:10.000000Z\",\n      \"detail_route\": \"/inventory/inventory-revaluations/9001\",\n      \"user\": {\n        \"id\": 7,\n        \"name\": \"Jane Operator\"\n      },\n      \"fifo_layer\": {\n        \"id\": 501,\n        \"fifo_layer_date\": \"2026-03-15T00:00:00.000000Z\",\n        \"original_quantity\": 100,\n        \"available_quantity\": 80,\n        \"avg_cost\": 12.33,\n        \"link_type\": \"App\\\\Models\\\\InboundShipmentReceiptLine\",\n        \"link_id\": 3120,\n        \"link_reference\": \"PO-10044\"\n      },\n      \"product\": {\n        \"id\": 101,\n        \"sku\": \"WIDGET-RED-L\",\n        \"name\": \"Red Widget Large\"\n      },\n      \"warehouse\": {\n        \"id\": 3,\n        \"name\": \"Main Warehouse\"\n      },\n      \"fifo_layer_reference\": \"Layer #501\",\n      \"fifo_layer_route\": \"/inventory/fifo-layers/501\",\n      \"origin\": {\n        \"link_type\": \"App\\\\Models\\\\InboundShipmentReceiptLine\",\n        \"link_id\": 3120,\n        \"name\": \"inbound_shipment_receipt_line\",\n        \"parent_link_id\": 10044,\n        \"detail_link_id\": 720,\n        \"reference\": \"PO-10044\"\n      },\n      \"ledger\": {\n        \"entry_count\": 1,\n        \"has_entries\": true,\n        \"status\": {\n          \"key\": \"posted\",\n          \"label\": \"1 Entry\",\n          \"color\": \"primary\"\n        },\n        \"sync_status\": {\n          \"key\": \"synced\",\n          \"label\": \"Synced\",\n          \"color\": \"success\"\n        },\n        \"entries\": [\n          {\n            \"id\": 88412,\n            \"type\": \"inventory_revaluation\",\n            \"type_label\": \"Inventory Revaluation\",\n            \"status\": \"posted\",\n            \"reference\": \"INV-REVAL-9001-2026-05\",\n            \"document_number\": \"JE-88412\",\n            \"effective_at\": \"2026-05-31T23:59:59+00:00\",\n            \"posted_at\": \"2026-06-01T02:10:04+00:00\",\n            \"total\": 233,\n            \"route\": \"/ledger/entries/88412\",\n            \"batch_id\": 640,\n            \"batch_route\": \"/ledger/batches/640\",\n            \"display_status\": {\n              \"key\": \"synced\",\n              \"label\": \"Synced\",\n              \"color\": \"success\"\n            }\n          }\n        ]\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 5,\n  \"per_page\": 10,\n  \"total\": 47,\n  \"from\": 1,\n  \"to\": 10,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/cogs-revaluation?page=1\",\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/cogs-revaluation?page=5\",\n  \"next_page_url\": \"{{protocol}}{{domain}}/api/cogs-revaluation?page=2\",\n  \"prev_page_url\": null,\n  \"path\": \"{{protocol}}{{domain}}/api/cogs-revaluation\"\n}"}]},{"name":"Search COGS Revaluations","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation","host":["{{baseUrl}}"],"path":["api","cogs-revaluation"]},"description":"Returns the same paginated inventory revaluation ledger as the GET version of this endpoint, but accepts filters as a JSON request body instead of the query string. Use this POST variant when the filter set is too large or too complex to fit in a URL (for example, deeply nested AND/OR filter groups or long lists of values).\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nProvide advanced filters under `filter_groups`, a tree of conditions combined with a `conjunction` of `and` or `or`. Each condition names a `column`, an `operator`, and a `value`.\n\nText columns (`reason`, `cogs_source`, `reason_notes`, `sku`, `product_name`) accept: is, is_not, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, is_one_of, is_not_one_of.\nNumeric and date columns (`id`, `fifo_layer_id`, `old_unit_cost`, `new_unit_cost`, `units_on_hand_at_time`, `product_id`, `warehouse_id`, `effective_at`, `posted_at`, `created_at`) accept: is, is_not, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between, is_empty, is_not_empty.\n\nSortable columns (via `sort`, prefix with `-` for descending): id, effective_at, posted_at, new_unit_cost, old_unit_cost, reason, cogs_source, created_at, product, warehouse. Results default to `-effective_at`. The legacy `effective_from` / `effective_to` date-range filters remain supported.","body":{"mode":"raw","raw":"{\n  \"per_page\": 25,\n  \"page\": 1,\n  \"sort\": \"-effective_at\",\n  \"filter_groups\": {\n    \"conjunction\": \"and\",\n    \"children\": [\n      {\n        \"type\": \"condition\",\n        \"condition\": {\n          \"column\": \"reason\",\n          \"operator\": \"is\",\n          \"value\": \"recalculation\"\n        }\n      },\n      {\n        \"type\": \"group\",\n        \"group\": {\n          \"conjunction\": \"or\",\n          \"children\": [\n            {\n              \"type\": \"condition\",\n              \"condition\": {\n                \"column\": \"new_unit_cost\",\n                \"operator\": \"greater_than\",\n                \"value\": 10\n              }\n            },\n            {\n              \"type\": \"condition\",\n              \"condition\": {\n                \"column\": \"sku\",\n                \"operator\": \"contains\",\n                \"value\": \"WIDGET\"\n              }\n            }\n          ]\n        }\n      }\n    ]\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cogs-revaluation","host":["{{baseUrl}}"],"path":["api","cogs-revaluation"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 9001,\n      \"fifo_layer_id\": 501,\n      \"reason\": \"landed_cost_added\",\n      \"reason_label\": \"Landed Cost Added\",\n      \"reason_notes\": \"Freight + duties allocated from customs invoice CI-4471\",\n      \"cogs_source\": \"manual\",\n      \"cost_version_before\": 2,\n      \"cost_version_after\": 3,\n      \"old_unit_cost\": 10,\n      \"new_unit_cost\": 12.33,\n      \"unit_cost_delta\": 2.33,\n      \"old_total_cost\": 1000,\n      \"new_total_cost\": 1233,\n      \"total_cost_delta\": 233,\n      \"units_on_hand_at_time\": 80,\n      \"units_consumed_at_time\": 20,\n      \"effective_at\": \"2026-05-01T00:00:00.000000Z\",\n      \"posted_at\": \"2026-05-01T00:00:00.000000Z\",\n      \"created_at\": \"2026-05-01T14:22:10.000000Z\",\n      \"detail_route\": \"/inventory/inventory-revaluations/9001\",\n      \"user\": {\n        \"id\": 7,\n        \"name\": \"Jane Operator\"\n      },\n      \"fifo_layer\": {\n        \"id\": 501,\n        \"fifo_layer_date\": \"2026-03-15T00:00:00.000000Z\",\n        \"original_quantity\": 100,\n        \"available_quantity\": 80,\n        \"avg_cost\": 12.33,\n        \"link_type\": \"App\\\\Models\\\\InboundShipmentReceiptLine\",\n        \"link_id\": 3120,\n        \"link_reference\": \"PO-10044\"\n      },\n      \"product\": {\n        \"id\": 101,\n        \"sku\": \"WIDGET-RED-L\",\n        \"name\": \"Red Widget Large\"\n      },\n      \"warehouse\": {\n        \"id\": 3,\n        \"name\": \"Main Warehouse\"\n      },\n      \"fifo_layer_reference\": \"Layer #501\",\n      \"fifo_layer_route\": \"/inventory/fifo-layers/501\",\n      \"origin\": {\n        \"link_type\": \"App\\\\Models\\\\InboundShipmentReceiptLine\",\n        \"link_id\": 3120,\n        \"name\": \"inbound_shipment_receipt_line\",\n        \"parent_link_id\": 10044,\n        \"detail_link_id\": 720,\n        \"reference\": \"PO-10044\"\n      },\n      \"ledger\": {\n        \"entry_count\": 1,\n        \"has_entries\": true,\n        \"status\": {\n          \"key\": \"posted\",\n          \"label\": \"1 Entry\",\n          \"color\": \"primary\"\n        },\n        \"sync_status\": {\n          \"key\": \"synced\",\n          \"label\": \"Synced\",\n          \"color\": \"success\"\n        },\n        \"entries\": [\n          {\n            \"id\": 88412,\n            \"type\": \"inventory_revaluation\",\n            \"type_label\": \"Inventory Revaluation\",\n            \"status\": \"posted\",\n            \"reference\": \"INV-REVAL-9001-2026-05\",\n            \"document_number\": \"JE-88412\",\n            \"effective_at\": \"2026-05-31T23:59:59+00:00\",\n            \"posted_at\": \"2026-06-01T02:10:04+00:00\",\n            \"total\": 233,\n            \"route\": \"/ledger/entries/88412\",\n            \"batch_id\": 640,\n            \"batch_route\": \"/ledger/batches/640\",\n            \"display_status\": {\n              \"key\": \"synced\",\n              \"label\": \"Synced\",\n              \"color\": \"success\"\n            }\n          }\n        ]\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"per_page\": 25,\n  \"from\": 1,\n  \"to\": 1,\n  \"last_page\": 6,\n  \"total\": 138,\n  \"next_page_url\": \"https://api.example.com/api/cogs-revaluation?page=2\",\n  \"prev_page_url\": null\n}"}]},{"name":"List Inventory Revaluation Activity","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation/{revaluation}/activity-log","host":["{{baseUrl}}"],"path":["api","cogs-revaluation",":revaluation","activity-log"],"variable":[{"key":"revaluation","value":"","description":"The revaluation ID."}]},"description":"Paginated audit trail for one inventory revaluation — what changed, when, and who changed it. Newest first.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Authentication:** Requires Bearer token (PAT).\n\n**Path parameter:** `{revaluation}` — the revaluation ID (numeric).\n\n**Pagination:** `page` + `per_page` (default 15).\n\nA revaluation is an immutable ledger row, so in practice this returns its creation entry plus any later corrections. `properties` carries the before/after values."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs-revaluation/{revaluation}/activity-log","host":["{{baseUrl}}"],"path":["api","cogs-revaluation",":revaluation","activity-log"],"variable":[{"key":"revaluation","value":"","description":"The revaluation ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 445102,\n      \"description\": \"created\",\n      \"event\": \"created\",\n      \"subject_type\": \"InventoryRevaluation\",\n      \"subject_id\": 9001,\n      \"properties\": {\n        \"attributes\": {\n          \"fifo_layer_id\": 501,\n          \"old_unit_cost\": 10,\n          \"new_unit_cost\": 12.33,\n          \"reason\": \"landed_cost_added\"\n        },\n        \"metadata\": []\n      },\n      \"causer_name\": \"Jane Operator\",\n      \"created_at\": \"2026-05-01T14:22:10.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/cogs-revaluation/9001/activity-log?page=1\",\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/cogs-revaluation/9001/activity-log?page=1\",\n  \"next_page_url\": null,\n  \"prev_page_url\": null,\n  \"path\": \"{{protocol}}{{domain}}/api/cogs-revaluation/9001/activity-log\"\n}"}]},{"name":"Get Inventory Revaluation","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation/{revaluation}","host":["{{baseUrl}}"],"path":["api","cogs-revaluation",":revaluation"],"variable":[{"key":"revaluation","value":"","description":"The revaluation ID."}]},"description":"Get one inventory revaluation with its full context: the cost movement, the revalued FIFO layer, the document that layer came from, the product and warehouse, and the accounting ledger entries the revaluation posted.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Authentication:** Requires Bearer token (PAT).\n\n**Path parameter:** `{revaluation}` — the revaluation ID (numeric).\n\n**Layer vs source:** `fifo_layer_reference` / `fifo_layer_route` identify the FIFO layer itself; `origin` is the document that created it (purchase order, inbound receipt, assembly, stock take …).\n\n**Accounting:** `ledger.entries[]` lists every live journal entry this revaluation posted — one per accounting month it is recognised in — each with its own status, totals, monthly batch and provider-sync state. `ledger.sync_status` aggregates those worst-first. An empty list means the revaluation moved no accounting balance.\n\nReturns 404 when no revaluation has that ID."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs-revaluation/{revaluation}","host":["{{baseUrl}}"],"path":["api","cogs-revaluation",":revaluation"],"variable":[{"key":"revaluation","value":"","description":"The revaluation ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 9001,\n    \"fifo_layer_id\": 501,\n    \"reason\": \"landed_cost_added\",\n    \"reason_label\": \"Landed Cost Added\",\n    \"reason_notes\": \"Freight + duties allocated from customs invoice CI-4471\",\n    \"cogs_source\": \"manual\",\n    \"cost_version_before\": 2,\n    \"cost_version_after\": 3,\n    \"old_unit_cost\": 10,\n    \"new_unit_cost\": 12.33,\n    \"unit_cost_delta\": 2.33,\n    \"old_total_cost\": 1000,\n    \"new_total_cost\": 1233,\n    \"total_cost_delta\": 233,\n    \"units_on_hand_at_time\": 80,\n    \"units_consumed_at_time\": 20,\n    \"effective_at\": \"2026-05-01T00:00:00.000000Z\",\n    \"posted_at\": \"2026-05-01T00:00:00.000000Z\",\n    \"created_at\": \"2026-05-01T14:22:10.000000Z\",\n    \"detail_route\": \"/inventory/inventory-revaluations/9001\",\n    \"user\": {\n      \"id\": 7,\n      \"name\": \"Jane Operator\"\n    },\n    \"fifo_layer\": {\n      \"id\": 501,\n      \"fifo_layer_date\": \"2026-03-15T00:00:00.000000Z\",\n      \"original_quantity\": 100,\n      \"available_quantity\": 80,\n      \"avg_cost\": 12.33,\n      \"link_type\": \"App\\\\Models\\\\InboundShipmentReceiptLine\",\n      \"link_id\": 3120,\n      \"link_reference\": \"PO-10044\"\n    },\n    \"product\": {\n      \"id\": 101,\n      \"sku\": \"WIDGET-RED-L\",\n      \"name\": \"Red Widget Large\"\n    },\n    \"warehouse\": {\n      \"id\": 3,\n      \"name\": \"Main Warehouse\"\n    },\n    \"fifo_layer_reference\": \"Layer #501\",\n    \"fifo_layer_route\": \"/inventory/fifo-layers/501\",\n    \"origin\": {\n      \"link_type\": \"App\\\\Models\\\\InboundShipmentReceiptLine\",\n      \"link_id\": 3120,\n      \"name\": \"inbound_shipment_receipt_line\",\n      \"parent_link_id\": 10044,\n      \"detail_link_id\": 720,\n      \"reference\": \"PO-10044\"\n    },\n    \"ledger\": {\n      \"entry_count\": 1,\n      \"has_entries\": true,\n      \"status\": {\n        \"key\": \"posted\",\n        \"label\": \"1 Entry\",\n        \"color\": \"primary\"\n      },\n      \"sync_status\": {\n        \"key\": \"synced\",\n        \"label\": \"Synced\",\n        \"color\": \"success\"\n      },\n      \"entries\": [\n        {\n          \"id\": 88412,\n          \"type\": \"inventory_revaluation\",\n          \"type_label\": \"Inventory Revaluation\",\n          \"status\": \"posted\",\n          \"reference\": \"INV-REVAL-9001-2026-05\",\n          \"document_number\": \"JE-88412\",\n          \"effective_at\": \"2026-05-31T23:59:59+00:00\",\n          \"posted_at\": \"2026-06-01T02:10:04+00:00\",\n          \"total\": 233,\n          \"route\": \"/ledger/entries/88412\",\n          \"batch_id\": 640,\n          \"batch_route\": \"/ledger/batches/640\",\n          \"display_status\": {\n            \"key\": \"synced\",\n            \"label\": \"Synced\",\n            \"color\": \"success\"\n          }\n        }\n      ]\n    }\n  },\n  \"message\": null,\n  \"errors\": []\n}"}]},{"name":"Get Variation by Product Report","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation/variation-by-product?variation_threshold=5","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","variation-by-product"],"query":[{"key":"variation_threshold","value":"5","description":"Minimum variation % to include a product (numeric, min 5, default 5)","disabled":true}]},"description":"Returns a report of products whose COGS varies significantly across FIFO layers. Products with cost variation above the threshold are flagged for revaluation review.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nQuery params:\n- variation_threshold (optional): Minimum variation % to include, numeric, min 5, default 5"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs-revaluation/variation-by-product?variation_threshold=5","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","variation-by-product"],"query":[{"key":"variation_threshold","value":"5","description":"Minimum variation % to include a product (numeric, min 5, default 5)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": [\n    {\n      \"product_id\": 42,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Blue Widget\",\n      \"avg_cost\": 12.5,\n      \"min_cost\": 8,\n      \"max_cost\": 18.75,\n      \"variation_percentage\": 85,\n      \"layer_count\": 5\n    },\n    {\n      \"product_id\": 78,\n      \"sku\": \"GADGET-XL\",\n      \"name\": \"XL Gadget\",\n      \"avg_cost\": 45,\n      \"min_cost\": 38,\n      \"max_cost\": 55,\n      \"variation_percentage\": 36.8,\n      \"layer_count\": 3\n    }\n  ]\n}"}]},{"name":"Get Product COGS Report","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation/product-report/{id}?outlier_threshold=5","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","product-report",":id"],"query":[{"key":"outlier_threshold","value":"5","description":"Outlier detection threshold % (numeric, min 5, max 100, default 5)","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns a detailed COGS report for a specific product, including all FIFO layers and their costs.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPath params:\n- product: Product ID\n\nQuery params:\n- outlier_threshold (optional): Percentage threshold for detecting cost outliers (5-100, default 5)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs-revaluation/product-report/{id}?outlier_threshold=5","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","product-report",":id"],"query":[{"key":"outlier_threshold","value":"5","description":"Outlier detection threshold % (numeric, min 5, max 100, default 5)","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": {\n    \"product_id\": 42,\n    \"sku\": \"WIDGET-001\",\n    \"name\": \"Blue Widget\",\n    \"avg_cost\": 12.5,\n    \"outlier_threshold\": 5,\n    \"fifo_layers\": [\n      {\n        \"id\": 101,\n        \"fifo_layer_date\": \"2024-01-15T00:00:00.000000Z\",\n        \"unit_cost\": 8,\n        \"original_quantity\": 500,\n        \"remaining_quantity\": 200,\n        \"warehouse_name\": \"Main Warehouse\",\n        \"is_outlier\": true\n      }\n    ]\n  }\n}"}]},{"name":"Get FIFO Layer COGS Report","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation/fifo-report/{id}","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","fifo-report",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns a detailed COGS report for a specific FIFO layer including cost history and revaluation events.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPath params:\n- fifoLayer: FIFO Layer ID"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs-revaluation/fifo-report/{id}","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","fifo-report",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": {\n    \"fifo_layer_id\": 101,\n    \"product_id\": 42,\n    \"sku\": \"WIDGET-001\",\n    \"unit_cost\": 12.5,\n    \"original_quantity\": 500,\n    \"remaining_quantity\": 200,\n    \"warehouse_name\": \"Main Warehouse\",\n    \"fifo_layer_date\": \"2024-01-15T00:00:00.000000Z\",\n    \"revaluations\": [\n      {\n        \"id\": 55,\n        \"old_unit_cost\": 8,\n        \"new_unit_cost\": 12.5,\n        \"reason\": \"purchase_cost_update\",\n        \"posted_at\": \"2024-03-01T09:00:00.000000Z\"\n      }\n    ]\n  }\n}"}]},{"name":"Get Bulk FIFO Layer COGS Report","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation/bulk-fifo-report?ids[]=101","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","bulk-fifo-report"],"query":[{"key":"ids[]","value":"101","description":"FIFO Layer IDs (required array, min 1)","disabled":true}]},"description":"Returns COGS reports for multiple FIFO layers in a single request.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nQuery params:\n- ids (required): Array of FIFO Layer IDs"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs-revaluation/bulk-fifo-report?ids[]=101","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","bulk-fifo-report"],"query":[{"key":"ids[]","value":"101","description":"FIFO Layer IDs (required array, min 1)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": [\n    {\n      \"fifo_layer_id\": 101,\n      \"unit_cost\": 12.5,\n      \"product_sku\": \"WIDGET-001\",\n      \"original_quantity\": 500\n    },\n    {\n      \"fifo_layer_id\": 102,\n      \"unit_cost\": 9.75,\n      \"product_sku\": \"GADGET-XL\",\n      \"original_quantity\": 250\n    }\n  ]\n}"}]},{"name":"Revalue FIFO Layer","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation/{id}/revalue","host":["{{baseUrl}}"],"path":["api","cogs-revaluation",":id","revalue"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Revalues a single FIFO layer to a new unit cost, creating an InventoryRevaluation record.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nFields (CreateInventoryRevaluationData):\n- cogs_source (required, CogsSourceEnum): the source for the new cost (e.g., purchase_order)\n- new_unit_cost (required, float): the new per-unit cost\n- reason (optional, InventoryRevaluationReasonEnum): reason for revaluation\n- reason_notes (optional, string): free-text notes\n- effective_at (optional, date-only YYYY-MM-DD or ISO 8601 datetime): date the cost change takes effect; drives the point-in-time valuation overlay and the GL revaluation period. Defaults to now(). If it falls inside a locked accounting period it is moved forward to the first open day.\n- posted_at (optional, date-only YYYY-MM-DD or ISO 8601 datetime): GL posting timestamp. Defaults to now().\n- old_unit_cost (optional, float): explicit prior per-unit cost; defaults to the layer's current average cost.\n- old_total_cost (optional, float): explicit prior total cost for the layer.\n- new_total_cost (optional, float): explicit new total cost for the layer.","body":{"mode":"raw","raw":"{\n  \"cogs_source\": \"purchase_order\",\n  \"new_unit_cost\": 12.33,\n  \"reason\": \"manual_adjustment\",\n  \"reason_notes\": \"Correcting cost based on updated supplier invoice\",\n  \"effective_at\": \"2026-04-01\",\n  \"posted_at\": \"2024-06-01T00:00:00Z\",\n  \"old_unit_cost\": 10,\n  \"old_total_cost\": 1000,\n  \"new_total_cost\": 1233\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cogs-revaluation/{id}/revalue","host":["{{baseUrl}}"],"path":["api","cogs-revaluation",":id","revalue"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"success\"\n}"}]},{"name":"Bulk Revalue FIFO Layers","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation/bulk-revalue","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","bulk-revalue"]},"description":"Revalues multiple FIFO layers in a single request.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody fields:\n- layerDataItems (required): Array of layer revaluation items\n  - layerDataItems.*.fifo_layer_id: FIFO Layer ID\n  - layerDataItems.*.cogs_source: Source of the cost value\n  - layerDataItems.*.new_unit_cost: New unit cost as float\n  - layerDataItems.*.effective_at (optional, date-only YYYY-MM-DD or ISO 8601 datetime): Date the cost change takes effect (per item); defaults to now(), moved to the first open day if the period is locked\n  - layerDataItems.*.posted_at (optional): GL posting timestamp (per item); defaults to now()\n  - layerDataItems.*.old_unit_cost (optional): Explicit prior per-unit cost (per item)\n  - layerDataItems.*.old_total_cost (optional): Explicit prior total cost (per item)\n  - layerDataItems.*.new_total_cost (optional): Explicit new total cost (per item)\n- reason (required): Reason enum value applied to all items\n- reason_notes (optional): Free-text explanation","body":{"mode":"raw","raw":"{\n  \"layerDataItems\": [\n    {\n      \"fifo_layer_id\": 101,\n      \"cogs_source\": \"purchase_order\",\n      \"new_unit_cost\": 14.99,\n      \"effective_at\": \"2026-04-01\",\n      \"old_unit_cost\": 12\n    },\n    {\n      \"fifo_layer_id\": 102,\n      \"cogs_source\": \"manual\",\n      \"new_unit_cost\": 22.5\n    }\n  ],\n  \"reason\": \"purchase_cost_update\",\n  \"reason_notes\": \"Quarterly cost reconciliation\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cogs-revaluation/bulk-revalue","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","bulk-revalue"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\"\n}"}]},{"name":"Rebuild COGS for Product","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation/rebuild-cogs/{id}","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","rebuild-cogs",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Rebuilds COGS (recalculates FIFO layer costs from source documents) for a single product. This is a synchronous operation.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPath params:\n- product: Product ID"},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cogs-revaluation/rebuild-cogs/{id}","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","rebuild-cogs",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"COGS rebuilt successfully\"\n}"}]},{"name":"Rebuild COGS for Multiple Products","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation/rebuild-cogs-bulk","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","rebuild-cogs-bulk"]},"description":"Rebuilds COGS for multiple specific products synchronously. Returns per-product success/failure summary.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody fields:\n- product_ids (required): Array of product IDs (min 1, must exist in products table)","body":{"mode":"raw","raw":"{\n  \"product_ids\": [\n    42,\n    78,\n    99\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cogs-revaluation/rebuild-cogs-bulk","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","rebuild-cogs-bulk"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Successfully rebuilt COGS for 3 products\",\n  \"data\": {\n    \"product_count\": 3,\n    \"failed_count\": 0,\n    \"errors\": []\n  }\n}"}]},{"name":"Start Tracked COGS Rebuild","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation/rebuild-cogs-tracked","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","rebuild-cogs-tracked"]},"description":"Starts a background tracked COGS rebuild job for all products (or a filtered subset). Returns a tracked_job_id to poll for progress.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nBody fields:\n- last_rebuilt_before (optional): Only rebuild products rebuilt before this date\n- product_ids (optional): Array of specific product IDs to rebuild. If omitted, rebuilds all products","body":{"mode":"raw","raw":"{\n  \"last_rebuilt_before\": \"2024-01-01\",\n  \"product_ids\": [\n    42,\n    78\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cogs-revaluation/rebuild-cogs-tracked","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","rebuild-cogs-tracked"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Tracked COGS rebuild started successfully\",\n  \"data\": {\n    \"tracked_job_id\": 1234\n  }\n}"}]},{"name":"Get Tracked Job Status","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation/tracked-job/{id}","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","tracked-job",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Polls the status and progress of a tracked COGS rebuild job.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPath params:\n- trackedJobId: Tracked job log ID from startTrackedRebuild response"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs-revaluation/tracked-job/{id}","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","tracked-job",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": {\n    \"id\": 1234,\n    \"status\": \"processing\",\n    \"progress_percentage\": 65.5,\n    \"total_items\": 200,\n    \"processed_items\": 131,\n    \"messages\": null,\n    \"results\": {\n      \"batch_ids\": [\n        \"abc-123\",\n        \"def-456\"\n      ]\n    },\n    \"batch_progress\": {\n      \"total_jobs\": 20,\n      \"pending_jobs\": 7,\n      \"processed_jobs\": 13,\n      \"failed_jobs\": 0,\n      \"finished\": false,\n      \"cancelled\": false,\n      \"progress_percentage\": 65,\n      \"chunks_count\": 2\n    },\n    \"created_at\": \"2024-06-15T10:00:00.000000Z\",\n    \"attempted_at\": \"2024-06-15T10:00:05.000000Z\",\n    \"completed_at\": null\n  }\n}"}]},{"name":"Get Unified Tracking Data","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation/unified-tracking/{id}?since=2024-06-15T10:00:00Z&limit=50","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","unified-tracking",":id"],"query":[{"key":"since","value":"2024-06-15T10:00:00Z","description":"Show recently rebuilt products since this datetime (default: 1 hour ago)","disabled":true},{"key":"limit","value":"50","description":"Max recently rebuilt products to return (integer 1-1000, default 100)","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns combined job status, recently rebuilt products, FIFO layer changes, and failed products in a single call. Replaces polling multiple endpoints.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPath params:\n- trackedJobId: Tracked job log ID\n\nQuery params:\n- since (optional): Show products rebuilt after this datetime (default: 1 hour ago)\n- limit (optional): Max recently rebuilt products to return (1-1000, default 100)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs-revaluation/unified-tracking/{id}?since=2024-06-15T10:00:00Z&limit=50","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","unified-tracking",":id"],"query":[{"key":"since","value":"2024-06-15T10:00:00Z","description":"Show recently rebuilt products since this datetime (default: 1 hour ago)","disabled":true},{"key":"limit","value":"50","description":"Max recently rebuilt products to return (integer 1-1000, default 100)","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": {\n    \"job_status\": {\n      \"id\": 1234,\n      \"status\": \"processing\",\n      \"progress_percentage\": 65.5,\n      \"total_items\": 200,\n      \"processed_items\": 131,\n      \"results\": {\n        \"batch_ids\": [\n          \"abc-123\"\n        ]\n      },\n      \"batch_progress\": {\n        \"total_jobs\": 20,\n        \"processed_jobs\": 13,\n        \"finished\": false\n      },\n      \"created_at\": \"2024-06-15T10:00:00.000000Z\",\n      \"attempted_at\": \"2024-06-15T10:00:05.000000Z\",\n      \"completed_at\": null\n    },\n    \"recently_rebuilt_products\": [\n      {\n        \"id\": 42,\n        \"sku\": \"WIDGET-001\",\n        \"name\": \"Blue Widget\",\n        \"last_rebuilt_cogs_at\": \"2024-06-15T10:05:00.000000Z\"\n      }\n    ],\n    \"fifo_layer_changes\": [\n      {\n        \"fifo_layer_id\": 101,\n        \"product_sku\": \"WIDGET-001\",\n        \"product_name\": \"Blue Widget\",\n        \"warehouse_name\": \"Main Warehouse\",\n        \"before_cost\": 8,\n        \"after_cost\": 12.5,\n        \"cost_difference\": 4.5,\n        \"cost_change_percentage\": 56.25,\n        \"changed_at\": \"2024-06-15T10:05:00.000000Z\",\n        \"reason\": \"rebuild\",\n        \"cogs_source\": \"purchase_order\"\n      }\n    ],\n    \"failed_products\": []\n  }\n}"}]},{"name":"Cancel Tracked Rebuild","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation/cancel-tracked-rebuild/{id}","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","cancel-tracked-rebuild",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Cancels a running tracked COGS rebuild job. Cancels all associated batch jobs and marks the tracked job as cancelled.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPath params:\n- trackedJobId: Tracked job log ID\n\nNote: Returns 400 if the job is already completed, failed, or cancelled."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cogs-revaluation/cancel-tracked-rebuild/{id}","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","cancel-tracked-rebuild",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"message\": \"Job cancelled successfully - 2 batch(es) cancelled\"\n}"}]},{"name":"Get Recently Rebuilt Products","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation/recently-rebuilt-products?since=2024-06-01&limit=100","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","recently-rebuilt-products"],"query":[{"key":"since","value":"2024-06-01","description":"Only return products rebuilt after this datetime (ISO 8601, default: 1 hour ago)","disabled":true},{"key":"limit","value":"100","description":"Max records to return (integer, 1-1000, default 100)","disabled":true}]},"description":"Returns a list of products that have had their COGS rebuilt recently. Used for monitoring rebuild progress.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nQuery params:\n- since (optional): Datetime filter — only return products rebuilt after this time (default: 1 hour ago)\n- limit (optional): Max records to return, integer 1-1000, default 100"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs-revaluation/recently-rebuilt-products?since=2024-06-01&limit=100","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","recently-rebuilt-products"],"query":[{"key":"since","value":"2024-06-01","description":"Only return products rebuilt after this datetime (ISO 8601, default: 1 hour ago)","disabled":true},{"key":"limit","value":"100","description":"Max records to return (integer, 1-1000, default 100)","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"status\": \"success\",\n  \"data\": [\n    {\n      \"id\": 42,\n      \"sku\": \"WIDGET-001\",\n      \"name\": \"Blue Widget\",\n      \"last_rebuilt_cogs_at\": \"2024-06-15T14:30:00.000000Z\"\n    },\n    {\n      \"id\": 78,\n      \"sku\": \"GADGET-XL\",\n      \"name\": \"XL Gadget\",\n      \"last_rebuilt_cogs_at\": \"2024-06-15T14:28:00.000000Z\"\n    }\n  ]\n}"}]},{"name":"Export FIFO Layer Changes CSV","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cogs-revaluation/export-fifo-changes/{id}","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","export-fifo-changes",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Exports FIFO layer cost changes from a tracked rebuild job as a CSV file download.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token.\n\nPath params:\n- trackedJobId: Tracked job log ID\n\nResponse: CSV file download (Content-Type: text/csv)\nColumns: Changed At, Product SKU, Product Name, Warehouse, FIFO Layer ID, FIFO Layer Date, Before Cost, After Cost, Cost Difference, Cost Change %, Original Quantity, Link Type, Revaluation ID, Drawer Reference, Reason, COGS Source"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cogs-revaluation/export-fifo-changes/{id}","host":["{{baseUrl}}"],"path":["api","cogs-revaluation","export-fifo-changes",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"\"Changed At\",\"Product SKU\",\"Product Name\",\"Warehouse\",\"FIFO Layer ID\",\"FIFO Layer Date\",\"Before Cost\",\"After Cost\",\"Cost Difference\",\"Cost Change %\",\"Original Quantity\",\"Link Type\",\"Revaluation ID\",\"Drawer Reference\",\"Reason\",\"COGS Source\"\n\"2024-06-15T10:05:00.000000Z\",\"WIDGET-001\",\"Blue Widget\",\"Main Warehouse\",\"101\",\"2024-01-15T00:00:00.000000Z\",\"8\",\"12.5\",\"4.5\",\"56.25%\",\"500\",\"Purchase Order Receiving\",\"55\",\"PO-2024-0042\",\"rebuild\",\"purchase_order\""}]}]},{"name":"Product Pro Forma","item":[{"name":"Get Pro Forma Context","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/pro-forma/context","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","context"]},"description":"Full Pro Forma Analyzer bootstrap payload.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns one channel tab per connected sales-channel family (Amazon, Shopify, eBay, ...) plus a `custom` tab. Each tab includes:\n- `instances` — sync-active integration instances of that family\n- `listing` (includes `product_listing_id`) — the product's listing prefill (price, is_fba, listing_sku, asin) when product_id is given\n- `fee_defaults` — engine-resolved default fee lines (rate cards, instance settings, listing overrides) with provenance `source`\n- `supports_live_fees` — true for Amazon (use POST /amazon-fees)\n- `fulfillment_options` — `[\"fba\", \"fbm\"]` for Amazon, empty otherwise\n- `history` — 12-month realized actuals on that channel including returns economics (`avg_refund_pct`, `recovery_rate_pct`; both null when no returns); null when no orders\n\nAlso returns the product's per-unit `cogs_breakdown` from open FIFO layers (falls back to a single average-cost line) and its saved `proforma_assumptions`. The `product` object includes `default_price` — the price at the product default pricing tier, used as the selling-price fallback when a channel has no listing price (null when no default-tier price is set).\n\nChannel families with no sync-active instance are excluded unless the product has order history there, in which case the tab is kept with an \"(inactive)\" label.\n\nRequires Bearer token (products scope)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/pro-forma/context","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","context"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"product\": {\n      \"id\": 1,\n      \"sku\": \"GSS342\",\n      \"name\": \"Grow Light Panel GSS342\",\n      \"average_cost\": 70,\n      \"default_price\": 129.99\n    },\n    \"cogs_breakdown\": [\n      {\n        \"cost_category_id\": 1,\n        \"name\": \"Base Product\",\n        \"amount\": 70,\n        \"source\": \"fifo\"\n      },\n      {\n        \"cost_category_id\": 3,\n        \"name\": \"Freight\",\n        \"amount\": 4.25,\n        \"source\": \"fifo\"\n      }\n    ],\n    \"channels\": [\n      {\n        \"integration_type\": \"amazon\",\n        \"label\": \"Amazon\",\n        \"instances\": [\n          {\n            \"integration_instance_id\": 4,\n            \"name\": \"Amazon US\",\n            \"sales_channel_id\": 2\n          }\n        ],\n        \"listing\": {\n          \"product_listing_id\": 42,\n          \"price\": 99.99,\n          \"is_fba\": true,\n          \"listing_sku\": \"GSS342-FBA\",\n          \"asin\": \"B08XYZ1234\"\n        },\n        \"fee_defaults\": [\n          {\n            \"key\": \"referral_fee\",\n            \"label\": \"Referral fee\",\n            \"fee_basis\": \"percent_of_price\",\n            \"percent\": 15,\n            \"fixed_amount\": null,\n            \"source\": \"rate_card\",\n            \"rate_card_version\": \"2026-01\",\n            \"metadata\": null\n          },\n          {\n            \"key\": \"fba_fulfillment\",\n            \"label\": \"FBA fulfillment fee\",\n            \"fee_basis\": \"fixed_per_unit\",\n            \"percent\": null,\n            \"fixed_amount\": 6.92,\n            \"source\": \"rate_card\",\n            \"rate_card_version\": \"2026-01\",\n            \"metadata\": {\n              \"size_tiers\": \"standard\"\n            }\n          }\n        ],\n        \"supports_live_fees\": true,\n        \"fulfillment_options\": [\n          \"fba\",\n          \"fbm\"\n        ],\n        \"history\": {\n          \"orders\": 312,\n          \"units\": 340,\n          \"avg_unit_revenue\": 98.45,\n          \"avg_unit_cogs\": 70,\n          \"avg_unit_shipping\": 1.12,\n          \"avg_unit_fees\": 21.6,\n          \"avg_margin_pct\": 8.42,\n          \"return_rate_pct\": 2.35,\n          \"avg_refund_pct\": 96.5,\n          \"recovery_rate_pct\": 41.2,\n          \"period\": \"12m\"\n        }\n      },\n      {\n        \"integration_type\": \"custom\",\n        \"label\": \"Custom\",\n        \"instances\": [],\n        \"listing\": null,\n        \"fee_defaults\": [],\n        \"supports_live_fees\": false,\n        \"fulfillment_options\": [],\n        \"history\": null\n      }\n    ],\n    \"proforma_assumptions\": {\n      \"shipping_cost\": 4.5,\n      \"landed_cost_percentage\": 6,\n      \"marketplace_cost_percentage\": null\n    }\n  }\n}"}]},{"name":"Get Amazon Fee Estimate (Live)","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/pro-forma/amazon-fees","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","amazon-fees"]},"description":"Live Amazon SP-API Product Fees v0 estimate for a price/fulfillment combination.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody fields:\n- `integration_instance_id` (required, integer) — Amazon integration instance\n- `id_type` (required, string) — `sku` (existing listing, seller SKU) or `asin` (catalog item)\n- `id_value` (required, string, max 255) — the seller SKU or ASIN\n- `price` (required, numeric, min 0.01) — listing price to estimate fees at\n- `is_amazon_fulfilled` (required, boolean) — true = FBA (sends OptionalFulfillmentProgram FBA_CORE), false = FBM\n- `refresh` (optional, boolean) — bypass the 24h cache and force a live SP-API call\n\nResults are cached for 24 hours on the exact parameter tuple; `cached` + `fetched_at` in the response indicate provenance. Amazon SP-API rate limit is 1 rps — keep `refresh` use sparing. The endpoint is throttled to 30 requests/minute (`throttle:30,1,pro-forma-amazon-fees`); exceeding it returns 429 Too Many Requests.\n\nWhen Amazon is unreachable or rejects the request, returns 422 with an `IntegrationApiError` error so the client can fall back to rate-card defaults.\n\nRequires Bearer token (products scope).","body":{"mode":"raw","raw":"{\n  \"integration_instance_id\": 4,\n  \"id_type\": \"sku\",\n  \"id_value\": \"GSS342-FBA\",\n  \"price\": 99.99,\n  \"is_amazon_fulfilled\": true,\n  \"refresh\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/pro-forma/amazon-fees","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","amazon-fees"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"integration_instance_id\": 4,\n    \"id_type\": \"sku\",\n    \"id_value\": \"GSS342-FBA\",\n    \"price\": 99.99,\n    \"is_amazon_fulfilled\": true,\n    \"total_fees_estimate\": 21.92,\n    \"fee_details\": [\n      {\n        \"fee_type\": \"ReferralFee\",\n        \"amount\": 15,\n        \"included\": []\n      },\n      {\n        \"fee_type\": \"FBAFees\",\n        \"amount\": 6.92,\n        \"included\": [\n          {\n            \"fee_type\": \"FBAPerUnitFulfillmentFee\",\n            \"amount\": 6.92,\n            \"included\": []\n          }\n        ]\n      }\n    ],\n    \"fetched_at\": \"2026-06-10T18:24:11.000000Z\",\n    \"cached\": false\n  }\n}"}]},{"name":"Get Channel Sales History (Pro Forma)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/pro-forma/history?product_id=1&sales_channel_ids[]=2","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","history"],"query":[{"key":"product_id","value":"1","description":"Required. Product to aggregate actuals for.","disabled":true},{"key":"sales_channel_ids[]","value":"2","description":"Required (min 1). Sales channel ids to scope to. Repeat for multiple.","disabled":true}]},"description":"Historical realized actuals for one product on a set of sales channels — the \"pro forma vs. actuals\" comparison.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nMetrics (per the period):\n- `orders` — distinct order count (drafts and cancelled excluded)\n- `units` — total units sold\n- `avg_unit_revenue` / `avg_unit_cogs` / `avg_unit_fees` — per-unit averages from sales order line financials (fees = allocated settlement/channel costs)\n- `avg_unit_shipping` — fulfillment costs prorated to the product by quantity share\n- `avg_margin_pct` — profit / revenue * 100\n- `return_rate_pct` — quantity-weighted share of lines with credits or returned COGS\n- `avg_refund_pct` — refund $ as % of gross revenue over returned lines (null when the period has no returns)\n- `recovery_rate_pct` — COGS recovered (restocked) as % of COGS over returned lines (null when the period has no returns)\n\n`data` is null when the product has no qualifying orders in the period.\n\nRequires Bearer token (products scope)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/pro-forma/history?product_id=1&sales_channel_ids[]=2","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","history"],"query":[{"key":"product_id","value":"1","description":"Required. Product to aggregate actuals for.","disabled":true},{"key":"sales_channel_ids[]","value":"2","description":"Required (min 1). Sales channel ids to scope to. Repeat for multiple.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"orders\": 312,\n    \"units\": 340,\n    \"avg_unit_revenue\": 98.45,\n    \"avg_unit_cogs\": 70,\n    \"avg_unit_shipping\": 1.12,\n    \"avg_unit_fees\": 21.6,\n    \"avg_margin_pct\": 8.42,\n    \"return_rate_pct\": 2.35,\n    \"avg_refund_pct\": 96.5,\n    \"recovery_rate_pct\": 41.2,\n    \"period\": \"12m\"\n  }\n}"}]},{"name":"List Active Rate Cards","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/pro-forma/rate-cards","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","rate-cards"]},"description":"Active (latest effective) rate-card rows for every channel type, keyed by channel_type. Default-category rows only — category-specific variants are resolved server-side into the context fee defaults.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nUsed by the frontend calculator as offline fee defaults and for the \"rates as of\" provenance chip (`version` / `effective_from`).\n\nRequires Bearer token (products scope)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/pro-forma/rate-cards","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","rate-cards"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"amazon\": [\n      {\n        \"id\": 1,\n        \"channel_type\": \"amazon\",\n        \"version\": \"2026-01\",\n        \"effective_from\": \"2026-01-15\",\n        \"fee_key\": \"referral_fee\",\n        \"label\": \"Referral fee\",\n        \"fee_basis\": \"percent_of_price\",\n        \"category\": \"default\",\n        \"percent\": 15,\n        \"fixed_amount\": null,\n        \"metadata\": {\n          \"minimum_per_item\": 0.3\n        }\n      },\n      {\n        \"id\": 2,\n        \"channel_type\": \"amazon\",\n        \"version\": \"2026-01\",\n        \"effective_from\": \"2026-01-15\",\n        \"fee_key\": \"fba_fulfillment\",\n        \"label\": \"FBA fulfillment fee\",\n        \"fee_basis\": \"fixed_per_unit\",\n        \"category\": \"default\",\n        \"percent\": null,\n        \"fixed_amount\": 6.92,\n        \"metadata\": {\n          \"size_tiers\": \"standard\"\n        }\n      }\n    ],\n    \"ebay\": [\n      {\n        \"id\": 5,\n        \"channel_type\": \"ebay\",\n        \"version\": \"2026-01\",\n        \"effective_from\": \"2026-01-15\",\n        \"fee_key\": \"final_value_fee\",\n        \"label\": \"Final value fee\",\n        \"fee_basis\": \"percent_of_price\",\n        \"category\": \"default\",\n        \"percent\": 13.6,\n        \"fixed_amount\": null,\n        \"metadata\": null\n      }\n    ]\n  }\n}"}]},{"name":"List Scenarios","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/pro-forma/scenarios","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","scenarios"]},"description":"The authenticated user's saved what-if scenarios, newest first. Scenarios are strictly per-user — other users' scenarios are never returned.\n\n**Required scope: `products:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`product_id` null on a scenario = hypothetical product (manual profile stored in `inputs.manual_profile`).\n\nRequires Bearer token (products scope)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/pro-forma/scenarios","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","scenarios"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 3,\n      \"name\": \"Amazon FBA at $99.99\",\n      \"channel_type\": \"amazon\",\n      \"product_id\": 1,\n      \"integration_instance_id\": 4,\n      \"inputs\": {\n        \"price\": 99.99,\n        \"fulfillment\": \"fba\",\n        \"fee_overrides\": {\n          \"referral_fee\": {\n            \"percent\": 15\n          }\n        },\n        \"shipping_cost\": 0\n      },\n      \"outputs\": {\n        \"net_profit\": 8.29,\n        \"margin_pct\": 8.29,\n        \"roi_pct\": 11.84,\n        \"breakeven_price\": 91.7\n      },\n      \"rate_card_version\": \"2026-01\",\n      \"created_at\": \"2026-06-10T18:24:11.000000Z\",\n      \"updated_at\": \"2026-06-10T18:24:11.000000Z\"\n    }\n  ]\n}"}]},{"name":"Create Scenario","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/pro-forma/scenarios","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","scenarios"]},"description":"Save a what-if scenario snapshot for the authenticated user.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody fields:\n- `name` (required, string, max 255)\n- `channel_type` (required, string, max 50) — e.g. amazon, shopify, ebay, custom\n- `product_id` (optional, nullable, integer, exists:products) — null for hypothetical products (store the manual profile in `inputs.manual_profile`)\n- `integration_instance_id` (optional, nullable, integer, exists:integration_instances)\n- `inputs` (required, object) — full input snapshot: prices, fee overrides, advanced levers, manual_profile\n- `outputs` (required, object) — computed metrics snapshot: net_profit, margin_pct, roi_pct, breakeven_price, ...\n- `rate_card_version` (optional, nullable, string, max 50) — rate-card version used, enables \"superseded rates\" prompts\n\nRequires Bearer token (products scope).","body":{"mode":"raw","raw":"{\n  \"name\": \"Amazon FBA at $99.99\",\n  \"channel_type\": \"amazon\",\n  \"product_id\": 1,\n  \"integration_instance_id\": 4,\n  \"inputs\": {\n    \"price\": 99.99,\n    \"fulfillment\": \"fba\",\n    \"fee_overrides\": {\n      \"referral_fee\": {\n        \"percent\": 15\n      }\n    },\n    \"shipping_cost\": 0\n  },\n  \"outputs\": {\n    \"net_profit\": 8.29,\n    \"margin_pct\": 8.29,\n    \"roi_pct\": 11.84,\n    \"breakeven_price\": 91.7\n  },\n  \"rate_card_version\": \"2026-01\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/pro-forma/scenarios","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","scenarios"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Amazon FBA at $99.99\",\n    \"channel_type\": \"amazon\",\n    \"product_id\": 1,\n    \"integration_instance_id\": 4,\n    \"inputs\": {\n      \"price\": 99.99,\n      \"fulfillment\": \"fba\",\n      \"fee_overrides\": {\n        \"referral_fee\": {\n          \"percent\": 15\n        }\n      },\n      \"shipping_cost\": 0\n    },\n    \"outputs\": {\n      \"net_profit\": 8.29,\n      \"margin_pct\": 8.29,\n      \"roi_pct\": 11.84,\n      \"breakeven_price\": 91.7\n    },\n    \"rate_card_version\": \"2026-01\",\n    \"created_at\": \"2026-06-10T18:24:11.000000Z\",\n    \"updated_at\": \"2026-06-10T18:24:11.000000Z\"\n  },\n  \"message\": \"Scenario saved\"\n}"}]},{"name":"Update Scenario","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/pro-forma/scenarios/{scenarioId}","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","scenarios",":scenarioId"],"variable":[{"key":"scenarioId","value":"3","description":"Scenario id (must belong to the authenticated user)"}]},"description":"Partially update a saved scenario. All fields optional (`sometimes`): name, channel_type, product_id, integration_instance_id, inputs, outputs, rate_card_version.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`inputs` and `outputs`, when present, are FULL snapshots — last write wins, no deep merge.\n\nReturns 404 when the scenario does not exist or belongs to another user.\n\nRequires Bearer token (products scope).","body":{"mode":"raw","raw":"{\n  \"name\": \"Amazon FBA at $109.99\",\n  \"inputs\": {\n    \"price\": 109.99,\n    \"fulfillment\": \"fba\"\n  },\n  \"outputs\": {\n    \"net_profit\": 14.8,\n    \"margin_pct\": 13.46\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/v2/pro-forma/scenarios/{scenarioId}","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","scenarios",":scenarioId"],"variable":[{"key":"scenarioId","value":"3","description":"Scenario id (must belong to the authenticated user)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 3,\n    \"name\": \"Amazon FBA at $109.99\",\n    \"channel_type\": \"amazon\",\n    \"product_id\": 1,\n    \"integration_instance_id\": 4,\n    \"inputs\": {\n      \"price\": 109.99,\n      \"fulfillment\": \"fba\"\n    },\n    \"outputs\": {\n      \"net_profit\": 14.8,\n      \"margin_pct\": 13.46\n    },\n    \"rate_card_version\": \"2026-01\",\n    \"created_at\": \"2026-06-10T18:24:11.000000Z\",\n    \"updated_at\": \"2026-06-10T18:24:11.000000Z\"\n  },\n  \"message\": \"Scenario updated\"\n}"}]},{"name":"Delete Scenario","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/pro-forma/scenarios/{scenarioId}","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","scenarios",":scenarioId"],"variable":[{"key":"scenarioId","value":"3","description":"Scenario id (must belong to the authenticated user)"}]},"description":"Delete a saved scenario. Returns 404 when the scenario does not exist or belongs to another user.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequires Bearer token (products scope)."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/pro-forma/scenarios/{scenarioId}","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","scenarios",":scenarioId"],"variable":[{"key":"scenarioId","value":"3","description":"Scenario id (must belong to the authenticated user)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Scenario deleted\"\n}"}]},{"name":"Update Product Assumptions","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/pro-forma/products/{productId}/assumptions","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","products",":productId","assumptions"],"variable":[{"key":"productId","value":"1","description":"Product id"}]},"description":"Persist product-level pro forma assumptions used as analyzer defaults, plus optional per-listing marketplace fee overrides.\n\n**Required scope: `products:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody fields (all optional/nullable):\n- `proforma_shipping_cost` (numeric, min 0) — default outbound shipping cost per unit\n- `proforma_landed_cost_percentage` (numeric, 0-100) — landed cost uplift percent\n- `proforma_marketplace_cost_percentage` (numeric, 0-100) — marketplace fee override percent (beats rate cards; listing-level value beats this)\n- `listings` (array, optional) — per-listing marketplace fee overrides\n- `listings.*.product_listing_id` (integer, required with listings, must exist) — listing to override; silently ignored if the listing does not belong to this product\n- `listings.*.proforma_marketplace_cost_percentage` (numeric|null, 0-100) — listing-level marketplace fee override percent (null clears the override)\n\nRequires Bearer token (products scope).","body":{"mode":"raw","raw":"{\n  \"proforma_shipping_cost\": 4.5,\n  \"proforma_landed_cost_percentage\": 6,\n  \"proforma_marketplace_cost_percentage\": 14,\n  \"listings\": [\n    {\n      \"product_listing_id\": 42,\n      \"proforma_marketplace_cost_percentage\": 14.5\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/v2/pro-forma/products/{productId}/assumptions","host":["{{baseUrl}}"],"path":["api","v2","pro-forma","products",":productId","assumptions"],"variable":[{"key":"productId","value":"1","description":"Product id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"shipping_cost\": 4.5,\n    \"landed_cost_percentage\": 6,\n    \"marketplace_cost_percentage\": 14\n  },\n  \"message\": \"Pro forma assumptions updated\"\n}"}]}]},{"name":"Merchandise Planning","item":[{"name":"List Merchandise Plans","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/open-to-buy/merchandise-plans","host":["{{baseUrl}}"],"path":["api","open-to-buy","merchandise-plans"]},"description":"Paginated Open-to-Buy plan grid. Each row carries its live OTB breakdown (otb_value, committed_value, effective_otb_value, otb_remaining_value, status) computed on read.\n\nEffective budget = min(computed OTB, budget_cap_value) when a manual cash cap is set; otb_remaining_value = effective budget − committed.\n\nRequires a Bearer token with the `inventory` read/write scope (middleware: scope.rw:inventory)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/open-to-buy/merchandise-plans","host":["{{baseUrl}}"],"path":["api","open-to-buy","merchandise-plans"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"retail_period_id\": 5,\n      \"node_type\": \"category\",\n      \"node_id\": 1,\n      \"planned_sales_value\": 80000,\n      \"planned_sales_units\": 1600,\n      \"planned_markdown_value\": 2000,\n      \"planned_eop_value\": 30000,\n      \"planned_eop_units\": 600,\n      \"bop_value\": 25000,\n      \"bop_units\": 500,\n      \"otb_value\": 87000,\n      \"otb_units\": 1700,\n      \"on_order_value\": 0,\n      \"committed_value\": 0,\n      \"budget_cap_value\": 30000,\n      \"effective_otb_value\": 30000,\n      \"otb_remaining_value\": 30000,\n      \"status\": \"under_budget\",\n      \"budget_enforced\": false,\n      \"retail_period\": {\n        \"id\": 5,\n        \"name\": \"Fall 2026\",\n        \"type\": \"season\",\n        \"start_date\": \"2026-08-01\",\n        \"end_date\": \"2026-10-31\"\n      },\n      \"created_at\": \"2026-06-22T14:03:11.000000Z\",\n      \"updated_at\": \"2026-06-22T14:09:52.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Create Merchandise Plan","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/open-to-buy/merchandise-plans","host":["{{baseUrl}}"],"path":["api","open-to-buy","merchandise-plans"]},"description":"Create a plan row for a node/period and compute its initial OTB.\n\nFields:\n- retail_period_id (required, integer, exists)\n- node_type (required, enum: category | brand | department | product)\n- node_id (required, integer >= 0)\n- planned_sales_value (optional, numeric >= 0)\n- planned_sales_units (optional, integer >= 0)\n- planned_markdown_value (optional, numeric >= 0)\n- planned_eop_value (optional, numeric >= 0)\n- planned_eop_units (optional, integer >= 0)\n- budget_enforced (optional, boolean) — enforce the OTB guardrail on forecast buys\n- budget_cap_value (optional, nullable, numeric >= 0) — manual cash ceiling; effective budget = min(computed OTB, this)\n\nRequires the `inventory` read/write scope.","body":{"mode":"raw","raw":"{\n  \"retail_period_id\": 5,\n  \"node_type\": \"category\",\n  \"node_id\": 1,\n  \"planned_sales_value\": 80000,\n  \"planned_sales_units\": 1600,\n  \"planned_markdown_value\": 2000,\n  \"planned_eop_value\": 30000,\n  \"planned_eop_units\": 600,\n  \"budget_enforced\": false,\n  \"budget_cap_value\": 30000\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/open-to-buy/merchandise-plans","host":["{{baseUrl}}"],"path":["api","open-to-buy","merchandise-plans"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"retail_period_id\": 5,\n    \"node_type\": \"category\",\n    \"node_id\": 1,\n    \"planned_sales_value\": 80000,\n    \"planned_sales_units\": 1600,\n    \"planned_markdown_value\": 2000,\n    \"planned_eop_value\": 30000,\n    \"planned_eop_units\": 600,\n    \"bop_value\": 25000,\n    \"bop_units\": 500,\n    \"otb_value\": 87000,\n    \"otb_units\": 1700,\n    \"on_order_value\": 0,\n    \"committed_value\": 0,\n    \"budget_cap_value\": 30000,\n    \"effective_otb_value\": 30000,\n    \"otb_remaining_value\": 30000,\n    \"status\": \"under_budget\",\n    \"budget_enforced\": false,\n    \"retail_period\": {\n      \"id\": 5,\n      \"name\": \"Fall 2026\",\n      \"type\": \"season\",\n      \"start_date\": \"2026-08-01\",\n      \"end_date\": \"2026-10-31\"\n    },\n    \"created_at\": \"2026-06-22T14:03:11.000000Z\",\n    \"updated_at\": \"2026-06-22T14:09:52.000000Z\"\n  }\n}"}]},{"name":"Seed Plan From Forecast / Last Year","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/open-to-buy/merchandise-plans/seed","host":["{{baseUrl}}"],"path":["api","open-to-buy","merchandise-plans","seed"]},"description":"Seed a period's grid in bulk from a forecast or last-year actuals. Each incoming node row upserts a plan and gets its OTB computed.\n\nFields:\n- retail_period_id (required, integer, exists)\n- source (required, one of: forecast, last_year)\n- rows (required, array, min 1)\n  - rows.*.node_type (required, enum: category | brand | department | product)\n  - rows.*.node_id (required, integer >= 0)\n  - rows.*.planned_sales_value (optional, numeric >= 0)\n  - rows.*.planned_sales_units (optional, integer >= 0)\n  - rows.*.planned_markdown_value (optional, numeric >= 0)\n  - rows.*.planned_eop_value (optional, numeric >= 0)\n  - rows.*.planned_eop_units (optional, integer >= 0)\n\nRequires the `inventory` read/write scope.","body":{"mode":"raw","raw":"{\n  \"retail_period_id\": 5,\n  \"source\": \"forecast\",\n  \"rows\": [\n    {\n      \"node_type\": \"category\",\n      \"node_id\": 1,\n      \"planned_sales_value\": 80000,\n      \"planned_sales_units\": 1600,\n      \"planned_markdown_value\": 2000,\n      \"planned_eop_value\": 30000,\n      \"planned_eop_units\": 600\n    },\n    {\n      \"node_type\": \"category\",\n      \"node_id\": 2,\n      \"planned_sales_value\": 45000,\n      \"planned_sales_units\": 900,\n      \"planned_markdown_value\": 1000,\n      \"planned_eop_value\": 18000,\n      \"planned_eop_units\": 360\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/open-to-buy/merchandise-plans/seed","host":["{{baseUrl}}"],"path":["api","open-to-buy","merchandise-plans","seed"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"retail_period_id\": 5,\n      \"node_type\": \"category\",\n      \"node_id\": 1,\n      \"planned_sales_value\": 80000,\n      \"planned_sales_units\": 1600,\n      \"planned_markdown_value\": 2000,\n      \"planned_eop_value\": 30000,\n      \"planned_eop_units\": 600,\n      \"bop_value\": 25000,\n      \"bop_units\": 500,\n      \"otb_value\": 87000,\n      \"otb_units\": 1700,\n      \"on_order_value\": 0,\n      \"committed_value\": 0,\n      \"budget_cap_value\": 30000,\n      \"effective_otb_value\": 30000,\n      \"otb_remaining_value\": 30000,\n      \"status\": \"under_budget\",\n      \"budget_enforced\": false,\n      \"retail_period\": {\n        \"id\": 5,\n        \"name\": \"Fall 2026\",\n        \"type\": \"season\",\n        \"start_date\": \"2026-08-01\",\n        \"end_date\": \"2026-10-31\"\n      },\n      \"created_at\": \"2026-06-22T14:03:11.000000Z\",\n      \"updated_at\": \"2026-06-22T14:09:52.000000Z\"\n    }\n  ]\n}"}]},{"name":"Get Merchandise Plan","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/open-to-buy/merchandise-plans/{merchandisePlan}","host":["{{baseUrl}}"],"path":["api","open-to-buy","merchandise-plans",":merchandisePlan"],"variable":[{"key":"merchandisePlan","value":"1","description":"Merchandise plan id."}]},"description":"Show a single plan row with its live OTB breakdown (including budget_cap_value, effective_otb_value, otb_remaining_value).\n\nRequires the `inventory` read/write scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/open-to-buy/merchandise-plans/{merchandisePlan}","host":["{{baseUrl}}"],"path":["api","open-to-buy","merchandise-plans",":merchandisePlan"],"variable":[{"key":"merchandisePlan","value":"1","description":"Merchandise plan id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"retail_period_id\": 5,\n    \"node_type\": \"category\",\n    \"node_id\": 1,\n    \"planned_sales_value\": 80000,\n    \"planned_sales_units\": 1600,\n    \"planned_markdown_value\": 2000,\n    \"planned_eop_value\": 30000,\n    \"planned_eop_units\": 600,\n    \"bop_value\": 25000,\n    \"bop_units\": 500,\n    \"otb_value\": 87000,\n    \"otb_units\": 1700,\n    \"on_order_value\": 0,\n    \"committed_value\": 0,\n    \"budget_cap_value\": 30000,\n    \"effective_otb_value\": 30000,\n    \"otb_remaining_value\": 30000,\n    \"status\": \"under_budget\",\n    \"budget_enforced\": false,\n    \"retail_period\": {\n      \"id\": 5,\n      \"name\": \"Fall 2026\",\n      \"type\": \"season\",\n      \"start_date\": \"2026-08-01\",\n      \"end_date\": \"2026-10-31\"\n    },\n    \"created_at\": \"2026-06-22T14:03:11.000000Z\",\n    \"updated_at\": \"2026-06-22T14:09:52.000000Z\"\n  }\n}"}]},{"name":"Update Merchandise Plan (cell save / cash cap)","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/open-to-buy/merchandise-plans/{merchandisePlan}","host":["{{baseUrl}}"],"path":["api","open-to-buy","merchandise-plans",":merchandisePlan"],"variable":[{"key":"merchandisePlan","value":"1","description":"Merchandise plan id."}]},"description":"Update editable plan cell(s) — a debounced save — and recompute OTB. PATCH only the changed fields.\n\nFields (all optional / 'sometimes'):\n- planned_sales_value (numeric >= 0)\n- planned_sales_units (integer >= 0)\n- planned_markdown_value (numeric >= 0)\n- planned_eop_value (numeric >= 0)\n- planned_eop_units (integer >= 0)\n- budget_enforced (boolean)\n- budget_cap_value (nullable, numeric >= 0) — set a manual cash ceiling; pass null to clear it (back to the full computed OTB)\n\nWhen a cash cap below the computed OTB is set, effective_otb_value and otb_remaining_value drop to the cap.\n\nRequires the `inventory` read/write scope.","body":{"mode":"raw","raw":"{\n  \"planned_sales_value\": 80000,\n  \"planned_eop_value\": 30000,\n  \"budget_enforced\": false,\n  \"budget_cap_value\": 30000\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/open-to-buy/merchandise-plans/{merchandisePlan}","host":["{{baseUrl}}"],"path":["api","open-to-buy","merchandise-plans",":merchandisePlan"],"variable":[{"key":"merchandisePlan","value":"1","description":"Merchandise plan id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"retail_period_id\": 5,\n    \"node_type\": \"category\",\n    \"node_id\": 1,\n    \"planned_sales_value\": 80000,\n    \"planned_sales_units\": 1600,\n    \"planned_markdown_value\": 2000,\n    \"planned_eop_value\": 30000,\n    \"planned_eop_units\": 600,\n    \"bop_value\": 25000,\n    \"bop_units\": 500,\n    \"otb_value\": 87000,\n    \"otb_units\": 1700,\n    \"on_order_value\": 0,\n    \"committed_value\": 0,\n    \"budget_cap_value\": 30000,\n    \"effective_otb_value\": 30000,\n    \"otb_remaining_value\": 30000,\n    \"status\": \"under_budget\",\n    \"budget_enforced\": false,\n    \"retail_period\": {\n      \"id\": 5,\n      \"name\": \"Fall 2026\",\n      \"type\": \"season\",\n      \"start_date\": \"2026-08-01\",\n      \"end_date\": \"2026-10-31\"\n    },\n    \"created_at\": \"2026-06-22T14:03:11.000000Z\",\n    \"updated_at\": \"2026-06-22T14:09:52.000000Z\"\n  }\n}"}]},{"name":"Get OTB Breakdown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/open-to-buy/merchandise-plans/{merchandisePlan}/otb","host":["{{baseUrl}}"],"path":["api","open-to-buy","merchandise-plans",":merchandisePlan","otb"],"variable":[{"key":"merchandisePlan","value":"1","description":"Merchandise plan id."}]},"description":"Live OTB + remaining + committed breakdown for a plan row.\n\nReturns: otb_value, otb_units, bop_value, bop_units, on_order_value, committed_value, budget_cap_value (nullable), effective_otb_value (= min(otb_value, cap)), otb_remaining_value (= effective − committed), and status (under_budget | near_budget | over_budget).\n\nRequires the `inventory` read/write scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/open-to-buy/merchandise-plans/{merchandisePlan}/otb","host":["{{baseUrl}}"],"path":["api","open-to-buy","merchandise-plans",":merchandisePlan","otb"],"variable":[{"key":"merchandisePlan","value":"1","description":"Merchandise plan id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"otb_value\": 87000,\n    \"otb_units\": 1700,\n    \"bop_value\": 25000,\n    \"bop_units\": 500,\n    \"on_order_value\": 0,\n    \"committed_value\": 0,\n    \"budget_cap_value\": 30000,\n    \"effective_otb_value\": 30000,\n    \"otb_remaining_value\": 30000,\n    \"status\": \"under_budget\"\n  }\n}"}]}]},{"name":"Serial Numbers","item":[{"name":"List Serial Numbers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/serial-numbers","host":["{{baseUrl}}"],"path":["api","v2","serial-numbers"]},"description":"Paginated list of individual serial numbers across all products, with their lifecycle status and current location/order.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Advanced filters.** Pass `filter_groups` — a base64-encoded JSON tree of AND/OR groups, e.g. `{\"conjunction\":\"and\",\"children\":[{\"type\":\"condition\",\"condition\":{\"column\":\"product_sku\",\"operator\":\"is\",\"value\":\"ATL-GPS-60\"}}]}`. Columns: id, serial, status, product_id, warehouse_id, product_sku, product_name (text/numeric operators: is, is_not, is_one_of, is_not_one_of, contains, does_not_contain, starts_with, ends_with, is_empty, is_not_empty, greater_than, less_than, between) and the datetimes received_at, shipped_at, quarantined_at, created_at.\n\n**Search.** `filter[search]` matches the serial (partial), the product SKU (partial) and the id (exact). Narrow it with `search_columns=serial,product_sku,id`.\n\n**Sorts.** id, serial, status, received_at, shipped_at, quarantined_at, created_at (prefix - for descending; default -id). Default page size 10."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/serial-numbers","host":["{{baseUrl}}"],"path":["api","v2","serial-numbers"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 91,\n      \"serial\": \"SN-100482\",\n      \"status\": \"in_stock\",\n      \"status_label\": \"In Stock\",\n      \"product\": {\n        \"id\": 2,\n        \"sku\": \"WIDGET-SN\",\n        \"name\": \"Serialized Widget\",\n        \"link_route\": \"/products/2\"\n      },\n      \"warehouse\": {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      },\n      \"received_at\": \"2026-07-19T10:00:00.000000Z\",\n      \"shipped_at\": null,\n      \"current_order\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1\n}"}]},{"name":"Get Serial Number","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/serial-numbers/{serialNumber}","host":["{{baseUrl}}"],"path":["api","v2","serial-numbers",":serialNumber"],"variable":[{"key":"serialNumber","value":"91","description":"Serial number id"}]},"description":"A single serial's full detail plus its genealogy timeline (received → allocated → committed → shipped → returned/scrapped/quarantined), each event cross-linked to its source document, order and customer.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Timeline.** `timeline[]` is ordered by when each step happened. `event` names the step the way the screen does: a sale is `shipped`, a transfer is `transferred_out` then `transferred_in`, a return to a vendor is `returned_to_vendor`, production and assembly use is `consumed`, and a stock take shortfall or inventory decrease is `scrapped`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/serial-numbers/{serialNumber}","host":["{{baseUrl}}"],"path":["api","v2","serial-numbers",":serialNumber"],"variable":[{"key":"serialNumber","value":"91","description":"Serial number id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 91,\n    \"serial\": \"SN-100482\",\n    \"status\": \"shipped\",\n    \"status_label\": \"Shipped\",\n    \"product\": {\n      \"id\": 2,\n      \"sku\": \"WIDGET-SN\",\n      \"name\": \"Serialized Widget\",\n      \"link_route\": \"/products/2\"\n    },\n    \"warehouse\": null,\n    \"received_at\": \"2026-07-19T10:00:00.000000Z\",\n    \"shipped_at\": \"2026-07-21T09:00:00.000000Z\",\n    \"current_order\": {\n      \"number\": \"SO-1234\",\n      \"link_route\": \"/orders/sales-orders/33\"\n    },\n    \"warranty_start_at\": \"2026-07-21\",\n    \"warranty_registration_ref\": null,\n    \"disposition_reason\": null,\n    \"timeline\": [\n      {\n        \"event\": \"received\",\n        \"at\": \"2026-07-19T10:00:00.000000Z\",\n        \"movement_id\": 10,\n        \"link_reference\": \"PO-4501\",\n        \"link_route\": \"/orders/purchase-orders/9\"\n      },\n      {\n        \"event\": \"shipped\",\n        \"at\": \"2026-07-21T09:00:00.000000Z\",\n        \"movement_id\": 55,\n        \"sales_order_number\": \"SO-1234\",\n        \"customer_name\": \"Acme Corporation\",\n        \"link_route\": \"/orders/sales-orders/33\"\n      }\n    ]\n  }\n}"}]},{"name":"List Product Serial Numbers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/serial-numbers","host":["{{baseUrl}}"],"path":["api","v2","products",":product","serial-numbers"],"variable":[{"key":"product","value":"2","description":"Product id"}]},"description":"The serials for a single product. Accepts the same filters, `filter_groups` tree, search and sorts as List Serial Numbers.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/serial-numbers","host":["{{baseUrl}}"],"path":["api","v2","products",":product","serial-numbers"],"variable":[{"key":"product","value":"2","description":"Product id"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 91,\n      \"serial\": \"SN-100482\",\n      \"status\": \"in_stock\",\n      \"status_label\": \"In Stock\",\n      \"product\": {\n        \"id\": 2,\n        \"sku\": \"WIDGET-SN\",\n        \"name\": \"Serialized Widget\",\n        \"link_route\": \"/products/2\"\n      },\n      \"warehouse\": {\n        \"id\": 1,\n        \"name\": \"Main Warehouse\"\n      },\n      \"received_at\": \"2026-07-19T10:00:00.000000Z\",\n      \"shipped_at\": null,\n      \"current_order\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1\n}"}]},{"name":"Quarantine Serial Numbers","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/serial-numbers/quarantine","host":["{{baseUrl}}"],"path":["api","v2","serial-numbers","quarantine"]},"description":"Hold one or more serials (recall / damage / inspection). Quarantined serials are excluded from availability and cannot be allocated or shipped until released.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody: `serial_number_ids` (array of ids — required unless `apply_to_all`), `apply_to_all` (boolean, optional), `filters` (object, with `apply_to_all`), `reason` (optional, max 255).\n\n**Acting on every filtered serial.** Instead of `serial_number_ids`, send `apply_to_all: true` with `filters` — the same filters the list accepts (bare keys such as `status` / `warehouse_id` / `search`, and/or `filter_groups`). Every serial those filters match is targeted.\n\n**Response.** Returns `quarantined` and `skipped` counts: only serials that are on hand (In Stock, Allocated or Committed) can be quarantined, the rest are skipped. When none of the serials can be quarantined, the call fails with 422 and the same counts.","body":{"mode":"raw","raw":"{\n  \"serial_number_ids\": [\n    91,\n    92\n  ],\n  \"reason\": \"Recall batch 2026-07\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/serial-numbers/quarantine","host":["{{baseUrl}}"],"path":["api","v2","serial-numbers","quarantine"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"7 serial(s) quarantined. 2 skipped because they aren't in stock.\",\n  \"quarantined\": 7,\n  \"skipped\": 2\n}"}]},{"name":"Release Serial Numbers","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/serial-numbers/release","host":["{{baseUrl}}"],"path":["api","v2","serial-numbers","release"]},"description":"Release quarantined serials back to in-stock.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBody: `serial_number_ids` (array of ids — required unless `apply_to_all`), `apply_to_all` (boolean, optional), `filters` (object, with `apply_to_all`).\n\n**Acting on every filtered serial.** Instead of `serial_number_ids`, send `apply_to_all: true` with `filters` — the same filters the list accepts (bare keys such as `status` / `warehouse_id` / `search`, and/or `filter_groups`). Every serial those filters match is targeted.\n\n**Response.** Returns `released` and `skipped` counts: only quarantined serials are released. When none are quarantined, the call fails with 422.","body":{"mode":"raw","raw":"{\n  \"serial_number_ids\": [\n    91,\n    92\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/serial-numbers/release","host":["{{baseUrl}}"],"path":["api","v2","serial-numbers","release"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Serials released back to stock.\",\n  \"released\": 7,\n  \"skipped\": 0\n}"}]},{"name":"Serial Recall Impact","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/serial-numbers/recall-impact?product_id=2","host":["{{baseUrl}}"],"path":["api","v2","serial-numbers","recall-impact"],"query":[{"key":"product_id","value":"2","description":"Required. Product to assess.","disabled":true}]},"description":"For a product (optionally narrowed to one receiving movement), list the shipped serials with the orders/customers they went to — the affected-customer list for a recall.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/serial-numbers/recall-impact?product_id=2","host":["{{baseUrl}}"],"path":["api","v2","serial-numbers","recall-impact"],"query":[{"key":"product_id","value":"2","description":"Required. Product to assess.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"serial\": \"SN-100482\",\n      \"sales_order_number\": \"SO-1234\",\n      \"customer_name\": \"Acme Corporation\",\n      \"shipped_at\": \"2026-07-21\"\n    }\n  ]\n}"}]}]},{"name":"Lot Compliance","item":[{"name":"List Lot Documents","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/documents","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","documents"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]},"description":"Compliance documents filed against a lot — Certificates of Analysis, batch records, production photos.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAlso returns how many lots share this lot's batch number, and any documents already filed on sibling lots of the same batch that are not yet linked to this one."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/documents","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","documents"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"documents\": [\n      {\n        \"id\": 412,\n        \"file_name\": \"coa-B240817.pdf\",\n        \"mime_type\": \"application/pdf\",\n        \"is_image\": false,\n        \"file_size\": 184320,\n        \"source\": \"lot_compliance\",\n        \"description\": \"Finished-product COA for batch B240817\",\n        \"document_type\": \"coa\",\n        \"document_type_label\": \"Certificate of Analysis\",\n        \"document_type_color\": \"primary\",\n        \"issued_at\": \"2026-08-01\",\n        \"expires_at\": null,\n        \"expiry_state\": null,\n        \"issuer\": \"Eurofins\",\n        \"reference\": \"L-99213\",\n        \"file_url\": \"https://acme.sku.io/api/v2/compliance-documents/412/file\",\n        \"uploaded_by\": \"Dana Reyes\",\n        \"linked_records\": [\n          {\n            \"type\": \"App\\\\Models\\\\FifoLayer\",\n            \"id\": 8842\n          },\n          {\n            \"type\": \"App\\\\Models\\\\FifoLayer\",\n            \"id\": 8851\n          }\n        ],\n        \"created_at\": \"2026-08-18T14:22:11.000000Z\"\n      }\n    ],\n    \"batch_lot_count\": 2,\n    \"linkable_batch_documents\": [],\n    \"document_types\": [\n      {\n        \"value\": \"coa\",\n        \"label\": \"Certificate of Analysis\",\n        \"color\": \"primary\"\n      },\n      {\n        \"value\": \"batch_record\",\n        \"label\": \"Batch Record\",\n        \"color\": \"indigo\"\n      },\n      {\n        \"value\": \"spec_sheet\",\n        \"label\": \"Specification Sheet\",\n        \"color\": \"teal\"\n      },\n      {\n        \"value\": \"lab_report\",\n        \"label\": \"Lab Report\",\n        \"color\": \"indigo\"\n      },\n      {\n        \"value\": \"batch_photo\",\n        \"label\": \"Batch Photo\",\n        \"color\": \"grey\"\n      },\n      {\n        \"value\": \"other\",\n        \"label\": \"Other\",\n        \"color\": \"grey\"\n      }\n    ]\n  }\n}"}]},{"name":"Upload Lot Document","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/documents","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","documents"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]},"description":"File a compliance document against a lot.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA Certificate of Analysis belongs to the lot, not the purchase order: one purchase order receives many lots, one lot can arrive across many purchase orders, and a recall is scoped by lot.\n\nSet `apply_to_batch` to link the same stored file to every lot sharing this lot's batch number — a supplier batch routinely lands as several lots (two warehouses, a split receipt), and the certificate covers the batch.\n\nSend as multipart/form-data. Max file size 20 MB.","body":{"mode":"formdata","formdata":[{"key":"file","type":"file","src":[]},{"key":"document_type","type":"text","value":""},{"key":"issuer","type":"text","value":""},{"key":"reference","type":"text","value":""},{"key":"issued_at","type":"text","value":""},{"key":"expires_at","type":"text","value":""},{"key":"description","type":"text","value":""},{"key":"apply_to_batch","type":"text","value":""}]}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/documents","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","documents"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 412,\n    \"file_name\": \"coa-B240817.pdf\",\n    \"mime_type\": \"application/pdf\",\n    \"is_image\": false,\n    \"file_size\": 184320,\n    \"source\": \"lot_compliance\",\n    \"description\": \"Finished-product COA for batch B240817\",\n    \"document_type\": \"coa\",\n    \"document_type_label\": \"Certificate of Analysis\",\n    \"document_type_color\": \"primary\",\n    \"issued_at\": \"2026-08-01\",\n    \"expires_at\": null,\n    \"expiry_state\": null,\n    \"issuer\": \"Eurofins\",\n    \"reference\": \"L-99213\",\n    \"file_url\": \"https://acme.sku.io/api/v2/compliance-documents/412/file\",\n    \"uploaded_by\": \"Dana Reyes\",\n    \"linked_records\": [\n      {\n        \"type\": \"App\\\\Models\\\\FifoLayer\",\n        \"id\": 8842\n      },\n      {\n        \"type\": \"App\\\\Models\\\\FifoLayer\",\n        \"id\": 8851\n      }\n    ],\n    \"created_at\": \"2026-08-18T14:22:11.000000Z\"\n  }\n}"}]},{"name":"Update Lot Document","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/documents/{document}","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","documents",":document"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."},{"key":"document","value":"","description":"The document ID."}]},"description":"Correct a document's metadata — type, issuer, reference, issue and expiry dates.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nOnly the fields you send are changed; the stored file itself is immutable, so re-upload to replace it.","body":{"mode":"raw","raw":"{\n  \"document_type\": \"coa\",\n  \"issuer\": \"Eurofins\",\n  \"reference\": \"L-99213\",\n  \"issued_at\": \"2026-08-01\",\n  \"expires_at\": null,\n  \"description\": \"Corrected reference\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/documents/{document}","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","documents",":document"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."},{"key":"document","value":"","description":"The document ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 412,\n    \"file_name\": \"coa-B240817.pdf\",\n    \"mime_type\": \"application/pdf\",\n    \"is_image\": false,\n    \"file_size\": 184320,\n    \"source\": \"lot_compliance\",\n    \"description\": \"Finished-product COA for batch B240817\",\n    \"document_type\": \"coa\",\n    \"document_type_label\": \"Certificate of Analysis\",\n    \"document_type_color\": \"primary\",\n    \"issued_at\": \"2026-08-01\",\n    \"expires_at\": null,\n    \"expiry_state\": null,\n    \"issuer\": \"Eurofins\",\n    \"reference\": \"L-99213\",\n    \"file_url\": \"https://acme.sku.io/api/v2/compliance-documents/412/file\",\n    \"uploaded_by\": \"Dana Reyes\",\n    \"linked_records\": [\n      {\n        \"type\": \"App\\\\Models\\\\FifoLayer\",\n        \"id\": 8842\n      },\n      {\n        \"type\": \"App\\\\Models\\\\FifoLayer\",\n        \"id\": 8851\n      }\n    ],\n    \"created_at\": \"2026-08-18T14:22:11.000000Z\"\n  }\n}"}]},{"name":"Remove Lot Document","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/documents/{document}","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","documents",":document"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."},{"key":"document","value":"","description":"The document ID."}]},"description":"Detach the document from THIS lot. The stored file is deleted only when that was its last link anywhere — removing a Certificate of Analysis from one lot of a batch leaves the other lots certified.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/documents/{document}","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","documents",":document"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."},{"key":"document","value":"","description":"The document ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Document removed from this lot. It remains on other records.\"\n}"}]},{"name":"Get Lot Document Link Count","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/documents/{document}/link-count","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","documents",":document","link-count"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."},{"key":"document","value":"","description":"The document ID."}]},"description":"How many records still reference this document, so a client can state whether removing it from this lot will unlink it or permanently delete the file.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/documents/{document}/link-count","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","documents",":document","link-count"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."},{"key":"document","value":"","description":"The document ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"total_links\": 2,\n    \"other_links\": 1,\n    \"will_delete_file\": false\n  }\n}"}]},{"name":"Link Batch Document to Lot","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/documents/{document}/link","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","documents",":document","link"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."},{"key":"document","value":"","description":"The document ID."}]},"description":"Adopt a document already filed on a sibling lot of the same batch.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA lot received after the batch's Certificate of Analysis was filed does not pick it up automatically — this is the explicit accept action.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/documents/{document}/link","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","documents",":document","link"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."},{"key":"document","value":"","description":"The document ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 412,\n    \"file_name\": \"coa-B240817.pdf\",\n    \"mime_type\": \"application/pdf\",\n    \"is_image\": false,\n    \"file_size\": 184320,\n    \"source\": \"lot_compliance\",\n    \"description\": \"Finished-product COA for batch B240817\",\n    \"document_type\": \"coa\",\n    \"document_type_label\": \"Certificate of Analysis\",\n    \"document_type_color\": \"primary\",\n    \"issued_at\": \"2026-08-01\",\n    \"expires_at\": null,\n    \"expiry_state\": null,\n    \"issuer\": \"Eurofins\",\n    \"reference\": \"L-99213\",\n    \"file_url\": \"https://acme.sku.io/api/v2/compliance-documents/412/file\",\n    \"uploaded_by\": \"Dana Reyes\",\n    \"linked_records\": [\n      {\n        \"type\": \"App\\\\Models\\\\FifoLayer\",\n        \"id\": 8842\n      },\n      {\n        \"type\": \"App\\\\Models\\\\FifoLayer\",\n        \"id\": 8851\n      }\n    ],\n    \"created_at\": \"2026-08-18T14:22:11.000000Z\"\n  }\n}"}]},{"name":"Get Lot COA Results","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/coa-results","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","coa-results"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]},"description":"The quality specification that applies to this lot, merged with any results already captured.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA specification line with no result yet is returned as a blank row rather than omitted, so a client can show what is still owed.\n\n`receiving_verification` is deliberately separate from the Certificate of Analysis: under 21 CFR 111.75 a supplier's certificate does not by itself verify a component's identity."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/coa-results","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","coa-results"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"results\": [\n      {\n        \"name\": \"Withanolides\",\n        \"product_specification_id\": 31,\n        \"method\": \"HPLC\",\n        \"unit\": \"%\",\n        \"spec_min\": 2.5,\n        \"spec_max\": null,\n        \"spec_expected_text\": null,\n        \"is_required\": true,\n        \"is_orphaned\": false,\n        \"result_value\": 2.8,\n        \"result_text\": null,\n        \"passed\": true,\n        \"is_suggested\": false,\n        \"captured_at\": \"2026-08-18T14:31:02.000000Z\"\n      },\n      {\n        \"name\": \"Lead\",\n        \"product_specification_id\": 32,\n        \"method\": \"ICP-MS\",\n        \"unit\": \"ppm\",\n        \"spec_min\": null,\n        \"spec_max\": 0.5,\n        \"spec_expected_text\": null,\n        \"is_required\": true,\n        \"is_orphaned\": false,\n        \"result_value\": 0.4,\n        \"result_text\": null,\n        \"passed\": true,\n        \"is_suggested\": false,\n        \"captured_at\": \"2026-08-18T14:31:02.000000Z\"\n      },\n      {\n        \"name\": \"Salmonella\",\n        \"product_specification_id\": 33,\n        \"method\": \"AOAC 2016.01\",\n        \"unit\": null,\n        \"spec_min\": null,\n        \"spec_max\": null,\n        \"spec_expected_text\": \"Absent\",\n        \"is_required\": true,\n        \"is_orphaned\": false,\n        \"result_value\": null,\n        \"result_text\": \"Absent\",\n        \"passed\": true,\n        \"is_suggested\": false,\n        \"captured_at\": \"2026-08-18T14:31:02.000000Z\"\n      }\n    ],\n    \"overall\": \"passed\",\n    \"lot_status\": null,\n    \"supplier_id\": 14,\n    \"receiving_verification\": {\n      \"method\": \"FTIR identity\",\n      \"notes\": null,\n      \"verified_by\": \"Dana Reyes\",\n      \"verified_at\": \"2026-08-18T15:02:00.000000Z\"\n    }\n  }\n}"}]},{"name":"Record Lot COA Results","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/coa-results","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","coa-results"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]},"description":"Record the values read off a Certificate of Analysis, evaluate them against the product specification, and gate the lot.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA result outside specification quarantines the lot: the available quantity is placed on a quality-control hold and leaves available-to-promise. A lot that previously failed and now passes has that hold released automatically.\n\nA named analyte left blank is `incomplete`, not `failed` — it warns but does not quarantine. With no specification on the product nothing can be judged and the verdict is `not_applicable`.\n\nSet `is_suggested` when the values came from automated document extraction rather than a person: suggested values are stored but never evaluated and never gate a lot.\n\nOverall verdicts: passed, failed, incomplete, not_applicable.","body":{"mode":"raw","raw":"{\n  \"document_id\": 412,\n  \"is_suggested\": false,\n  \"results\": [\n    {\n      \"name\": \"Withanolides\",\n      \"product_specification_id\": 31,\n      \"unit\": \"%\",\n      \"result_value\": 2.8,\n      \"result_text\": null\n    },\n    {\n      \"name\": \"Lead\",\n      \"product_specification_id\": 32,\n      \"unit\": \"ppm\",\n      \"result_value\": 0.4,\n      \"result_text\": null\n    },\n    {\n      \"name\": \"Salmonella\",\n      \"product_specification_id\": 33,\n      \"unit\": null,\n      \"result_value\": null,\n      \"result_text\": \"Absent\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/coa-results","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","coa-results"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"results\": [\n      {\n        \"name\": \"Withanolides\",\n        \"product_specification_id\": 31,\n        \"method\": \"HPLC\",\n        \"unit\": \"%\",\n        \"spec_min\": 2.5,\n        \"spec_max\": null,\n        \"spec_expected_text\": null,\n        \"is_required\": true,\n        \"is_orphaned\": false,\n        \"result_value\": 2.8,\n        \"result_text\": null,\n        \"passed\": true,\n        \"is_suggested\": false,\n        \"captured_at\": \"2026-08-18T14:31:02.000000Z\"\n      },\n      {\n        \"name\": \"Lead\",\n        \"product_specification_id\": 32,\n        \"method\": \"ICP-MS\",\n        \"unit\": \"ppm\",\n        \"spec_min\": null,\n        \"spec_max\": 0.5,\n        \"spec_expected_text\": null,\n        \"is_required\": true,\n        \"is_orphaned\": false,\n        \"result_value\": 0.4,\n        \"result_text\": null,\n        \"passed\": true,\n        \"is_suggested\": false,\n        \"captured_at\": \"2026-08-18T14:31:02.000000Z\"\n      },\n      {\n        \"name\": \"Salmonella\",\n        \"product_specification_id\": 33,\n        \"method\": \"AOAC 2016.01\",\n        \"unit\": null,\n        \"spec_min\": null,\n        \"spec_max\": null,\n        \"spec_expected_text\": \"Absent\",\n        \"is_required\": true,\n        \"is_orphaned\": false,\n        \"result_value\": null,\n        \"result_text\": \"Absent\",\n        \"passed\": true,\n        \"is_suggested\": false,\n        \"captured_at\": \"2026-08-18T14:31:02.000000Z\"\n      }\n    ],\n    \"overall\": \"passed\",\n    \"overall_label\": \"COA passed\",\n    \"overall_color\": \"success\",\n    \"lot_status\": null,\n    \"inventory_hold_id\": null\n  }\n}"}]},{"name":"Record Lot Receiving Verification","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/receiving-verification","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","receiving-verification"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]},"description":"Record your own identity verification of a received lot, under 21 CFR 111.75.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nKept separate from the supplier's Certificate of Analysis on purpose: the certificate alone does not satisfy the per-lot identity requirement, so this is always a deliberate act with its own actor and timestamp. One verification per lot — recording again replaces the previous one.","body":{"mode":"raw","raw":"{\n  \"method\": \"FTIR identity\",\n  \"notes\": \"Matched reference spectrum.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/receiving-verification","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","receiving-verification"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"method\": \"FTIR identity\",\n    \"notes\": \"Matched reference spectrum.\",\n    \"verified_by\": \"Dana Reyes\",\n    \"verified_at\": \"2026-08-18T15:02:00.000000Z\"\n  }\n}"}]},{"name":"List Lot Retention Samples","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/retention-samples","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","retention-samples"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]},"description":"Reserve samples held against a lot under 21 CFR 111.83.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAlso returns the retention date the rule would produce for a new sample, and which branch of the rule produced it — one year past the lot's expiry where shelf-life dating is used, otherwise two years from the retention date.\n\n**Stock effect.** A reserve sample takes its units off the shelf: creating one places an inventory hold for `quantity`, so those units leave available-to-sell. `inventory_hold_id` and `held_quantity` report it. The hold is released when the sample's `status` becomes `returned`, and when the sample is deleted. A `discarded` sample KEEPS its hold — the units were destroyed, so releasing them would return stock that no longer exists to available-to-promise; write them off with a separate inventory adjustment. `quantity` is always in stock units; `unit` is a descriptive note. Retaining more than the lot has available returns 422."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/retention-samples","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","retention-samples"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"samples\": [\n      {\n        \"id\": 77,\n        \"fifo_layer_id\": 8842,\n        \"sample_identifier\": \"RS-000412\",\n        \"quantity\": 2,\n        \"unit\": \"bottle\",\n        \"storage_location\": \"Cabinet A\",\n        \"inventory_hold_id\": 9014,\n        \"held_quantity\": 2,\n        \"retained_at\": \"2026-08-18\",\n        \"retain_until\": \"2031-02-14\",\n        \"retain_until_overridden\": false,\n        \"is_due_for_disposal\": false,\n        \"status\": \"retained\",\n        \"status_label\": \"Retained\",\n        \"status_color\": \"success\",\n        \"lab_name\": null,\n        \"sent_at\": null,\n        \"tracking_number\": null,\n        \"disposition_notes\": null,\n        \"disposed_at\": null,\n        \"created_by\": \"Dana Reyes\",\n        \"batch_number\": \"B240817\",\n        \"product_name\": \"Ashwagandha 500mg\",\n        \"created_at\": \"2026-08-18T14:40:00.000000Z\"\n      }\n    ],\n    \"retain_until_preview\": {\n      \"retain_until\": \"2031-02-14\",\n      \"rule\": \"1 year past the lot expiry date (21 CFR 111.83)\"\n    },\n    \"statuses\": [\n      {\n        \"value\": \"retained\",\n        \"label\": \"Retained\",\n        \"color\": \"success\"\n      },\n      {\n        \"value\": \"sent_to_lab\",\n        \"label\": \"Sent to Lab\",\n        \"color\": \"warning\"\n      },\n      {\n        \"value\": \"returned\",\n        \"label\": \"Returned\",\n        \"color\": \"info\"\n      },\n      {\n        \"value\": \"discarded\",\n        \"label\": \"Discarded\",\n        \"color\": \"grey\"\n      }\n    ]\n  }\n}"}]},{"name":"Create Lot Retention Sample","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/retention-samples","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","retention-samples"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]},"description":"Record a reserve sample held against a lot.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nOmit `retain_until` to let the 21 CFR 111.83 clock compute it. Supplying it is treated as a deliberate override and is flagged as such, so a computed date can be told apart from a chosen one.\n\nA sample with status `sent_to_lab` must name the lab holding it.\n\n**Stock effect.** A reserve sample takes its units off the shelf: creating one places an inventory hold for `quantity`, so those units leave available-to-sell. `inventory_hold_id` and `held_quantity` report it. The hold is released when the sample's `status` becomes `returned`, and when the sample is deleted. A `discarded` sample KEEPS its hold — the units were destroyed, so releasing them would return stock that no longer exists to available-to-promise; write them off with a separate inventory adjustment. `quantity` is always in stock units; `unit` is a descriptive note. Retaining more than the lot has available returns 422.","body":{"mode":"raw","raw":"{\n  \"sample_identifier\": \"RS-000412\",\n  \"retained_at\": \"2026-08-18\",\n  \"status\": \"retained\",\n  \"quantity\": 2,\n  \"unit\": \"bottle\",\n  \"storage_location\": \"Cabinet A\",\n  \"retain_until\": null,\n  \"lab_name\": null,\n  \"sent_at\": null,\n  \"tracking_number\": null,\n  \"disposition_notes\": null,\n  \"disposed_at\": null\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/retention-samples","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","retention-samples"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 77,\n    \"fifo_layer_id\": 8842,\n    \"sample_identifier\": \"RS-000412\",\n    \"quantity\": 2,\n    \"unit\": \"bottle\",\n    \"storage_location\": \"Cabinet A\",\n    \"inventory_hold_id\": 9014,\n    \"held_quantity\": 2,\n    \"retained_at\": \"2026-08-18\",\n    \"retain_until\": \"2031-02-14\",\n    \"retain_until_overridden\": false,\n    \"is_due_for_disposal\": false,\n    \"status\": \"retained\",\n    \"status_label\": \"Retained\",\n    \"status_color\": \"success\",\n    \"lab_name\": null,\n    \"sent_at\": null,\n    \"tracking_number\": null,\n    \"disposition_notes\": null,\n    \"disposed_at\": null,\n    \"created_by\": \"Dana Reyes\",\n    \"batch_number\": \"B240817\",\n    \"product_name\": \"Ashwagandha 500mg\",\n    \"created_at\": \"2026-08-18T14:40:00.000000Z\"\n  }\n}"}]},{"name":"Update Lot Retention Sample","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/retention-samples/{sample}","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","retention-samples",":sample"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."},{"key":"sample","value":"","description":"The sample ID."}]},"description":"Update a reserve sample or advance its status through retained, sent to lab, returned and discarded.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Stock effect.** A reserve sample takes its units off the shelf: creating one places an inventory hold for `quantity`, so those units leave available-to-sell. `inventory_hold_id` and `held_quantity` report it. The hold is released when the sample's `status` becomes `returned`, and when the sample is deleted. A `discarded` sample KEEPS its hold — the units were destroyed, so releasing them would return stock that no longer exists to available-to-promise; write them off with a separate inventory adjustment. `quantity` is always in stock units; `unit` is a descriptive note. Retaining more than the lot has available returns 422.","body":{"mode":"raw","raw":"{\n  \"sample_identifier\": \"RS-000412\",\n  \"retained_at\": \"2026-08-18\",\n  \"status\": \"sent_to_lab\",\n  \"lab_name\": \"Certified Laboratories\",\n  \"sent_at\": \"2026-09-01\",\n  \"tracking_number\": \"1Z999AA10123456784\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/retention-samples/{sample}","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","retention-samples",":sample"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."},{"key":"sample","value":"","description":"The sample ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 77,\n  \"fifo_layer_id\": 8842,\n  \"sample_identifier\": \"RS-000412\",\n  \"quantity\": 2,\n  \"unit\": \"bottle\",\n  \"storage_location\": \"Cabinet A\",\n  \"inventory_hold_id\": 9014,\n  \"held_quantity\": 2,\n  \"retained_at\": \"2026-08-18\",\n  \"retain_until\": \"2031-02-14\",\n  \"retain_until_overridden\": false,\n  \"is_due_for_disposal\": false,\n  \"status\": \"sent_to_lab\",\n  \"status_label\": \"Sent to Lab\",\n  \"status_color\": \"warning\",\n  \"lab_name\": \"Certified Laboratories\",\n  \"sent_at\": \"2026-09-01\",\n  \"tracking_number\": \"1Z999AA10123456784\",\n  \"disposition_notes\": null,\n  \"disposed_at\": null,\n  \"created_by\": \"Dana Reyes\",\n  \"batch_number\": \"B240817\",\n  \"product_name\": \"Ashwagandha 500mg\",\n  \"created_at\": \"2026-08-18T14:40:00.000000Z\"\n}"}]},{"name":"Delete Lot Retention Sample","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/retention-samples/{sample}","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","retention-samples",":sample"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."},{"key":"sample","value":"","description":"The sample ID."}]},"description":"Remove a retention sample record. The physical sample is unaffected.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/fifo-layers/{fifoLayer}/retention-samples/{sample}","host":["{{baseUrl}}"],"path":["api","v2","fifo-layers",":fifoLayer","retention-samples",":sample"],"variable":[{"key":"fifoLayer","value":"","description":"The fifo layer ID."},{"key":"sample","value":"","description":"The sample ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Retention sample removed.\"\n}"}]},{"name":"List Retention Samples","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/retention-samples","host":["{{baseUrl}}"],"path":["api","v2","retention-samples"]},"description":"Reserve samples across every lot, with a filter for samples past their retention date and still physically held.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Stock effect.** A reserve sample takes its units off the shelf: creating one places an inventory hold for `quantity`, so those units leave available-to-sell. `inventory_hold_id` and `held_quantity` report it. The hold is released when the sample's `status` becomes `returned`, and when the sample is deleted. A `discarded` sample KEEPS its hold — the units were destroyed, so releasing them would return stock that no longer exists to available-to-promise; write them off with a separate inventory adjustment. `quantity` is always in stock units; `unit` is a descriptive note. Retaining more than the lot has available returns 422.\n\nPermission: `inventory.index`. Users without it receive 403. Personal access tokens need the `inventory:read` scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/retention-samples","host":["{{baseUrl}}"],"path":["api","v2","retention-samples"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 77,\n      \"fifo_layer_id\": 8842,\n      \"sample_identifier\": \"RS-000412\",\n      \"quantity\": 2,\n      \"unit\": \"bottle\",\n      \"storage_location\": \"Cabinet A\",\n      \"inventory_hold_id\": 9014,\n      \"held_quantity\": 2,\n      \"retained_at\": \"2026-08-18\",\n      \"retain_until\": \"2031-02-14\",\n      \"retain_until_overridden\": false,\n      \"is_due_for_disposal\": false,\n      \"status\": \"retained\",\n      \"status_label\": \"Retained\",\n      \"status_color\": \"success\",\n      \"lab_name\": null,\n      \"sent_at\": null,\n      \"tracking_number\": null,\n      \"disposition_notes\": null,\n      \"disposed_at\": null,\n      \"created_by\": \"Dana Reyes\",\n      \"batch_number\": \"B240817\",\n      \"product_name\": \"Ashwagandha 500mg\",\n      \"created_at\": \"2026-08-18T14:40:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"List Compliance Documents","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/compliance-documents","host":["{{baseUrl}}"],"path":["api","v2","compliance-documents"]},"description":"Every compliance document across all lots and suppliers — Certificates of Analysis, batch records, production photos and supplier certificates.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBatch number is the primary search key: plain search reaches it through each document's linked lots. Each row carries the lots the document covers, so one certificate covering three lots of a batch appears once rather than three times.\n\nOnly typed documents appear here; untyped attachments on other records are excluded.\n\nPermission: `inventory.index`. Users without it receive 403. Personal access tokens need the `inventory:read` scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/compliance-documents","host":["{{baseUrl}}"],"path":["api","v2","compliance-documents"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 412,\n      \"file_name\": \"coa-B240817.pdf\",\n      \"mime_type\": \"application/pdf\",\n      \"is_image\": false,\n      \"file_size\": 184320,\n      \"source\": \"lot_compliance\",\n      \"description\": \"Finished-product COA for batch B240817\",\n      \"document_type\": \"coa\",\n      \"document_type_label\": \"Certificate of Analysis\",\n      \"document_type_color\": \"primary\",\n      \"issued_at\": \"2026-08-01\",\n      \"expires_at\": null,\n      \"expiry_state\": null,\n      \"issuer\": \"Eurofins\",\n      \"reference\": \"L-99213\",\n      \"file_url\": \"https://acme.sku.io/api/v2/compliance-documents/412/file\",\n      \"uploaded_by\": \"Dana Reyes\",\n      \"linked_records\": [\n        {\n          \"type\": \"App\\\\Models\\\\FifoLayer\",\n          \"id\": 8842\n        },\n        {\n          \"type\": \"App\\\\Models\\\\FifoLayer\",\n          \"id\": 8851\n        }\n      ],\n      \"created_at\": \"2026-08-18T14:22:11.000000Z\",\n      \"lots\": [\n        {\n          \"id\": 8842,\n          \"batch_number\": \"B240817\",\n          \"product_id\": 55,\n          \"warehouse_id\": 1,\n          \"expiry_date\": \"2030-02-14\"\n        },\n        {\n          \"id\": 8851,\n          \"batch_number\": \"B240817\",\n          \"product_id\": 55,\n          \"warehouse_id\": 2,\n          \"expiry_date\": \"2030-02-14\"\n        }\n      ],\n      \"lot_count\": 2\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"List Compliance Document Types","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/compliance-documents/types","host":["{{baseUrl}}"],"path":["api","v2","compliance-documents","types"]},"description":"Every document type with its display label and colour, for building a type filter or upload picker.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nPermission: `inventory.index`. Users without it receive 403. Personal access tokens need the `inventory:read` scope."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/compliance-documents/types","host":["{{baseUrl}}"],"path":["api","v2","compliance-documents","types"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"value\": \"coa\",\n      \"label\": \"Certificate of Analysis\",\n      \"color\": \"primary\"\n    },\n    {\n      \"value\": \"batch_record\",\n      \"label\": \"Batch Record\",\n      \"color\": \"indigo\"\n    },\n    {\n      \"value\": \"spec_sheet\",\n      \"label\": \"Specification Sheet\",\n      \"color\": \"teal\"\n    },\n    {\n      \"value\": \"lab_report\",\n      \"label\": \"Lab Report\",\n      \"color\": \"indigo\"\n    },\n    {\n      \"value\": \"batch_photo\",\n      \"label\": \"Batch Photo\",\n      \"color\": \"grey\"\n    },\n    {\n      \"value\": \"other\",\n      \"label\": \"Other\",\n      \"color\": \"grey\"\n    },\n    {\n      \"value\": \"nsf_cert\",\n      \"label\": \"NSF Certificate\",\n      \"color\": \"info\"\n    },\n    {\n      \"value\": \"gmp_cert\",\n      \"label\": \"GMP Certificate\",\n      \"color\": \"info\"\n    },\n    {\n      \"value\": \"tic_doc\",\n      \"label\": \"TIC Documentation\",\n      \"color\": \"info\"\n    },\n    {\n      \"value\": \"label_version\",\n      \"label\": \"Label Version\",\n      \"color\": \"teal\"\n    },\n    {\n      \"value\": \"insurance\",\n      \"label\": \"Insurance Certificate\",\n      \"color\": \"info\"\n    },\n    {\n      \"value\": \"audit_report\",\n      \"label\": \"Audit Report\",\n      \"color\": \"info\"\n    }\n  ]\n}"}]},{"name":"Download Compliance Document","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/compliance-documents/{document}/file","host":["{{baseUrl}}"],"path":["api","v2","compliance-documents",":document","file"],"variable":[{"key":"document","value":"","description":"The document ID."}]},"description":"Stream a lot-linked compliance document.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the stored file with its original content type, not JSON. Restricted to documents attached to a lot; supplier certificates stream from the supplier route instead.\n\nPermission: `inventory.index`. Users without it receive 403. Personal access tokens need the `inventory:read` scope."}},{"name":"List Product Specifications","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/specifications","host":["{{baseUrl}}"],"path":["api","v2","products",":product","specifications"],"variable":[{"key":"product","value":"","description":"The product ID."}]},"description":"The quality standard a product's lots are judged against — one row per analyte.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA row is either numeric (a minimum and/or maximum) or qualitative (an expected result). A row with a supplier overrides the product-wide row of the same name for lots from that supplier.\n\n`evaluated_lot_count` reports how many lots already carry verdicts reached under the current standard; those verdicts are snapshotted and are not re-judged when the standard changes."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/specifications","host":["{{baseUrl}}"],"path":["api","v2","products",":product","specifications"],"variable":[{"key":"product","value":"","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"specifications\": [\n      {\n        \"id\": 32,\n        \"product_id\": 55,\n        \"supplier_id\": null,\n        \"supplier_name\": null,\n        \"name\": \"Lead\",\n        \"method\": \"ICP-MS\",\n        \"unit\": \"ppm\",\n        \"min_value\": null,\n        \"max_value\": 0.5,\n        \"target_value\": null,\n        \"expected_text\": null,\n        \"is_qualitative\": false,\n        \"is_required\": true,\n        \"sort_order\": 1,\n        \"created_at\": \"2026-08-10T09:00:00.000000Z\",\n        \"updated_at\": \"2026-08-10T09:00:00.000000Z\"\n      }\n    ],\n    \"evaluated_lot_count\": 12\n  }\n}"}]},{"name":"Create Product Specification","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/specifications","host":["{{baseUrl}}"],"path":["api","v2","products",":product","specifications"],"variable":[{"key":"product","value":"","description":"The product ID."}]},"description":"Add an analyte to a product's quality specification.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nGive the line a bound: a minimum, a maximum, or an expected result. A line with no bound can never produce a verdict and is rejected.\n\nSet `supplier_id` to override the product-wide standard for one supplier only.","body":{"mode":"raw","raw":"{\n  \"name\": \"Lead\",\n  \"supplier_id\": null,\n  \"method\": \"ICP-MS\",\n  \"unit\": \"ppm\",\n  \"min_value\": null,\n  \"max_value\": 0.5,\n  \"target_value\": null,\n  \"expected_text\": null,\n  \"is_required\": true,\n  \"sort_order\": 1\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/specifications","host":["{{baseUrl}}"],"path":["api","v2","products",":product","specifications"],"variable":[{"key":"product","value":"","description":"The product ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 32,\n    \"product_id\": 55,\n    \"supplier_id\": null,\n    \"supplier_name\": null,\n    \"name\": \"Lead\",\n    \"method\": \"ICP-MS\",\n    \"unit\": \"ppm\",\n    \"min_value\": null,\n    \"max_value\": 0.5,\n    \"target_value\": null,\n    \"expected_text\": null,\n    \"is_qualitative\": false,\n    \"is_required\": true,\n    \"sort_order\": 1,\n    \"created_at\": \"2026-08-10T09:00:00.000000Z\",\n    \"updated_at\": \"2026-08-10T09:00:00.000000Z\"\n  }\n}"}]},{"name":"Update Product Specification","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/specifications/{specification}","host":["{{baseUrl}}"],"path":["api","v2","products",":product","specifications",":specification"],"variable":[{"key":"product","value":"","description":"The product ID."},{"key":"specification","value":"","description":"The specification ID."}]},"description":"Update one analyte of a product's quality specification.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nLots already evaluated keep the verdict they were given: the standard in force at the time is stored with each result, so tightening a limit does not silently re-judge history.","body":{"mode":"raw","raw":"{\n  \"name\": \"Lead\",\n  \"method\": \"ICP-MS\",\n  \"unit\": \"ppm\",\n  \"max_value\": 0.3,\n  \"is_required\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/specifications/{specification}","host":["{{baseUrl}}"],"path":["api","v2","products",":product","specifications",":specification"],"variable":[{"key":"product","value":"","description":"The product ID."},{"key":"specification","value":"","description":"The specification ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 32,\n  \"product_id\": 55,\n  \"supplier_id\": null,\n  \"supplier_name\": null,\n  \"name\": \"Lead\",\n  \"method\": \"ICP-MS\",\n  \"unit\": \"ppm\",\n  \"min_value\": null,\n  \"max_value\": 0.3,\n  \"target_value\": null,\n  \"expected_text\": null,\n  \"is_qualitative\": false,\n  \"is_required\": true,\n  \"sort_order\": 1,\n  \"created_at\": \"2026-08-10T09:00:00.000000Z\",\n  \"updated_at\": \"2026-08-10T09:00:00.000000Z\"\n}"}]},{"name":"Delete Product Specification","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/v2/products/{product}/specifications/{specification}","host":["{{baseUrl}}"],"path":["api","v2","products",":product","specifications",":specification"],"variable":[{"key":"product","value":"","description":"The product ID."},{"key":"specification","value":"","description":"The specification ID."}]},"description":"Remove an analyte from a product's quality specification. Results already recorded against it are kept as history.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/v2/products/{product}/specifications/{specification}","host":["{{baseUrl}}"],"path":["api","v2","products",":product","specifications",":specification"],"variable":[{"key":"product","value":"","description":"The product ID."},{"key":"specification","value":"","description":"The specification ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Specification removed.\"\n}"}]}]},{"name":"Cost Change Inbox","item":[{"name":"List Cost Changes","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-changes","host":["{{baseUrl}}"],"path":["api","cost-changes"]},"description":"Lists cost change sets: every retroactive cost change that was staged for review or resolved, one row per triggering document event. A set carries the trigger (the bill, invoice or purchase order edit that fired it), the originating purchase order, the receipt date span of the stock it touches, the total delta split into still-on-hand and already-consumed, and the flags that shaped its routing.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`restate_blocked_reason` is a sentence, or null, on every row. When set, restating this change is refused everywhere - preview, apply, switch and bulk resolve - because one or more of its original receipt months sit behind the accounting lock date. Only going forward is available for such a set.\n\nAuthentication: Requires Bearer token (inventory scope).\n\nFilters:\n- filter[status]: exact status - pending_review, applied, dismissed, superseded\n- filter[statuses]: comma-separated list of statuses\n- filter[trigger_type]: exact trigger - landed_cost_bill, purchase_invoice_new, purchase_invoice_edited, po_line_edited, po_financials_edited, po_currency_changed, data_fix\n- filter[trigger_types]: comma-separated list of triggers\n- filter[resolution]: exact resolution - restate, prospective\n- filter[applied_via]: how an applied set was resolved - policy, manual, backfill\n- filter[flags]: comma-separated; any of locked_period, closed_period, synced_entries (rows matching ANY listed flag)\n- filter[product_id]: sets with at least one line for this product\n- filter[warehouse_id]: sets with at least one line in this warehouse\n- filter[date_from] / filter[date_to]: staged on or after / on or before this day (Y-m-d)\n- filter[period]: sets with a receipt in this month (YYYY-MM)\n- filter[search]: matches the set id exactly, or partial match on the set uid, trigger document type and notes\n\nSorting: `sort` with an optional leading `-` for descending. Allowed sorts: id, created_at, updated_at, total_delta, affected_layers_count, affected_products_count, earliest_affected_period, resolved_at, trigger_document_date. Default: -created_at.\n\nPagination: `page` and `per_page` (default 10)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-changes","host":["{{baseUrl}}"],"path":["api","cost-changes"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 42,\n      \"uid\": \"9f1c2b6e-7d4a-4f2e-9b1a-3c5d8e7f6a21\",\n      \"trigger_type\": \"landed_cost_bill\",\n      \"trigger_type_label\": \"Landed Cost Bill\",\n      \"trigger\": {\n        \"type\": \"App\\\\Models\\\\Bill\",\n        \"type_label\": \"Bill\",\n        \"id\": 318,\n        \"reference\": \"FRT-2026-0142\",\n        \"date\": \"2026-07-28\",\n        \"entered_at\": \"2026-08-04\",\n        \"cost_lines\": [\n          {\n            \"id\": 910,\n            \"description\": \"Ocean freight - container MSKU7781\",\n            \"amount\": 823.4,\n            \"entered_at\": \"2026-08-04\"\n          }\n        ],\n        \"cost_lines_total\": 823.4,\n        \"route\": \"/purchases/bills/318\"\n      },\n      \"status\": \"pending_review\",\n      \"resolution\": null,\n      \"entered_date\": \"2026-08-04\",\n      \"prospective_anchor\": null,\n      \"prospective_anchor_date\": null,\n      \"applied_via\": null,\n      \"policy_rule_applied\": \"landed_cost_bill:material\",\n      \"totals\": {\n        \"delta\": 823.4,\n        \"on_hand_delta\": 512.75,\n        \"consumed_delta\": 310.65,\n        \"products\": 2,\n        \"layers\": 3\n      },\n      \"earliest_affected_period\": \"2026-05\",\n      \"receipts\": {\n        \"earliest\": \"2026-05-12\",\n        \"latest\": \"2026-06-03\"\n      },\n      \"origin\": {\n        \"type_label\": \"Purchase Order\",\n        \"id\": 1187,\n        \"reference\": \"PO-001187\",\n        \"date\": \"2026-04-30\",\n        \"route\": \"/orders/purchase-orders/1187\"\n      },\n      \"restate_blocked_reason\": null,\n      \"flags\": {\n        \"locked_period\": false,\n        \"closed_period\": true,\n        \"synced_entries\": false,\n        \"ledger_held\": true,\n        \"backfilled\": false,\n        \"cascaded\": false\n      },\n      \"notes\": null,\n      \"resolved_by\": null,\n      \"resolved_at\": null,\n      \"restaged_at\": null,\n      \"preview_computed_at\": \"2026-08-04T14:22:11+00:00\",\n      \"created_at\": \"2026-08-04T14:22:09+00:00\",\n      \"updated_at\": \"2026-08-04T14:22:11+00:00\"\n    },\n    {\n      \"id\": 43,\n      \"uid\": \"1a7e4d90-2b3c-4e5f-8a9b-0c1d2e3f4a55\",\n      \"trigger_type\": \"purchase_invoice_new\",\n      \"trigger_type_label\": \"Invoice After Receipt\",\n      \"trigger\": {\n        \"type\": \"App\\\\Models\\\\PurchaseInvoice\",\n        \"type_label\": \"Purchase Invoice\",\n        \"id\": 2244,\n        \"reference\": \"INV-88410\",\n        \"date\": \"2026-06-20\",\n        \"entered_at\": \"2026-08-06\",\n        \"cost_lines\": [\n          {\n            \"id\": 5120,\n            \"description\": \"Blue Widget x 400\",\n            \"amount\": 5240,\n            \"entered_at\": \"2026-08-06\"\n          }\n        ],\n        \"cost_lines_total\": 5240,\n        \"route\": \"/orders/purchase-invoices/2244\"\n      },\n      \"status\": \"pending_review\",\n      \"resolution\": null,\n      \"entered_date\": \"2026-08-04\",\n      \"prospective_anchor\": null,\n      \"prospective_anchor_date\": null,\n      \"applied_via\": null,\n      \"policy_rule_applied\": \"guardrail:locked_period\",\n      \"totals\": {\n        \"delta\": 240,\n        \"on_hand_delta\": 240,\n        \"consumed_delta\": 0,\n        \"products\": 1,\n        \"layers\": 1\n      },\n      \"earliest_affected_period\": \"2026-05\",\n      \"receipts\": {\n        \"earliest\": \"2026-05-28\",\n        \"latest\": \"2026-05-28\"\n      },\n      \"origin\": {\n        \"type_label\": \"Purchase Order\",\n        \"id\": 1187,\n        \"reference\": \"PO-001187\",\n        \"date\": \"2026-04-30\",\n        \"route\": \"/orders/purchase-orders/1187\"\n      },\n      \"restate_blocked_reason\": \"Restate would re-report May 2026 and Jun 2026, which are locked (accounting lock date 2026-06-30). The ledger would displace it to 2026-07-01, which is not a restatement. Apply it going forward, or lift the lock first.\",\n      \"flags\": {\n        \"locked_period\": true,\n        \"closed_period\": true,\n        \"synced_entries\": false,\n        \"ledger_held\": true,\n        \"backfilled\": false,\n        \"cascaded\": false\n      },\n      \"notes\": null,\n      \"resolved_by\": null,\n      \"resolved_at\": null,\n      \"restaged_at\": null,\n      \"preview_computed_at\": \"2026-08-06T08:01:40+00:00\",\n      \"created_at\": \"2026-08-06T08:01:38+00:00\",\n      \"updated_at\": \"2026-08-06T08:01:40+00:00\"\n    }\n  ],\n  \"first_page_url\": \"https://{{domain}}/api/cost-changes?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://{{domain}}/api/cost-changes?page=1\",\n  \"links\": [\n    {\n      \"url\": null,\n      \"label\": \"&laquo; Previous\",\n      \"active\": false\n    },\n    {\n      \"url\": \"https://{{domain}}/api/cost-changes?page=1\",\n      \"label\": \"1\",\n      \"active\": true\n    },\n    {\n      \"url\": null,\n      \"label\": \"Next &raquo;\",\n      \"active\": false\n    }\n  ],\n  \"next_page_url\": null,\n  \"path\": \"https://{{domain}}/api/cost-changes\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 2,\n  \"total\": 2\n}"}]},{"name":"Get Cost Change Summary","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-changes/summary","host":["{{baseUrl}}"],"path":["api","cost-changes","summary"]},"description":"Returns the inbox headline figures: how many cost changes await review, the sum of their deltas, how many of them touch a closed or locked accounting period, and how many ledger entries are being held back until a decision is made (one per pending set - the triggering document's own journal entry does not generate until its cost change is resolved).\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (inventory scope)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-changes/summary","host":["{{baseUrl}}"],"path":["api","cost-changes","summary"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"pending_count\": 4,\n    \"pending_delta\": 2316.8,\n    \"closed_or_locked_count\": 2,\n    \"held_entries_count\": 4\n  }\n}"}]},{"name":"Get Cost Change","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange"],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]},"description":"Returns one cost change set with its staged lines (one per FIFO layer: unit cost before and after, units on hand and already consumed at staging time, the original receipt date), its decision history, the restate-versus-prospective impact comparison (`impact`, the same payload as Get Impact Preview) and `what_changed` - every cost document on the originating purchase order, dated both by document date and by the day it was entered, so the delta can be read against its sources. `what_changed` is null when the trigger cannot be traced back to a purchase order.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`restate_blocked_reason` is null when restate is a real option, or the sentence explaining why it is refused when the original receipt months are behind the accounting lock date.\n\nAuthentication: Requires Bearer token (inventory scope).\n\nPath params:\n- costChange: Cost change set ID\n\nEach cost change also reports what it MOVED, by cost category. `composition_movement` on the change is the roll-up across its lines, and each line carries `composition_before`, `composition_after` and its own `composition_movement`. The trigger names the document that set the recalculation off, which is not always the document that caused the money to move — re-costing a purchase order picks up any charge on it that had not yet been capitalised. Comparing the movement's categories against the charges the trigger document actually carries is what tells the two apart. Empty on changes recorded before the composition was captured.\n\n`warnings[]` — `{code, message}` entries surfaced on the detail view. `overlaps_vendor_credit` means an authorized vendor credit already reduced the cost of goods this purchase order line or invoice edit is reducing again — a likely double count. It carries `vendor_credit_id`, `vendor_credit_number`, `vendor_credit_line_id` and `purchase_order_line_id`, and is only raised for edits dated on or after the credit; it warns and never blocks.\n\n`recorded` is null until the change is applied. For an APPLIED change `impact` reports what was actually booked rather than a re-run of the preview against today's layers: `recorded` is `{resolution, on_hand, consumed, total}` (the resolution that was applied, the amount booked to Inventory for units still on hand, the amount booked to cost of goods sold for units already consumed, and their sum), `periods` carries those booked figures per month on the applied side (`restate_*` or `prospective_*`), `preview_stale` is false and `downstream` is null (the cascade into stock made from these layers already ran on apply). Passing `prospective_date` on an applied change is a what-if and still returns the live preview with `recorded` null."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange"],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"uid\": \"9f1c2b6e-7d4a-4f2e-9b1a-3c5d8e7f6a21\",\n    \"trigger_type\": \"landed_cost_bill\",\n    \"trigger_type_label\": \"Landed Cost Bill\",\n    \"trigger\": {\n      \"type\": \"App\\\\Models\\\\Bill\",\n      \"type_label\": \"Bill\",\n      \"id\": 318,\n      \"reference\": \"FRT-2026-0142\",\n      \"date\": \"2026-07-28\",\n      \"entered_at\": \"2026-08-04\",\n      \"cost_lines\": [\n        {\n          \"id\": 910,\n          \"description\": \"Ocean freight - container MSKU7781\",\n          \"amount\": 823.4,\n          \"entered_at\": \"2026-08-04\"\n        }\n      ],\n      \"cost_lines_total\": 823.4,\n      \"route\": \"/purchases/bills/318\"\n    },\n    \"status\": \"pending_review\",\n    \"resolution\": null,\n    \"entered_date\": \"2026-08-04\",\n    \"prospective_anchor\": null,\n    \"prospective_anchor_date\": null,\n    \"applied_via\": null,\n    \"policy_rule_applied\": \"landed_cost_bill:material\",\n    \"totals\": {\n      \"delta\": 823.4,\n      \"on_hand_delta\": 512.75,\n      \"consumed_delta\": 310.65,\n      \"products\": 2,\n      \"layers\": 3\n    },\n    \"composition_movement\": {\n      \"Inbound Freight\": 823.4\n    },\n    \"earliest_affected_period\": \"2026-05\",\n    \"receipts\": {\n      \"earliest\": \"2026-05-12\",\n      \"latest\": \"2026-06-03\"\n    },\n    \"origin\": {\n      \"type_label\": \"Purchase Order\",\n      \"id\": 1187,\n      \"reference\": \"PO-001187\",\n      \"date\": \"2026-04-30\",\n      \"route\": \"/orders/purchase-orders/1187\"\n    },\n    \"restate_blocked_reason\": null,\n    \"flags\": {\n      \"locked_period\": false,\n      \"closed_period\": true,\n      \"synced_entries\": false,\n      \"ledger_held\": true,\n      \"backfilled\": false,\n      \"cascaded\": false\n    },\n    \"notes\": null,\n    \"resolved_by\": null,\n    \"resolved_at\": null,\n    \"restaged_at\": null,\n    \"preview_computed_at\": \"2026-08-04T14:22:11+00:00\",\n    \"created_at\": \"2026-08-04T14:22:09+00:00\",\n    \"updated_at\": \"2026-08-04T14:22:11+00:00\",\n    \"lines\": [\n      {\n        \"id\": 301,\n        \"fifo_layer_id\": 88412,\n        \"fifo_layer_route\": \"/inventory/fifo-layers/88412\",\n        \"product\": {\n          \"id\": 512,\n          \"sku\": \"WIDGET-001\",\n          \"name\": \"Blue Widget\",\n          \"route\": \"/products/512\"\n        },\n        \"warehouse\": \"Main Warehouse\",\n        \"old_unit_cost\": 12.5,\n        \"new_unit_cost\": 13.15,\n        \"old_total_cost\": 3000,\n        \"new_total_cost\": 3156,\n        \"delta_total\": 156,\n        \"units_on_hand\": 150,\n        \"units_consumed\": 90,\n        \"original_event_date\": \"2026-05-12\",\n        \"restate_clamped\": false,\n        \"inventory_revaluation_id\": null,\n        \"composition_before\": {\n          \"Base Product\": 3000\n        },\n        \"composition_after\": {\n          \"Base Product\": 3000,\n          \"Inbound Freight\": 156\n        },\n        \"composition_movement\": {\n          \"Inbound Freight\": 156\n        }\n      },\n      {\n        \"id\": 302,\n        \"fifo_layer_id\": 88413,\n        \"fifo_layer_route\": \"/inventory/fifo-layers/88413\",\n        \"product\": {\n          \"id\": 512,\n          \"sku\": \"WIDGET-001\",\n          \"name\": \"Blue Widget\",\n          \"route\": \"/products/512\"\n        },\n        \"warehouse\": \"Main Warehouse\",\n        \"old_unit_cost\": 12.5,\n        \"new_unit_cost\": 13.15,\n        \"old_total_cost\": 5000,\n        \"new_total_cost\": 5260,\n        \"delta_total\": 260,\n        \"units_on_hand\": 400,\n        \"units_consumed\": 0,\n        \"original_event_date\": \"2026-05-28\",\n        \"restate_clamped\": false,\n        \"inventory_revaluation_id\": null,\n        \"composition_before\": {\n          \"Base Product\": 3000\n        },\n        \"composition_after\": {\n          \"Base Product\": 3000,\n          \"Inbound Freight\": 156\n        },\n        \"composition_movement\": {\n          \"Inbound Freight\": 156\n        }\n      },\n      {\n        \"id\": 303,\n        \"fifo_layer_id\": 88601,\n        \"fifo_layer_route\": \"/inventory/fifo-layers/88601\",\n        \"product\": {\n          \"id\": 640,\n          \"sku\": \"GADGET-XL\",\n          \"name\": \"XL Gadget\",\n          \"route\": \"/products/640\"\n        },\n        \"warehouse\": \"Main Warehouse\",\n        \"old_unit_cost\": 45,\n        \"new_unit_cost\": 46.6975,\n        \"old_total_cost\": 10800,\n        \"new_total_cost\": 11207.4,\n        \"delta_total\": 407.4,\n        \"units_on_hand\": 60,\n        \"units_consumed\": 180,\n        \"original_event_date\": \"2026-06-03\",\n        \"restate_clamped\": false,\n        \"inventory_revaluation_id\": null,\n        \"composition_before\": {\n          \"Base Product\": 3000\n        },\n        \"composition_after\": {\n          \"Base Product\": 3000,\n          \"Inbound Freight\": 156\n        },\n        \"composition_movement\": {\n          \"Inbound Freight\": 156\n        }\n      }\n    ],\n    \"resolutions\": [],\n    \"impact\": {\n      \"periods\": [\n        {\n          \"period\": \"2026-05\",\n          \"restate_valuation_impact\": 416,\n          \"restate_cogs_impact\": 0,\n          \"prospective_impact\": 0,\n          \"prospective_valuation_impact\": 0,\n          \"prospective_cogs_impact\": 0,\n          \"locked\": false,\n          \"closed\": true,\n          \"clamped_to\": null\n        },\n        {\n          \"period\": \"2026-06\",\n          \"restate_valuation_impact\": 407.4,\n          \"restate_cogs_impact\": 0,\n          \"prospective_impact\": 0,\n          \"prospective_valuation_impact\": 0,\n          \"prospective_cogs_impact\": 0,\n          \"locked\": false,\n          \"closed\": true,\n          \"clamped_to\": null\n        },\n        {\n          \"period\": \"2026-07\",\n          \"restate_valuation_impact\": -310.65,\n          \"restate_cogs_impact\": 310.65,\n          \"prospective_impact\": 0,\n          \"prospective_valuation_impact\": 0,\n          \"prospective_cogs_impact\": 0,\n          \"locked\": false,\n          \"closed\": false,\n          \"clamped_to\": null\n        },\n        {\n          \"period\": \"2026-08\",\n          \"restate_valuation_impact\": 0,\n          \"restate_cogs_impact\": 0,\n          \"prospective_impact\": 823.4,\n          \"prospective_valuation_impact\": 512.75,\n          \"prospective_cogs_impact\": 310.65,\n          \"locked\": false,\n          \"closed\": false,\n          \"clamped_to\": null\n        }\n      ],\n      \"prospective_period\": \"2026-08\",\n      \"prospective_anchor_options\": {\n        \"entered\": {\n          \"date\": \"2026-08-04\"\n        },\n        \"document\": {\n          \"date\": \"2026-07-28\",\n          \"eligible\": true,\n          \"reason\": null\n        },\n        \"custom\": {\n          \"min_date\": \"2026-07-01\",\n          \"max_date\": \"2026-09-03\",\n          \"lock_date\": \"2026-06-30\",\n          \"in_closed_period\": false\n        }\n      },\n      \"blocked_by\": [],\n      \"restate_blocked_reason\": null,\n      \"preview_stale\": false,\n      \"downstream\": {\n        \"supported\": true,\n        \"levels\": 1,\n        \"rows\": [\n          {\n            \"fifo_layer_id\": 88950,\n            \"product_id\": 701,\n            \"level\": 1,\n            \"via\": \"Assembly #10\",\n            \"old_total_cost\": 4200,\n            \"new_total_cost\": 4286,\n            \"delta\": 86,\n            \"caused_by_this_change\": 4,\n            \"pre_existing_drift\": 82\n          }\n        ],\n        \"totals\": {\n          \"layers\": 1,\n          \"delta\": 86,\n          \"caused_by_this_change\": 4,\n          \"pre_existing_drift\": 82\n        },\n        \"error\": null\n      },\n      \"recorded\": null\n    },\n    \"what_changed\": {\n      \"documents\": [\n        {\n          \"type_label\": \"Purchase Order\",\n          \"id\": 1187,\n          \"reference\": \"PO-001187\",\n          \"date\": \"2026-04-30\",\n          \"entered_at\": \"2026-04-30\",\n          \"amount\": 18800,\n          \"route\": \"/orders/purchase-orders/1187\",\n          \"is_trigger\": false\n        },\n        {\n          \"type_label\": \"Purchase Invoice\",\n          \"id\": 2210,\n          \"reference\": \"INV-88231\",\n          \"date\": \"2026-05-15\",\n          \"entered_at\": \"2026-05-16\",\n          \"amount\": 18800,\n          \"route\": \"/orders/purchase-invoices/2210\",\n          \"is_trigger\": false\n        },\n        {\n          \"type_label\": \"Bill\",\n          \"id\": 318,\n          \"reference\": \"FRT-2026-0142\",\n          \"date\": \"2026-07-28\",\n          \"entered_at\": \"2026-08-04\",\n          \"amount\": 823.4,\n          \"route\": \"/purchases/bills/318\",\n          \"is_trigger\": true\n        }\n      ],\n      \"documents_total\": 38423.4,\n      \"delta\": 823.4,\n      \"unattributed\": 37600,\n      \"note\": \"The change re-derives each receipt's landed cost from every cost document on the order, not just the one that triggered it. Cost the receipts already carried, or that belongs to receipts outside this change, accounts for the difference.\"\n    },\n    \"warnings\": [\n      {\n        \"code\": \"overlaps_vendor_credit\",\n        \"message\": \"VC-00184 already reduced this line's cost by $4.13/unit.\",\n        \"vendor_credit_id\": 184,\n        \"vendor_credit_number\": \"VC-00184\",\n        \"vendor_credit_line_id\": 901,\n        \"purchase_order_line_id\": 733,\n        \"route\": \"/purchases/vendor-credits/184\"\n      }\n    ]\n  }\n}"}]},{"name":"Get Impact Preview","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}/impact-preview","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange","impact-preview"],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]},"description":"Returns the side-by-side comparison a reviewer decides from. Each row in `periods` is one accounting month with what each convention books there: restating puts the delta back where the stock was received (`restate_valuation_impact` on Inventory for every unit received, then each unit already sold moves from Inventory to cost of goods in the month it shipped, as `restate_cogs_impact`), while going forward books the whole delta as one dated catch-up (`prospective_impact`, split into `prospective_valuation_impact` and `prospective_cogs_impact`). Both sides add back to the set's total delta. `locked` and `closed` mark the period's accounting status; `clamped_to` is the first open day a locked period's write would be displaced to.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`prospective_anchor_options` lists the days a going-forward application can anchor on: the day the change was entered, the trigger document's own date (with `eligible` and a `reason` when it is not usable), and a custom window (`min_date`, `max_date`, `lock_date`, `in_closed_period`).\n\n`restate_blocked_reason` is null when restate is a real option. When it is set, restate is refused for this change everywhere - here, on apply, on switch and in bulk resolve - because one or more original receipt months are behind the accounting lock date; the sentence explains which months and where the ledger would otherwise displace the write.\n\n`blocked_by` lists older pending sets that must be resolved first because they touch the same FIFO layers. `preview_stale` is true when another change has moved a layer's base cost since this set was staged. `downstream` previews stock built or transferred from the affected layers (kits, transfers) that would be re-costed too, splitting each row into the part this change causes and any pre-existing drift; `supported` is false with an `error` when the bill of materials is recursive.\n\n`recorded` is null until the change is applied. For an APPLIED change the impact reports what was actually booked rather than a re-run of the preview against today's layers: `recorded` is `{resolution, on_hand, consumed, total}` (the resolution that was applied, the amount booked to Inventory for units still on hand, the amount booked to cost of goods sold for units already consumed, and their sum), `periods` carries those booked figures per month on the applied side (`restate_*` or `prospective_*`), `preview_stale` is false and `downstream` is null (the cascade into stock made from these layers already ran on apply). Passing `prospective_date` on an applied change is a what-if and still returns the live preview with `recorded` null.\n\nAuthentication: Requires Bearer token (inventory scope).\n\nPath params:\n- costChange: Cost change set ID\n\nQuery params:\n- prospective_date (optional, Y-m-d): recompute the going-forward side for the day a reviewer is considering. Returns 409 when that day is before the latest original receipt, in the future, or behind the accounting lock."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}/impact-preview","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange","impact-preview"],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"periods\": [\n      {\n        \"period\": \"2026-05\",\n        \"restate_valuation_impact\": 416,\n        \"restate_cogs_impact\": 0,\n        \"prospective_impact\": 0,\n        \"prospective_valuation_impact\": 0,\n        \"prospective_cogs_impact\": 0,\n        \"locked\": false,\n        \"closed\": true,\n        \"clamped_to\": null\n      },\n      {\n        \"period\": \"2026-06\",\n        \"restate_valuation_impact\": 407.4,\n        \"restate_cogs_impact\": 0,\n        \"prospective_impact\": 0,\n        \"prospective_valuation_impact\": 0,\n        \"prospective_cogs_impact\": 0,\n        \"locked\": false,\n        \"closed\": true,\n        \"clamped_to\": null\n      },\n      {\n        \"period\": \"2026-07\",\n        \"restate_valuation_impact\": -310.65,\n        \"restate_cogs_impact\": 310.65,\n        \"prospective_impact\": 0,\n        \"prospective_valuation_impact\": 0,\n        \"prospective_cogs_impact\": 0,\n        \"locked\": false,\n        \"closed\": false,\n        \"clamped_to\": null\n      },\n      {\n        \"period\": \"2026-08\",\n        \"restate_valuation_impact\": 0,\n        \"restate_cogs_impact\": 0,\n        \"prospective_impact\": 823.4,\n        \"prospective_valuation_impact\": 512.75,\n        \"prospective_cogs_impact\": 310.65,\n        \"locked\": false,\n        \"closed\": false,\n        \"clamped_to\": null\n      }\n    ],\n    \"prospective_period\": \"2026-08\",\n    \"prospective_anchor_options\": {\n      \"entered\": {\n        \"date\": \"2026-08-04\"\n      },\n      \"document\": {\n        \"date\": \"2026-07-28\",\n        \"eligible\": true,\n        \"reason\": null\n      },\n      \"custom\": {\n        \"min_date\": \"2026-07-01\",\n        \"max_date\": \"2026-09-03\",\n        \"lock_date\": \"2026-06-30\",\n        \"in_closed_period\": false\n      }\n    },\n    \"blocked_by\": [],\n    \"restate_blocked_reason\": null,\n    \"preview_stale\": false,\n    \"downstream\": {\n      \"supported\": true,\n      \"levels\": 1,\n      \"rows\": [\n        {\n          \"fifo_layer_id\": 88950,\n          \"product_id\": 701,\n          \"level\": 1,\n          \"via\": \"Assembly #10\",\n          \"old_total_cost\": 4200,\n          \"new_total_cost\": 4286,\n          \"delta\": 86,\n          \"caused_by_this_change\": 4,\n          \"pre_existing_drift\": 82\n        }\n      ],\n      \"totals\": {\n        \"layers\": 1,\n        \"delta\": 86,\n        \"caused_by_this_change\": 4,\n        \"pre_existing_drift\": 82\n      },\n      \"error\": null\n    },\n    \"recorded\": null\n  }\n}"}]},{"name":"List Cost Change Usages","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}/usages","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange","usages"],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]},"description":"Returns the usages behind the set's \"already consumed\" figure: for each staged line, the sales orders, assemblies and transfers that drew units from that receipt, the units each took, and what this change does to the cost of goods each carries (`cogs_delta`). Results are keyed by line ID.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nTransfers into a marketplace-run warehouse (Amazon FBA/AWD, Walmart WFS) are flagged `external`: their units left the layer and are trued up as cost of goods, but there is no local destination layer to re-cost. For transfers into Amazon FBA, `destination` follows the units onto the Amazon FIFO layers they created, with those layers' own usages (`kind`: sale, transfer, other) and children.\n\nAuthentication: Requires Bearer token (inventory scope).\n\nPath params:\n- costChange: Cost change set ID"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}/usages","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange","usages"],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"301\": {\n      \"line_id\": 301,\n      \"fifo_layer_id\": 88412,\n      \"units_used\": 90,\n      \"units_external\": 0,\n      \"cogs_delta\": 58.5,\n      \"usages\": [\n        {\n          \"type_label\": \"Sales Order\",\n          \"reference\": \"SO-104512\",\n          \"route\": \"/orders/sales-orders/104512\",\n          \"external\": false,\n          \"movement_id\": 771201,\n          \"date\": \"2026-07-14\",\n          \"units\": 60,\n          \"cogs_delta\": 39,\n          \"destination\": null\n        },\n        {\n          \"type_label\": \"Assembly\",\n          \"reference\": \"#10\",\n          \"route\": \"/inventory/assemblies/10\",\n          \"external\": false,\n          \"movement_id\": 771388,\n          \"date\": \"2026-07-21\",\n          \"units\": 30,\n          \"cogs_delta\": 19.5,\n          \"destination\": null\n        }\n      ]\n    },\n    \"302\": {\n      \"line_id\": 302,\n      \"fifo_layer_id\": 88413,\n      \"units_used\": 0,\n      \"units_external\": 0,\n      \"cogs_delta\": 0,\n      \"usages\": []\n    },\n    \"303\": {\n      \"line_id\": 303,\n      \"fifo_layer_id\": 88601,\n      \"units_used\": 180,\n      \"units_external\": 120,\n      \"cogs_delta\": 305.55,\n      \"usages\": [\n        {\n          \"type_label\": \"Sales Order\",\n          \"reference\": \"SO-104690\",\n          \"route\": \"/orders/sales-orders/104690\",\n          \"external\": false,\n          \"movement_id\": 772010,\n          \"date\": \"2026-07-09\",\n          \"units\": 60,\n          \"cogs_delta\": 101.85,\n          \"destination\": null\n        },\n        {\n          \"type_label\": \"Transfer to Amazon FBA US\",\n          \"reference\": \"WT-000318\",\n          \"route\": \"/inventory/warehouse-transfers/318\",\n          \"external\": true,\n          \"movement_id\": 772455,\n          \"date\": \"2026-07-18\",\n          \"units\": 120,\n          \"cogs_delta\": 203.7,\n          \"destination\": {\n            \"transfer\": {\n              \"id\": 318,\n              \"number\": \"WT-000318\",\n              \"route\": \"/inventory/warehouse-transfers/318\"\n            },\n            \"blend\": {\n              \"units_from_this_layer\": 120,\n              \"line_units\": 120,\n              \"per_unit_delta\": 1.6975\n            },\n            \"layers\": [\n              {\n                \"id\": 9031,\n                \"sku\": \"GADGET-XL\",\n                \"name\": \"XL Gadget\",\n                \"quantity\": 120,\n                \"on_hand\": 75,\n                \"used\": 45,\n                \"cogs_now\": 45,\n                \"cogs_becomes\": 46.6975,\n                \"per_unit_delta\": 1.6975,\n                \"delta_total\": 203.7,\n                \"cogs_source\": \"transfer\",\n                \"derived_from_transfer\": true,\n                \"date\": \"2026-07-22\",\n                \"route\": \"/integrations/amazon/fba/inventory-and-valuation/fifo-layers/9031\",\n                \"usages\": [\n                  {\n                    \"id\": 41877,\n                    \"kind\": \"sale\",\n                    \"event_type\": \"Shipments\",\n                    \"date\": \"2026-08-02\",\n                    \"quantity\": 45,\n                    \"cogs_delta\": 76.39,\n                    \"reference\": \"113-4470921-8811233\",\n                    \"fulfillment_center\": \"ONT8\"\n                  }\n                ],\n                \"children\": []\n              }\n            ]\n          }\n        }\n      ]\n    }\n  }\n}"}]},{"name":"Get Impact Tree","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}/impact-tree","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange","impact-tree"],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]},"description":"Returns one FIFO layer's cost genealogy for the change: what valued it (the purchase order's base cost, then every invoice, bill and correction that reached the line, stepping the unit cost from before to after), where its units are (still on hand, or consumed by sales orders, assemblies and transfers), the layers built from it (kits, with their own usages) and the accounting month each part lands in under either dating (`lands.restate` and `lands.prospective`, each with the period, day, account and period status: open, closed or locked).\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`set` is the cost change with its anchor options and the day the going-forward side is currently anchored on; `layers` is the rail of every staged line ordered by delta; `selected` is the tree for the requested line, defaulting to the line with the largest delta.\n\nAuthentication: Requires Bearer token (inventory scope).\n\nPath params:\n- costChange: Cost change set ID\n\nQuery params:\n- line (optional, integer): the staged line ID to expand; defaults to the line with the largest delta\n- prospective_date (optional, Y-m-d): preview the going-forward landing months for this posting day. Returns 409 when the day is outside the allowed window."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}/impact-tree","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange","impact-tree"],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"set\": {\n      \"id\": 42,\n      \"uid\": \"9f1c2b6e-7d4a-4f2e-9b1a-3c5d8e7f6a21\",\n      \"trigger_type\": \"landed_cost_bill\",\n      \"trigger_type_label\": \"Landed Cost Bill\",\n      \"trigger\": {\n        \"type\": \"App\\\\Models\\\\Bill\",\n        \"type_label\": \"Bill\",\n        \"id\": 318,\n        \"reference\": \"FRT-2026-0142\",\n        \"date\": \"2026-07-28\",\n        \"entered_at\": \"2026-08-04\",\n        \"cost_lines\": [\n          {\n            \"id\": 910,\n            \"description\": \"Ocean freight - container MSKU7781\",\n            \"amount\": 823.4,\n            \"entered_at\": \"2026-08-04\"\n          }\n        ],\n        \"cost_lines_total\": 823.4,\n        \"route\": \"/purchases/bills/318\"\n      },\n      \"status\": \"pending_review\",\n      \"resolution\": null,\n      \"entered_date\": \"2026-08-04\",\n      \"prospective_anchor\": null,\n      \"prospective_anchor_date\": null,\n      \"applied_via\": null,\n      \"policy_rule_applied\": \"landed_cost_bill:material\",\n      \"totals\": {\n        \"delta\": 823.4,\n        \"on_hand_delta\": 512.75,\n        \"consumed_delta\": 310.65,\n        \"products\": 2,\n        \"layers\": 3\n      },\n      \"earliest_affected_period\": \"2026-05\",\n      \"receipts\": {\n        \"earliest\": \"2026-05-12\",\n        \"latest\": \"2026-06-03\"\n      },\n      \"origin\": {\n        \"type_label\": \"Purchase Order\",\n        \"id\": 1187,\n        \"reference\": \"PO-001187\",\n        \"date\": \"2026-04-30\",\n        \"route\": \"/orders/purchase-orders/1187\"\n      },\n      \"restate_blocked_reason\": null,\n      \"flags\": {\n        \"locked_period\": false,\n        \"closed_period\": true,\n        \"synced_entries\": false,\n        \"ledger_held\": true,\n        \"backfilled\": false,\n        \"cascaded\": false\n      },\n      \"notes\": null,\n      \"resolved_by\": null,\n      \"resolved_at\": null,\n      \"restaged_at\": null,\n      \"preview_computed_at\": \"2026-08-04T14:22:11+00:00\",\n      \"created_at\": \"2026-08-04T14:22:09+00:00\",\n      \"updated_at\": \"2026-08-04T14:22:11+00:00\",\n      \"lines\": [\n        {\n          \"id\": 301,\n          \"fifo_layer_id\": 88412,\n          \"fifo_layer_route\": \"/inventory/fifo-layers/88412\",\n          \"product\": {\n            \"id\": 512,\n            \"sku\": \"WIDGET-001\",\n            \"name\": \"Blue Widget\",\n            \"route\": \"/products/512\"\n          },\n          \"warehouse\": \"Main Warehouse\",\n          \"old_unit_cost\": 12.5,\n          \"new_unit_cost\": 13.15,\n          \"old_total_cost\": 3000,\n          \"new_total_cost\": 3156,\n          \"delta_total\": 156,\n          \"units_on_hand\": 150,\n          \"units_consumed\": 90,\n          \"original_event_date\": \"2026-05-12\",\n          \"restate_clamped\": false,\n          \"inventory_revaluation_id\": null\n        },\n        {\n          \"id\": 302,\n          \"fifo_layer_id\": 88413,\n          \"fifo_layer_route\": \"/inventory/fifo-layers/88413\",\n          \"product\": {\n            \"id\": 512,\n            \"sku\": \"WIDGET-001\",\n            \"name\": \"Blue Widget\",\n            \"route\": \"/products/512\"\n          },\n          \"warehouse\": \"Main Warehouse\",\n          \"old_unit_cost\": 12.5,\n          \"new_unit_cost\": 13.15,\n          \"old_total_cost\": 5000,\n          \"new_total_cost\": 5260,\n          \"delta_total\": 260,\n          \"units_on_hand\": 400,\n          \"units_consumed\": 0,\n          \"original_event_date\": \"2026-05-28\",\n          \"restate_clamped\": false,\n          \"inventory_revaluation_id\": null\n        },\n        {\n          \"id\": 303,\n          \"fifo_layer_id\": 88601,\n          \"fifo_layer_route\": \"/inventory/fifo-layers/88601\",\n          \"product\": {\n            \"id\": 640,\n            \"sku\": \"GADGET-XL\",\n            \"name\": \"XL Gadget\",\n            \"route\": \"/products/640\"\n          },\n          \"warehouse\": \"Main Warehouse\",\n          \"old_unit_cost\": 45,\n          \"new_unit_cost\": 46.6975,\n          \"old_total_cost\": 10800,\n          \"new_total_cost\": 11207.4,\n          \"delta_total\": 407.4,\n          \"units_on_hand\": 60,\n          \"units_consumed\": 180,\n          \"original_event_date\": \"2026-06-03\",\n          \"restate_clamped\": false,\n          \"inventory_revaluation_id\": null\n        }\n      ],\n      \"resolutions\": [],\n      \"prospective_anchor_options\": {\n        \"entered\": {\n          \"date\": \"2026-08-04\"\n        },\n        \"document\": {\n          \"date\": \"2026-07-28\",\n          \"eligible\": true,\n          \"reason\": null\n        },\n        \"custom\": {\n          \"min_date\": \"2026-07-01\",\n          \"max_date\": \"2026-09-03\",\n          \"lock_date\": \"2026-06-30\",\n          \"in_closed_period\": false\n        }\n      },\n      \"anchor_day\": \"2026-08-04\"\n    },\n    \"layers\": [\n      {\n        \"line_id\": 303,\n        \"fifo_layer_id\": 88601,\n        \"sku\": \"GADGET-XL\",\n        \"name\": \"XL Gadget\",\n        \"route\": \"/inventory/fifo-layers/88601\",\n        \"received\": \"2026-06-03\",\n        \"unit_delta\": 1.6975,\n        \"delta_total\": 407.4\n      },\n      {\n        \"line_id\": 302,\n        \"fifo_layer_id\": 88413,\n        \"sku\": \"WIDGET-001\",\n        \"name\": \"Blue Widget\",\n        \"route\": \"/inventory/fifo-layers/88413\",\n        \"received\": \"2026-05-28\",\n        \"unit_delta\": 0.65,\n        \"delta_total\": 260\n      },\n      {\n        \"line_id\": 301,\n        \"fifo_layer_id\": 88412,\n        \"sku\": \"WIDGET-001\",\n        \"name\": \"Blue Widget\",\n        \"route\": \"/inventory/fifo-layers/88412\",\n        \"received\": \"2026-05-12\",\n        \"unit_delta\": 0.65,\n        \"delta_total\": 156\n      }\n    ],\n    \"selected\": {\n      \"line\": {\n        \"line_id\": 301,\n        \"fifo_layer_id\": 88412,\n        \"sku\": \"WIDGET-001\",\n        \"name\": \"Blue Widget\",\n        \"route\": \"/inventory/fifo-layers/88412\",\n        \"received\": \"2026-05-12\",\n        \"unit_before\": 12.5,\n        \"unit_after\": 13.15,\n        \"unit_delta\": 0.65,\n        \"units\": 240,\n        \"on_hand\": 150,\n        \"used\": 90,\n        \"value_before\": 3000,\n        \"value_after\": 3156,\n        \"delta_total\": 156\n      },\n      \"valued_by\": {\n        \"order\": {\n          \"reference\": \"PO-001187\",\n          \"route\": \"/orders/purchase-orders/1187\",\n          \"date\": \"2026-04-30\",\n          \"received_at\": \"2026-05-12\",\n          \"line_quantity\": 240,\n          \"receipt_quantity\": 240,\n          \"list_unit_price\": 12.5,\n          \"net_unit_price\": 12.5\n        },\n        \"steps\": [\n          {\n            \"type_label\": \"Purchase Invoice\",\n            \"reference\": \"INV-88231\",\n            \"route\": \"/orders/purchase-invoices/2210\",\n            \"date\": \"2026-05-15\",\n            \"entered_at\": \"2026-05-16\",\n            \"financial_line_id\": 6610,\n            \"description\": \"Blue Widget x 240\",\n            \"amount_on_document\": 3000,\n            \"is_trigger\": false,\n            \"kind\": \"document\",\n            \"amount\": 0,\n            \"per_unit\": 0,\n            \"unit_from\": 12.5,\n            \"unit_to\": 12.5\n          },\n          {\n            \"kind\": \"document\",\n            \"type_label\": \"Bill\",\n            \"reference\": \"FRT-2026-0142\",\n            \"route\": \"/purchases/bills/318\",\n            \"date\": \"2026-07-28\",\n            \"entered_at\": \"2026-08-04\",\n            \"description\": \"Landed cost allocated to this order line\",\n            \"amount_on_document\": 156,\n            \"is_trigger\": true,\n            \"amount\": 156,\n            \"per_unit\": 0.65,\n            \"unit_from\": 12.5,\n            \"unit_to\": 13.15\n          }\n        ]\n      },\n      \"on_hand\": {\n        \"units\": 150,\n        \"per_unit\": 0.65,\n        \"total\": 97.5,\n        \"value_before\": 1875,\n        \"value_after\": 1972.5,\n        \"lands\": {\n          \"restate\": {\n            \"period\": \"2026-05\",\n            \"day\": \"2026-05-12\",\n            \"key\": \"2026-05|inventory\",\n            \"account\": \"inventory\",\n            \"status\": \"closed\"\n          },\n          \"prospective\": {\n            \"period\": \"2026-08\",\n            \"day\": \"2026-08-04\",\n            \"key\": \"2026-08|inventory\",\n            \"account\": \"inventory\",\n            \"status\": \"open\"\n          }\n        }\n      },\n      \"usages\": [\n        {\n          \"type_label\": \"Sales Order\",\n          \"reference\": \"SO-104512\",\n          \"route\": \"/orders/sales-orders/104512\",\n          \"external\": false,\n          \"movement_id\": 771201,\n          \"date\": \"2026-07-14\",\n          \"units\": 60,\n          \"cogs_delta\": 39,\n          \"destination\": null,\n          \"lands\": {\n            \"restate\": {\n              \"period\": \"2026-07\",\n              \"day\": \"2026-07-14\",\n              \"key\": \"2026-07|cogs\",\n              \"account\": \"cogs\",\n              \"status\": \"open\"\n            },\n            \"prospective\": {\n              \"period\": \"2026-08\",\n              \"day\": \"2026-08-04\",\n              \"key\": \"2026-08|cogs\",\n              \"account\": \"cogs\",\n              \"status\": \"open\"\n            }\n          }\n        },\n        {\n          \"type_label\": \"Assembly\",\n          \"reference\": \"#10\",\n          \"route\": \"/inventory/assemblies/10\",\n          \"external\": false,\n          \"movement_id\": 771388,\n          \"date\": \"2026-07-21\",\n          \"units\": 30,\n          \"cogs_delta\": 19.5,\n          \"destination\": null,\n          \"lands\": {\n            \"restate\": {\n              \"period\": \"2026-07\",\n              \"day\": \"2026-07-21\",\n              \"key\": \"2026-07|cogs\",\n              \"account\": \"cogs\",\n              \"status\": \"open\"\n            },\n            \"prospective\": {\n              \"period\": \"2026-08\",\n              \"day\": \"2026-08-04\",\n              \"key\": \"2026-08|cogs\",\n              \"account\": \"cogs\",\n              \"status\": \"open\"\n            }\n          }\n        }\n      ],\n      \"kits\": [\n        {\n          \"fifo_layer_id\": 88950,\n          \"sku\": \"KIT-WIDGET-3PK\",\n          \"name\": \"Blue Widget 3-Pack\",\n          \"route\": \"/inventory/fifo-layers/88950\",\n          \"level\": 1,\n          \"made_via\": \"Assembly #10\",\n          \"built\": 10,\n          \"on_hand\": 6,\n          \"used\": 4,\n          \"value_now\": 420,\n          \"value_becomes\": 428.6,\n          \"unit_before\": 42,\n          \"unit_after\": 42.86,\n          \"per_unit\": 0.4,\n          \"total_per_unit\": 0.86,\n          \"drift_per_unit\": 0.46,\n          \"total\": 8.6,\n          \"from_this\": 4,\n          \"drift\": 4.6,\n          \"date\": \"2026-07-21\",\n          \"lands\": {\n            \"restate\": {\n              \"period\": \"2026-07\",\n              \"day\": \"2026-07-21\",\n              \"key\": \"2026-07|kit:88950\",\n              \"account\": \"kit:88950\",\n              \"status\": \"open\"\n            },\n            \"prospective\": {\n              \"period\": \"2026-08\",\n              \"day\": \"2026-08-04\",\n              \"key\": \"2026-08|kit:88950\",\n              \"account\": \"kit:88950\",\n              \"status\": \"open\"\n            }\n          },\n          \"usages\": [\n            {\n              \"type_label\": \"Sales Order\",\n              \"reference\": \"SO-104801\",\n              \"route\": \"/orders/sales-orders/104801\",\n              \"external\": false,\n              \"movement_id\": 773120,\n              \"date\": \"2026-07-30\",\n              \"units\": 4,\n              \"cogs_delta\": 1.6,\n              \"destination\": null,\n              \"lands\": {\n                \"restate\": {\n                  \"period\": \"2026-07\",\n                  \"day\": \"2026-07-30\",\n                  \"key\": \"2026-07|kit-cogs:88950\",\n                  \"account\": \"kit-cogs:88950\",\n                  \"status\": \"open\"\n                },\n                \"prospective\": {\n                  \"period\": \"2026-08\",\n                  \"day\": \"2026-08-04\",\n                  \"key\": \"2026-08|kit-cogs:88950\",\n                  \"account\": \"kit-cogs:88950\",\n                  \"status\": \"open\"\n                }\n              }\n            }\n          ]\n        }\n      ]\n    }\n  }\n}"}]},{"name":"Get Decision Impact","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}/decision-impact?to=prospective","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange","decision-impact"],"query":[{"key":"to","value":"prospective","description":"Target decision: restate, prospective or revert","disabled":true}],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]},"description":"Previews what a decision does to the books before it is made. `to` names the target: `restate` or `prospective` (apply a pending change that way, or switch an applied one), or `revert` (unwind an applied change back to the inbox, or reopen a dismissed one). `action` is what would actually happen for this set's current state - apply, switch, revert, reopen, or none - and `allowed` with `reason` says whether it may proceed.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`to=restate` returns `allowed: false` with `reason` set to the restate-blocked sentence when any of the set's original receipt months are behind the accounting lock date; the same sentence is what Apply Cost Change and Switch Cost Change Resolution reject with (422) in that case.\n\nFor a switch or revert, `entries` lists every ledger entry the change's revaluations posted: its period and period status (open, closed, locked), amount, whether it is already in the accounting provider (`sync`: none, individual or batch, with the provider and remote reference), what happens to it (`outcome`: redated, reversed or discarded), where a switch would re-date it (`new_effective_at`, `new_period`, `new_period_status`), the day a locked period pushes the write to (`posts_on`) and the provider-side effect (`provider_action`: none, void, update or batch_modified). `summary` totals those, and `in_flight` is true while the previous decision's downstream re-costing is still running.\n\nAuthentication: Requires Bearer token (inventory scope).\n\nPath params:\n- costChange: Cost change set ID\n\nQuery params:\n- to (required): restate, prospective or revert\n- prospective_date (optional, Y-m-d): the posting day a going-forward switch would use, instead of the set's stamped anchor or the day it was entered"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}/decision-impact?to=prospective","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange","decision-impact"],"query":[{"key":"to","value":"prospective","description":"Target decision: restate, prospective or revert","disabled":true}],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"set\": {\n      \"id\": 42,\n      \"status\": \"applied\",\n      \"resolution\": \"restate\",\n      \"backfilled\": false\n    },\n    \"to\": \"prospective\",\n    \"action\": \"switch\",\n    \"allowed\": true,\n    \"reason\": null,\n    \"lock_date\": \"2026-06-30\",\n    \"in_flight\": false,\n    \"entries\": [\n      {\n        \"id\": 90210,\n        \"route\": \"/ledger/entries/90210\",\n        \"revaluation_id\": 55010,\n        \"fifo_layer_id\": 88412,\n        \"layer_route\": \"/inventory/fifo-layers/88412\",\n        \"sku\": \"WIDGET-001\",\n        \"effective_at\": \"2026-05-12\",\n        \"period\": \"2026-05\",\n        \"period_status\": \"locked\",\n        \"amount\": 156,\n        \"status\": \"posted\",\n        \"sync\": {\n          \"kind\": \"batch\",\n          \"synced\": true,\n          \"batch\": {\n            \"id\": 61,\n            \"route\": \"/ledger/batches/61\",\n            \"period\": \"2026-07\",\n            \"status\": \"posted\"\n          },\n          \"provider\": \"QuickBooks Online\",\n          \"remote_id\": \"JE-10422\"\n        },\n        \"outcome\": \"redated\",\n        \"new_effective_at\": \"2026-08-15\",\n        \"new_period\": \"2026-08\",\n        \"new_period_status\": \"open\",\n        \"posts_on\": null,\n        \"provider_action\": \"batch_modified\"\n      },\n      {\n        \"id\": 90211,\n        \"route\": \"/ledger/entries/90211\",\n        \"revaluation_id\": 55011,\n        \"fifo_layer_id\": 88413,\n        \"layer_route\": \"/inventory/fifo-layers/88413\",\n        \"sku\": \"WIDGET-001\",\n        \"effective_at\": \"2026-05-28\",\n        \"period\": \"2026-05\",\n        \"period_status\": \"locked\",\n        \"amount\": 260,\n        \"status\": \"posted\",\n        \"sync\": {\n          \"kind\": \"individual\",\n          \"synced\": true,\n          \"batch\": null,\n          \"provider\": \"QuickBooks Online\",\n          \"remote_id\": \"812\",\n          \"remote_type\": \"JournalEntry\",\n          \"link_state\": \"linked\"\n        },\n        \"outcome\": \"redated\",\n        \"new_effective_at\": \"2026-08-15\",\n        \"new_period\": \"2026-08\",\n        \"new_period_status\": \"open\",\n        \"posts_on\": null,\n        \"provider_action\": \"update\"\n      },\n      {\n        \"id\": 90212,\n        \"route\": \"/ledger/entries/90212\",\n        \"revaluation_id\": 55012,\n        \"fifo_layer_id\": 88601,\n        \"layer_route\": \"/inventory/fifo-layers/88601\",\n        \"sku\": \"GADGET-XL\",\n        \"effective_at\": \"2026-06-03\",\n        \"period\": \"2026-06\",\n        \"period_status\": \"locked\",\n        \"amount\": 407.4,\n        \"status\": \"draft\",\n        \"sync\": {\n          \"kind\": \"none\",\n          \"synced\": false,\n          \"batch\": null,\n          \"provider\": null,\n          \"remote_id\": null\n        },\n        \"outcome\": \"redated\",\n        \"new_effective_at\": \"2026-08-15\",\n        \"new_period\": \"2026-08\",\n        \"new_period_status\": \"open\",\n        \"posts_on\": null,\n        \"provider_action\": \"none\"\n      }\n    ],\n    \"summary\": {\n      \"action\": \"switch\",\n      \"entries\": 3,\n      \"posted\": 2,\n      \"drafts\": 1,\n      \"amount\": 823.4,\n      \"voids\": 0,\n      \"updates\": 1,\n      \"batches_modified\": [\n        {\n          \"id\": 61,\n          \"route\": \"/ledger/batches/61\",\n          \"period\": \"2026-07\",\n          \"status\": \"posted\"\n        }\n      ],\n      \"displaced\": 0,\n      \"posts_on\": [],\n      \"locked_periods\": [\n        \"2026-05\",\n        \"2026-06\"\n      ],\n      \"closed_periods\": []\n    }\n  }\n}"}]},{"name":"Apply Cost Change","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}/apply","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange","apply"],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]},"description":"Applies a cost change awaiting review, writing the staged deltas onto the affected FIFO layers, re-costing anything built or transferred from them, and posting the revaluations to the ledger. The cost is re-derived from the live documents at apply time, so a stale preview never produces a stale write.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`resolution` chooses the dating: `restate` records each layer's delta on its original receipt date, re-reporting those months; `prospective` books the whole delta on a single day going forward.\n\nFor `prospective`, `prospective_anchor` picks that day: `entered` (the day the change was entered - the default), `document` (the trigger document's own date, only when it is later than the latest original receipt) or `custom` (a day you choose in `prospective_date`, which must be on or after the latest original receipt, not in the future and after the accounting lock). Each layer is still clamped to no earlier than its own receipt.\n\nResponds 422 when `resolution` is `restate` and any of the set's original receipt months are behind the accounting lock date - the message is the same `restate_blocked_reason` the listing and previews show - or when an older pending change touches the same FIFO layers and must be resolved first. Responds 409 when the change is not awaiting review, or the chosen posting day is outside the allowed window.\n\nAuthentication: Requires Bearer token (inventory scope) and permission to resolve cost changes.\n\nPath params:\n- costChange: Cost change set ID\n\nBody fields:\n- resolution (required): restate or prospective\n- prospective_anchor (optional): entered, document or custom; defaults to entered. Ignored for restate\n- prospective_date (optional, Y-m-d): required when prospective_anchor is custom\n- notes (optional, max 2000 chars): recorded on the decision","body":{"mode":"raw","raw":"{\n  \"resolution\": \"prospective\",\n  \"prospective_anchor\": \"custom\",\n  \"prospective_date\": \"2026-08-15\",\n  \"notes\": \"Freight bill arrived after month end; carrying forward to mid-August.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}/apply","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange","apply"],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"uid\": \"9f1c2b6e-7d4a-4f2e-9b1a-3c5d8e7f6a21\",\n    \"trigger_type\": \"landed_cost_bill\",\n    \"trigger_type_label\": \"Landed Cost Bill\",\n    \"trigger\": {\n      \"type\": \"App\\\\Models\\\\Bill\",\n      \"type_label\": \"Bill\",\n      \"id\": 318,\n      \"reference\": \"FRT-2026-0142\",\n      \"date\": \"2026-07-28\",\n      \"entered_at\": \"2026-08-04\",\n      \"cost_lines\": [\n        {\n          \"id\": 910,\n          \"description\": \"Ocean freight - container MSKU7781\",\n          \"amount\": 823.4,\n          \"entered_at\": \"2026-08-04\"\n        }\n      ],\n      \"cost_lines_total\": 823.4,\n      \"route\": \"/purchases/bills/318\"\n    },\n    \"status\": \"applied\",\n    \"resolution\": \"prospective\",\n    \"entered_date\": \"2026-08-04\",\n    \"prospective_anchor\": \"custom\",\n    \"prospective_anchor_date\": \"2026-08-15\",\n    \"applied_via\": \"manual\",\n    \"policy_rule_applied\": \"landed_cost_bill:material\",\n    \"totals\": {\n      \"delta\": 823.4,\n      \"on_hand_delta\": 512.75,\n      \"consumed_delta\": 310.65,\n      \"products\": 2,\n      \"layers\": 3\n    },\n    \"earliest_affected_period\": \"2026-05\",\n    \"receipts\": {\n      \"earliest\": \"2026-05-12\",\n      \"latest\": \"2026-06-03\"\n    },\n    \"origin\": {\n      \"type_label\": \"Purchase Order\",\n      \"id\": 1187,\n      \"reference\": \"PO-001187\",\n      \"date\": \"2026-04-30\",\n      \"route\": \"/orders/purchase-orders/1187\"\n    },\n    \"restate_blocked_reason\": null,\n    \"flags\": {\n      \"locked_period\": false,\n      \"closed_period\": true,\n      \"synced_entries\": false,\n      \"ledger_held\": false,\n      \"backfilled\": false,\n      \"cascaded\": true\n    },\n    \"notes\": \"Freight bill arrived after month end; carrying forward to mid-August.\",\n    \"resolved_by\": \"Jane Doe\",\n    \"resolved_at\": \"2026-08-05T09:14:02+00:00\",\n    \"restaged_at\": null,\n    \"preview_computed_at\": \"2026-08-04T14:22:11+00:00\",\n    \"created_at\": \"2026-08-04T14:22:09+00:00\",\n    \"updated_at\": \"2026-08-05T09:14:02+00:00\",\n    \"lines\": [\n      {\n        \"id\": 301,\n        \"fifo_layer_id\": 88412,\n        \"fifo_layer_route\": \"/inventory/fifo-layers/88412\",\n        \"product\": {\n          \"id\": 512,\n          \"sku\": \"WIDGET-001\",\n          \"name\": \"Blue Widget\",\n          \"route\": \"/products/512\"\n        },\n        \"warehouse\": \"Main Warehouse\",\n        \"old_unit_cost\": 12.5,\n        \"new_unit_cost\": 13.15,\n        \"old_total_cost\": 3000,\n        \"new_total_cost\": 3156,\n        \"delta_total\": 156,\n        \"units_on_hand\": 150,\n        \"units_consumed\": 90,\n        \"original_event_date\": \"2026-05-12\",\n        \"restate_clamped\": false,\n        \"inventory_revaluation_id\": 55010\n      },\n      {\n        \"id\": 302,\n        \"fifo_layer_id\": 88413,\n        \"fifo_layer_route\": \"/inventory/fifo-layers/88413\",\n        \"product\": {\n          \"id\": 512,\n          \"sku\": \"WIDGET-001\",\n          \"name\": \"Blue Widget\",\n          \"route\": \"/products/512\"\n        },\n        \"warehouse\": \"Main Warehouse\",\n        \"old_unit_cost\": 12.5,\n        \"new_unit_cost\": 13.15,\n        \"old_total_cost\": 5000,\n        \"new_total_cost\": 5260,\n        \"delta_total\": 260,\n        \"units_on_hand\": 400,\n        \"units_consumed\": 0,\n        \"original_event_date\": \"2026-05-28\",\n        \"restate_clamped\": false,\n        \"inventory_revaluation_id\": 55011\n      },\n      {\n        \"id\": 303,\n        \"fifo_layer_id\": 88601,\n        \"fifo_layer_route\": \"/inventory/fifo-layers/88601\",\n        \"product\": {\n          \"id\": 640,\n          \"sku\": \"GADGET-XL\",\n          \"name\": \"XL Gadget\",\n          \"route\": \"/products/640\"\n        },\n        \"warehouse\": \"Main Warehouse\",\n        \"old_unit_cost\": 45,\n        \"new_unit_cost\": 46.6975,\n        \"old_total_cost\": 10800,\n        \"new_total_cost\": 11207.4,\n        \"delta_total\": 407.4,\n        \"units_on_hand\": 60,\n        \"units_consumed\": 180,\n        \"original_event_date\": \"2026-06-03\",\n        \"restate_clamped\": false,\n        \"inventory_revaluation_id\": 55012\n      }\n    ],\n    \"resolutions\": [\n      {\n        \"id\": 77,\n        \"action\": \"apply\",\n        \"resolution\": \"prospective\",\n        \"user\": \"Jane Doe\",\n        \"notes\": \"Freight bill arrived after month end; carrying forward to mid-August.\",\n        \"created_at\": \"2026-08-05T09:14:02+00:00\"\n      }\n    ]\n  }\n}"}]},{"name":"Dismiss Cost Change","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}/dismiss","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange","dismiss"],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]},"description":"Dismisses a cost change awaiting review without applying it. The staged deltas are discarded from the queue, the affected layers keep their current cost, and the triggering document's held ledger entry is released. A dismissed change can be brought back to the inbox with Revert Cost Change.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nResponds 409 when the change is not awaiting review.\n\nAuthentication: Requires Bearer token (inventory scope) and permission to resolve cost changes.\n\nPath params:\n- costChange: Cost change set ID\n\nBody fields:\n- notes (optional, max 2000 chars): recorded on the decision","body":{"mode":"raw","raw":"{\n  \"notes\": \"Duplicate of the freight bill already allocated on PO-001187.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}/dismiss","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange","dismiss"],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"uid\": \"9f1c2b6e-7d4a-4f2e-9b1a-3c5d8e7f6a21\",\n    \"trigger_type\": \"landed_cost_bill\",\n    \"trigger_type_label\": \"Landed Cost Bill\",\n    \"trigger\": {\n      \"type\": \"App\\\\Models\\\\Bill\",\n      \"type_label\": \"Bill\",\n      \"id\": 318,\n      \"reference\": \"FRT-2026-0142\",\n      \"date\": \"2026-07-28\",\n      \"entered_at\": \"2026-08-04\",\n      \"cost_lines\": [\n        {\n          \"id\": 910,\n          \"description\": \"Ocean freight - container MSKU7781\",\n          \"amount\": 823.4,\n          \"entered_at\": \"2026-08-04\"\n        }\n      ],\n      \"cost_lines_total\": 823.4,\n      \"route\": \"/purchases/bills/318\"\n    },\n    \"status\": \"dismissed\",\n    \"resolution\": null,\n    \"entered_date\": \"2026-08-04\",\n    \"prospective_anchor\": null,\n    \"prospective_anchor_date\": null,\n    \"applied_via\": null,\n    \"policy_rule_applied\": \"landed_cost_bill:material\",\n    \"totals\": {\n      \"delta\": 823.4,\n      \"on_hand_delta\": 512.75,\n      \"consumed_delta\": 310.65,\n      \"products\": 2,\n      \"layers\": 3\n    },\n    \"earliest_affected_period\": \"2026-05\",\n    \"receipts\": {\n      \"earliest\": \"2026-05-12\",\n      \"latest\": \"2026-06-03\"\n    },\n    \"origin\": {\n      \"type_label\": \"Purchase Order\",\n      \"id\": 1187,\n      \"reference\": \"PO-001187\",\n      \"date\": \"2026-04-30\",\n      \"route\": \"/orders/purchase-orders/1187\"\n    },\n    \"restate_blocked_reason\": null,\n    \"flags\": {\n      \"locked_period\": false,\n      \"closed_period\": true,\n      \"synced_entries\": false,\n      \"ledger_held\": false,\n      \"backfilled\": false,\n      \"cascaded\": false\n    },\n    \"notes\": \"Duplicate of the freight bill already allocated on PO-001187.\",\n    \"resolved_by\": \"Jane Doe\",\n    \"resolved_at\": \"2026-08-05T09:20:44+00:00\",\n    \"restaged_at\": null,\n    \"preview_computed_at\": \"2026-08-04T14:22:11+00:00\",\n    \"created_at\": \"2026-08-04T14:22:09+00:00\",\n    \"updated_at\": \"2026-08-05T09:20:44+00:00\",\n    \"lines\": [\n      {\n        \"id\": 301,\n        \"fifo_layer_id\": 88412,\n        \"fifo_layer_route\": \"/inventory/fifo-layers/88412\",\n        \"product\": {\n          \"id\": 512,\n          \"sku\": \"WIDGET-001\",\n          \"name\": \"Blue Widget\",\n          \"route\": \"/products/512\"\n        },\n        \"warehouse\": \"Main Warehouse\",\n        \"old_unit_cost\": 12.5,\n        \"new_unit_cost\": 13.15,\n        \"old_total_cost\": 3000,\n        \"new_total_cost\": 3156,\n        \"delta_total\": 156,\n        \"units_on_hand\": 150,\n        \"units_consumed\": 90,\n        \"original_event_date\": \"2026-05-12\",\n        \"restate_clamped\": false,\n        \"inventory_revaluation_id\": null\n      },\n      {\n        \"id\": 302,\n        \"fifo_layer_id\": 88413,\n        \"fifo_layer_route\": \"/inventory/fifo-layers/88413\",\n        \"product\": {\n          \"id\": 512,\n          \"sku\": \"WIDGET-001\",\n          \"name\": \"Blue Widget\",\n          \"route\": \"/products/512\"\n        },\n        \"warehouse\": \"Main Warehouse\",\n        \"old_unit_cost\": 12.5,\n        \"new_unit_cost\": 13.15,\n        \"old_total_cost\": 5000,\n        \"new_total_cost\": 5260,\n        \"delta_total\": 260,\n        \"units_on_hand\": 400,\n        \"units_consumed\": 0,\n        \"original_event_date\": \"2026-05-28\",\n        \"restate_clamped\": false,\n        \"inventory_revaluation_id\": null\n      },\n      {\n        \"id\": 303,\n        \"fifo_layer_id\": 88601,\n        \"fifo_layer_route\": \"/inventory/fifo-layers/88601\",\n        \"product\": {\n          \"id\": 640,\n          \"sku\": \"GADGET-XL\",\n          \"name\": \"XL Gadget\",\n          \"route\": \"/products/640\"\n        },\n        \"warehouse\": \"Main Warehouse\",\n        \"old_unit_cost\": 45,\n        \"new_unit_cost\": 46.6975,\n        \"old_total_cost\": 10800,\n        \"new_total_cost\": 11207.4,\n        \"delta_total\": 407.4,\n        \"units_on_hand\": 60,\n        \"units_consumed\": 180,\n        \"original_event_date\": \"2026-06-03\",\n        \"restate_clamped\": false,\n        \"inventory_revaluation_id\": null\n      }\n    ],\n    \"resolutions\": [\n      {\n        \"id\": 78,\n        \"action\": \"dismiss\",\n        \"resolution\": null,\n        \"user\": \"Jane Doe\",\n        \"notes\": \"Duplicate of the freight bill already allocated on PO-001187.\",\n        \"created_at\": \"2026-08-05T09:20:44+00:00\"\n      }\n    ]\n  }\n}"}]},{"name":"Switch Cost Change Resolution","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}/switch","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange","switch"],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]},"description":"Switches an applied cost change between restate and going forward. Every revaluation the change produced is re-dated to the new convention (and its ledger entries with it), rather than reversed and re-posted, so the cost history stays attributable to this one decision. Switching to the resolution the change already carries is a no-op.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFor `prospective`, `prospective_anchor` and `prospective_date` choose the posting day exactly as on apply. Switching to `restate` clears any stamped anchor.\n\nResponds 422 when switching to `restate` and any of the set's original receipt months are behind the accounting lock date - the message is the same `restate_blocked_reason` the listing and previews show. Responds 409 when the change is not in the applied state, or the chosen posting day is outside the allowed window. Use Get Decision Impact first to see which ledger entries and provider records the switch touches.\n\nAuthentication: Requires Bearer token (inventory scope) and permission to resolve cost changes.\n\nPath params:\n- costChange: Cost change set ID\n\nBody fields:\n- resolution (required): restate or prospective\n- prospective_anchor (optional): entered, document or custom; defaults to entered. Ignored for restate\n- prospective_date (optional, Y-m-d): required when prospective_anchor is custom\n- notes (optional, max 2000 chars): recorded on the decision","body":{"mode":"raw","raw":"{\n  \"resolution\": \"prospective\",\n  \"prospective_anchor\": \"document\",\n  \"prospective_date\": null,\n  \"notes\": \"May and June are filed; carry the freight forward to the bill date.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}/switch","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange","switch"],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"uid\": \"9f1c2b6e-7d4a-4f2e-9b1a-3c5d8e7f6a21\",\n    \"trigger_type\": \"landed_cost_bill\",\n    \"trigger_type_label\": \"Landed Cost Bill\",\n    \"trigger\": {\n      \"type\": \"App\\\\Models\\\\Bill\",\n      \"type_label\": \"Bill\",\n      \"id\": 318,\n      \"reference\": \"FRT-2026-0142\",\n      \"date\": \"2026-07-28\",\n      \"entered_at\": \"2026-08-04\",\n      \"cost_lines\": [\n        {\n          \"id\": 910,\n          \"description\": \"Ocean freight - container MSKU7781\",\n          \"amount\": 823.4,\n          \"entered_at\": \"2026-08-04\"\n        }\n      ],\n      \"cost_lines_total\": 823.4,\n      \"route\": \"/purchases/bills/318\"\n    },\n    \"status\": \"applied\",\n    \"resolution\": \"prospective\",\n    \"entered_date\": \"2026-08-04\",\n    \"prospective_anchor\": \"document\",\n    \"prospective_anchor_date\": \"2026-07-28\",\n    \"applied_via\": \"manual\",\n    \"policy_rule_applied\": \"landed_cost_bill:material\",\n    \"totals\": {\n      \"delta\": 823.4,\n      \"on_hand_delta\": 512.75,\n      \"consumed_delta\": 310.65,\n      \"products\": 2,\n      \"layers\": 3\n    },\n    \"earliest_affected_period\": \"2026-05\",\n    \"receipts\": {\n      \"earliest\": \"2026-05-12\",\n      \"latest\": \"2026-06-03\"\n    },\n    \"origin\": {\n      \"type_label\": \"Purchase Order\",\n      \"id\": 1187,\n      \"reference\": \"PO-001187\",\n      \"date\": \"2026-04-30\",\n      \"route\": \"/orders/purchase-orders/1187\"\n    },\n    \"restate_blocked_reason\": null,\n    \"flags\": {\n      \"locked_period\": false,\n      \"closed_period\": true,\n      \"synced_entries\": false,\n      \"ledger_held\": false,\n      \"backfilled\": false,\n      \"cascaded\": true\n    },\n    \"notes\": \"May and June are filed; carry the freight forward to the bill date.\",\n    \"resolved_by\": \"Jane Doe\",\n    \"resolved_at\": \"2026-08-07T11:03:27+00:00\",\n    \"restaged_at\": null,\n    \"preview_computed_at\": \"2026-08-04T14:22:11+00:00\",\n    \"created_at\": \"2026-08-04T14:22:09+00:00\",\n    \"updated_at\": \"2026-08-07T11:03:27+00:00\",\n    \"lines\": [\n      {\n        \"id\": 301,\n        \"fifo_layer_id\": 88412,\n        \"fifo_layer_route\": \"/inventory/fifo-layers/88412\",\n        \"product\": {\n          \"id\": 512,\n          \"sku\": \"WIDGET-001\",\n          \"name\": \"Blue Widget\",\n          \"route\": \"/products/512\"\n        },\n        \"warehouse\": \"Main Warehouse\",\n        \"old_unit_cost\": 12.5,\n        \"new_unit_cost\": 13.15,\n        \"old_total_cost\": 3000,\n        \"new_total_cost\": 3156,\n        \"delta_total\": 156,\n        \"units_on_hand\": 150,\n        \"units_consumed\": 90,\n        \"original_event_date\": \"2026-05-12\",\n        \"restate_clamped\": false,\n        \"inventory_revaluation_id\": 55010\n      },\n      {\n        \"id\": 302,\n        \"fifo_layer_id\": 88413,\n        \"fifo_layer_route\": \"/inventory/fifo-layers/88413\",\n        \"product\": {\n          \"id\": 512,\n          \"sku\": \"WIDGET-001\",\n          \"name\": \"Blue Widget\",\n          \"route\": \"/products/512\"\n        },\n        \"warehouse\": \"Main Warehouse\",\n        \"old_unit_cost\": 12.5,\n        \"new_unit_cost\": 13.15,\n        \"old_total_cost\": 5000,\n        \"new_total_cost\": 5260,\n        \"delta_total\": 260,\n        \"units_on_hand\": 400,\n        \"units_consumed\": 0,\n        \"original_event_date\": \"2026-05-28\",\n        \"restate_clamped\": false,\n        \"inventory_revaluation_id\": 55011\n      },\n      {\n        \"id\": 303,\n        \"fifo_layer_id\": 88601,\n        \"fifo_layer_route\": \"/inventory/fifo-layers/88601\",\n        \"product\": {\n          \"id\": 640,\n          \"sku\": \"GADGET-XL\",\n          \"name\": \"XL Gadget\",\n          \"route\": \"/products/640\"\n        },\n        \"warehouse\": \"Main Warehouse\",\n        \"old_unit_cost\": 45,\n        \"new_unit_cost\": 46.6975,\n        \"old_total_cost\": 10800,\n        \"new_total_cost\": 11207.4,\n        \"delta_total\": 407.4,\n        \"units_on_hand\": 60,\n        \"units_consumed\": 180,\n        \"original_event_date\": \"2026-06-03\",\n        \"restate_clamped\": false,\n        \"inventory_revaluation_id\": 55012\n      }\n    ],\n    \"resolutions\": [\n      {\n        \"id\": 77,\n        \"action\": \"apply\",\n        \"resolution\": \"restate\",\n        \"user\": \"Jane Doe\",\n        \"notes\": null,\n        \"created_at\": \"2026-08-05T09:14:02+00:00\"\n      },\n      {\n        \"id\": 81,\n        \"action\": \"switch\",\n        \"resolution\": \"prospective\",\n        \"user\": \"Jane Doe\",\n        \"notes\": \"May and June are filed; carry the freight forward to the bill date.\",\n        \"created_at\": \"2026-08-07T11:03:27+00:00\"\n      }\n    ]\n  }\n}"}]},{"name":"Revert Cost Change","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}/revert","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange","revert"],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]},"description":"Returns an applied or dismissed cost change to the inbox. For an applied change, every revaluation it produced is reversed, anything re-costed downstream is unwound, and the set is re-staged for review with a fresh preview; posted ledger entries are reversed and drafts discarded. For a dismissed change the set is simply reopened.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nResponds 409 when the change is already in the inbox, when it was applied by a backfill (switch its resolution instead), or when the same inventory has been revalued by a later change that must be reverted first. Use Get Decision Impact with `to=revert` to see the ledger and provider consequences before calling this.\n\nAuthentication: Requires Bearer token (inventory scope) and permission to resolve cost changes.\n\nPath params:\n- costChange: Cost change set ID\n\nBody fields:\n- notes (optional, max 2000 chars): recorded on the decision","body":{"mode":"raw","raw":"{\n  \"notes\": \"Applied against the wrong bill; sending back for review.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-changes/{costChange}/revert","host":["{{baseUrl}}"],"path":["api","cost-changes",":costChange","revert"],"variable":[{"key":"costChange","value":"","description":"The cost change ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 42,\n    \"uid\": \"9f1c2b6e-7d4a-4f2e-9b1a-3c5d8e7f6a21\",\n    \"trigger_type\": \"landed_cost_bill\",\n    \"trigger_type_label\": \"Landed Cost Bill\",\n    \"trigger\": {\n      \"type\": \"App\\\\Models\\\\Bill\",\n      \"type_label\": \"Bill\",\n      \"id\": 318,\n      \"reference\": \"FRT-2026-0142\",\n      \"date\": \"2026-07-28\",\n      \"entered_at\": \"2026-08-04\",\n      \"cost_lines\": [\n        {\n          \"id\": 910,\n          \"description\": \"Ocean freight - container MSKU7781\",\n          \"amount\": 823.4,\n          \"entered_at\": \"2026-08-04\"\n        }\n      ],\n      \"cost_lines_total\": 823.4,\n      \"route\": \"/purchases/bills/318\"\n    },\n    \"status\": \"pending_review\",\n    \"resolution\": null,\n    \"entered_date\": \"2026-08-04\",\n    \"prospective_anchor\": null,\n    \"prospective_anchor_date\": null,\n    \"applied_via\": null,\n    \"policy_rule_applied\": \"landed_cost_bill:material\",\n    \"totals\": {\n      \"delta\": 823.4,\n      \"on_hand_delta\": 512.75,\n      \"consumed_delta\": 310.65,\n      \"products\": 2,\n      \"layers\": 3\n    },\n    \"earliest_affected_period\": \"2026-05\",\n    \"receipts\": {\n      \"earliest\": \"2026-05-12\",\n      \"latest\": \"2026-06-03\"\n    },\n    \"origin\": {\n      \"type_label\": \"Purchase Order\",\n      \"id\": 1187,\n      \"reference\": \"PO-001187\",\n      \"date\": \"2026-04-30\",\n      \"route\": \"/orders/purchase-orders/1187\"\n    },\n    \"restate_blocked_reason\": null,\n    \"flags\": {\n      \"locked_period\": false,\n      \"closed_period\": true,\n      \"synced_entries\": false,\n      \"ledger_held\": true,\n      \"backfilled\": false,\n      \"cascaded\": false\n    },\n    \"notes\": \"Applied against the wrong bill; sending back for review.\",\n    \"resolved_by\": null,\n    \"resolved_at\": null,\n    \"restaged_at\": \"2026-08-08T15:41:10+00:00\",\n    \"preview_computed_at\": \"2026-08-08T15:41:11+00:00\",\n    \"created_at\": \"2026-08-04T14:22:09+00:00\",\n    \"updated_at\": \"2026-08-08T15:41:11+00:00\",\n    \"lines\": [\n      {\n        \"id\": 301,\n        \"fifo_layer_id\": 88412,\n        \"fifo_layer_route\": \"/inventory/fifo-layers/88412\",\n        \"product\": {\n          \"id\": 512,\n          \"sku\": \"WIDGET-001\",\n          \"name\": \"Blue Widget\",\n          \"route\": \"/products/512\"\n        },\n        \"warehouse\": \"Main Warehouse\",\n        \"old_unit_cost\": 12.5,\n        \"new_unit_cost\": 13.15,\n        \"old_total_cost\": 3000,\n        \"new_total_cost\": 3156,\n        \"delta_total\": 156,\n        \"units_on_hand\": 150,\n        \"units_consumed\": 90,\n        \"original_event_date\": \"2026-05-12\",\n        \"restate_clamped\": false,\n        \"inventory_revaluation_id\": null\n      },\n      {\n        \"id\": 302,\n        \"fifo_layer_id\": 88413,\n        \"fifo_layer_route\": \"/inventory/fifo-layers/88413\",\n        \"product\": {\n          \"id\": 512,\n          \"sku\": \"WIDGET-001\",\n          \"name\": \"Blue Widget\",\n          \"route\": \"/products/512\"\n        },\n        \"warehouse\": \"Main Warehouse\",\n        \"old_unit_cost\": 12.5,\n        \"new_unit_cost\": 13.15,\n        \"old_total_cost\": 5000,\n        \"new_total_cost\": 5260,\n        \"delta_total\": 260,\n        \"units_on_hand\": 400,\n        \"units_consumed\": 0,\n        \"original_event_date\": \"2026-05-28\",\n        \"restate_clamped\": false,\n        \"inventory_revaluation_id\": null\n      },\n      {\n        \"id\": 303,\n        \"fifo_layer_id\": 88601,\n        \"fifo_layer_route\": \"/inventory/fifo-layers/88601\",\n        \"product\": {\n          \"id\": 640,\n          \"sku\": \"GADGET-XL\",\n          \"name\": \"XL Gadget\",\n          \"route\": \"/products/640\"\n        },\n        \"warehouse\": \"Main Warehouse\",\n        \"old_unit_cost\": 45,\n        \"new_unit_cost\": 46.6975,\n        \"old_total_cost\": 10800,\n        \"new_total_cost\": 11207.4,\n        \"delta_total\": 407.4,\n        \"units_on_hand\": 60,\n        \"units_consumed\": 180,\n        \"original_event_date\": \"2026-06-03\",\n        \"restate_clamped\": false,\n        \"inventory_revaluation_id\": null\n      }\n    ],\n    \"resolutions\": [\n      {\n        \"id\": 77,\n        \"action\": \"apply\",\n        \"resolution\": \"prospective\",\n        \"user\": \"Jane Doe\",\n        \"notes\": null,\n        \"created_at\": \"2026-08-05T09:14:02+00:00\"\n      },\n      {\n        \"id\": 84,\n        \"action\": \"revert\",\n        \"resolution\": null,\n        \"user\": \"Jane Doe\",\n        \"notes\": \"Applied against the wrong bill; sending back for review.\",\n        \"created_at\": \"2026-08-08T15:41:10+00:00\"\n      }\n    ]\n  }\n}"}]},{"name":"Bulk Resolve Cost Changes","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-changes/bulk-resolve","host":["{{baseUrl}}"],"path":["api","cost-changes","bulk-resolve"]},"description":"Resolves several cost changes in one call, oldest-first so that overlapping changes apply in the order they were staged. Every action is state-aware and never fails the batch outright: each selected set is either resolved, skipped with a reason, or failed with an error, and the response lists all three.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`action`:\n- apply: applies sets awaiting review with the given `resolution`; switches already-applied sets to that resolution; skips dismissed sets and sets already carrying it\n- dismiss: dismisses sets awaiting review; skips the rest\n- revert: reverts applied or dismissed sets to the inbox; skips sets already there\n\nWhen `action` is omitted, a `resolution` means apply and no `resolution` means dismiss.\n\nWith `resolution: restate`, any set whose original receipt months are behind the accounting lock date is reported under `skipped` with the restate-blocked sentence as its message, and the rest of the batch proceeds. A set blocked by an older pending change on the same layers is reported under `failed`.\n\nAuthentication: Requires Bearer token (inventory scope) and permission to resolve cost changes.\n\nBody fields:\n- ids (required, array of integers, min 1): cost change set IDs\n- action (optional): apply, dismiss or revert\n- resolution (optional, required when action is apply): restate or prospective\n- prospective_anchor (optional): entered or document (a custom day is not available in bulk); defaults to entered\n- notes (optional, max 2000 chars): recorded on every decision in the batch","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    42,\n    43,\n    44\n  ],\n  \"action\": \"apply\",\n  \"resolution\": \"prospective\",\n  \"prospective_anchor\": \"document\",\n  \"notes\": \"Q3 freight bills - carrying forward.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-changes/bulk-resolve","host":["{{baseUrl}}"],"path":["api","cost-changes","bulk-resolve"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"action\": \"apply\",\n    \"resolved\": [\n      42,\n      44\n    ],\n    \"skipped\": [\n      {\n        \"id\": 43,\n        \"message\": \"Already applied going forward.\"\n      }\n    ],\n    \"failed\": []\n  },\n  \"message\": \"2 cost change(s) applied, 1 skipped.\"\n}"}]},{"name":"Get Bulk Decision Impact","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-changes/bulk-decision-impact","host":["{{baseUrl}}"],"path":["api","cost-changes","bulk-decision-impact"]},"description":"Returns the decision-impact preview for a selection: one block per set (the same shape as Get Decision Impact, in oldest-first order) plus `totals` across them - how many sets are allowed to proceed, how many ledger entries are touched and their total amount, the provider-side voids and updates, the period batches that would be re-sent, and how many writes a locked period displaces. Use it to confirm a bulk switch or revert with the whole picture in view.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (inventory scope) and permission to resolve cost changes.\n\nBody fields:\n- ids (required, array of integers, min 1): cost change set IDs\n- to (required): restate, prospective or revert","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    42,\n    43\n  ],\n  \"to\": \"prospective\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/cost-changes/bulk-decision-impact","host":["{{baseUrl}}"],"path":["api","cost-changes","bulk-decision-impact"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"to\": \"prospective\",\n    \"sets\": [\n      {\n        \"set\": {\n          \"id\": 42,\n          \"status\": \"applied\",\n          \"resolution\": \"restate\",\n          \"backfilled\": false\n        },\n        \"to\": \"prospective\",\n        \"action\": \"switch\",\n        \"allowed\": true,\n        \"reason\": null,\n        \"lock_date\": \"2026-06-30\",\n        \"in_flight\": false,\n        \"entries\": [\n          {\n            \"id\": 90210,\n            \"route\": \"/ledger/entries/90210\",\n            \"revaluation_id\": 55010,\n            \"fifo_layer_id\": 88412,\n            \"layer_route\": \"/inventory/fifo-layers/88412\",\n            \"sku\": \"WIDGET-001\",\n            \"effective_at\": \"2026-05-12\",\n            \"period\": \"2026-05\",\n            \"period_status\": \"locked\",\n            \"amount\": 156,\n            \"status\": \"posted\",\n            \"sync\": {\n              \"kind\": \"batch\",\n              \"synced\": true,\n              \"batch\": {\n                \"id\": 61,\n                \"route\": \"/ledger/batches/61\",\n                \"period\": \"2026-07\",\n                \"status\": \"posted\"\n              },\n              \"provider\": \"QuickBooks Online\",\n              \"remote_id\": \"JE-10422\"\n            },\n            \"outcome\": \"redated\",\n            \"new_effective_at\": \"2026-08-15\",\n            \"new_period\": \"2026-08\",\n            \"new_period_status\": \"open\",\n            \"posts_on\": null,\n            \"provider_action\": \"batch_modified\"\n          },\n          {\n            \"id\": 90211,\n            \"route\": \"/ledger/entries/90211\",\n            \"revaluation_id\": 55011,\n            \"fifo_layer_id\": 88413,\n            \"layer_route\": \"/inventory/fifo-layers/88413\",\n            \"sku\": \"WIDGET-001\",\n            \"effective_at\": \"2026-05-28\",\n            \"period\": \"2026-05\",\n            \"period_status\": \"locked\",\n            \"amount\": 260,\n            \"status\": \"posted\",\n            \"sync\": {\n              \"kind\": \"individual\",\n              \"synced\": true,\n              \"batch\": null,\n              \"provider\": \"QuickBooks Online\",\n              \"remote_id\": \"812\",\n              \"remote_type\": \"JournalEntry\",\n              \"link_state\": \"linked\"\n            },\n            \"outcome\": \"redated\",\n            \"new_effective_at\": \"2026-08-15\",\n            \"new_period\": \"2026-08\",\n            \"new_period_status\": \"open\",\n            \"posts_on\": null,\n            \"provider_action\": \"update\"\n          },\n          {\n            \"id\": 90212,\n            \"route\": \"/ledger/entries/90212\",\n            \"revaluation_id\": 55012,\n            \"fifo_layer_id\": 88601,\n            \"layer_route\": \"/inventory/fifo-layers/88601\",\n            \"sku\": \"GADGET-XL\",\n            \"effective_at\": \"2026-06-03\",\n            \"period\": \"2026-06\",\n            \"period_status\": \"locked\",\n            \"amount\": 407.4,\n            \"status\": \"draft\",\n            \"sync\": {\n              \"kind\": \"none\",\n              \"synced\": false,\n              \"batch\": null,\n              \"provider\": null,\n              \"remote_id\": null\n            },\n            \"outcome\": \"redated\",\n            \"new_effective_at\": \"2026-08-15\",\n            \"new_period\": \"2026-08\",\n            \"new_period_status\": \"open\",\n            \"posts_on\": null,\n            \"provider_action\": \"none\"\n          }\n        ],\n        \"summary\": {\n          \"action\": \"switch\",\n          \"entries\": 3,\n          \"posted\": 2,\n          \"drafts\": 1,\n          \"amount\": 823.4,\n          \"voids\": 0,\n          \"updates\": 1,\n          \"batches_modified\": [\n            {\n              \"id\": 61,\n              \"route\": \"/ledger/batches/61\",\n              \"period\": \"2026-07\",\n              \"status\": \"posted\"\n            }\n          ],\n          \"displaced\": 0,\n          \"posts_on\": [],\n          \"locked_periods\": [\n            \"2026-05\",\n            \"2026-06\"\n          ],\n          \"closed_periods\": []\n        }\n      },\n      {\n        \"set\": {\n          \"id\": 43,\n          \"status\": \"pending_review\",\n          \"resolution\": null,\n          \"backfilled\": false\n        },\n        \"to\": \"prospective\",\n        \"action\": \"apply\",\n        \"allowed\": true,\n        \"reason\": null,\n        \"lock_date\": \"2026-06-30\",\n        \"in_flight\": false,\n        \"entries\": [],\n        \"summary\": {\n          \"action\": \"apply\",\n          \"entries\": 0,\n          \"posted\": 0,\n          \"drafts\": 0,\n          \"amount\": 0,\n          \"voids\": 0,\n          \"updates\": 0,\n          \"batches_modified\": [],\n          \"displaced\": 0,\n          \"posts_on\": [],\n          \"locked_periods\": [],\n          \"closed_periods\": []\n        }\n      }\n    ],\n    \"totals\": {\n      \"sets\": 2,\n      \"allowed\": 2,\n      \"entries\": 3,\n      \"amount\": 823.4,\n      \"voids\": 0,\n      \"updates\": 1,\n      \"batches_modified\": [\n        {\n          \"id\": 61,\n          \"route\": \"/ledger/batches/61\",\n          \"period\": \"2026-07\",\n          \"status\": \"posted\"\n        }\n      ],\n      \"displaced\": 0\n    }\n  }\n}"}]},{"name":"Get Cost Change Policy Settings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/cost-changes/settings","host":["{{baseUrl}}"],"path":["api","cost-changes","settings"]},"description":"Returns the routing policy for retroactive cost changes: per trigger type, whether a change applies immediately or is held in the inbox for review, and which resolution an automatic application uses. These rules decide routing only - every change stays an individually dated revaluation and any held set can still be resolved either way by a person.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`trigger_policies` is keyed by trigger (landed_cost_bill, purchase_invoice_new, purchase_invoice_edited, po_line_edited, po_financials_edited, po_currency_changed, data_fix). Each rule has:\n- mode: auto (apply without review), review (always hold), review_if_material (hold when the delta reaches materiality_amount or materiality_percent)\n- auto_resolution: prospective, restate, or restate_while_open (restate while the original months are still open, otherwise go forward)\n- materiality_amount / materiality_percent: thresholds for review_if_material\n- label: display name of the trigger\n\nTriggers without an explicit rule report the default (auto, prospective, 100 / 5%), except data_fix which defaults to review. The three `always_review_*` guardrails escalate any set that touches a locked period, a closed period, or entries already synced to the accounting provider, regardless of the trigger's rule.\n\nAuthentication: Requires Bearer token (inventory scope)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/cost-changes/settings","host":["{{baseUrl}}"],"path":["api","cost-changes","settings"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"trigger_policies\": {\n      \"landed_cost_bill\": {\n        \"mode\": \"review_if_material\",\n        \"auto_resolution\": \"prospective\",\n        \"materiality_amount\": 250,\n        \"materiality_percent\": 2.5,\n        \"label\": \"Landed Cost Bill\"\n      },\n      \"purchase_invoice_new\": {\n        \"mode\": \"auto\",\n        \"auto_resolution\": \"prospective\",\n        \"materiality_amount\": 100,\n        \"materiality_percent\": 5,\n        \"label\": \"Invoice After Receipt\"\n      },\n      \"purchase_invoice_edited\": {\n        \"mode\": \"auto\",\n        \"auto_resolution\": \"prospective\",\n        \"materiality_amount\": 100,\n        \"materiality_percent\": 5,\n        \"label\": \"Invoice Edited\"\n      },\n      \"po_line_edited\": {\n        \"mode\": \"auto\",\n        \"auto_resolution\": \"prospective\",\n        \"materiality_amount\": 100,\n        \"materiality_percent\": 5,\n        \"label\": \"PO Price Edit\"\n      },\n      \"po_financials_edited\": {\n        \"mode\": \"auto\",\n        \"auto_resolution\": \"prospective\",\n        \"materiality_amount\": 100,\n        \"materiality_percent\": 5,\n        \"label\": \"PO Financials Edit\"\n      },\n      \"po_currency_changed\": {\n        \"mode\": \"review\",\n        \"auto_resolution\": \"prospective\",\n        \"materiality_amount\": 100,\n        \"materiality_percent\": 5,\n        \"label\": \"PO Currency Change\"\n      },\n      \"data_fix\": {\n        \"mode\": \"review\",\n        \"auto_resolution\": \"prospective\",\n        \"materiality_amount\": 100,\n        \"materiality_percent\": 5,\n        \"label\": \"Data Fix\"\n      }\n    },\n    \"always_review_locked_impact\": true,\n    \"always_review_closed_impact\": false,\n    \"always_review_synced_entries\": false\n  }\n}"}]},{"name":"Update Cost Change Policy Settings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/cost-changes/settings","host":["{{baseUrl}}"],"path":["api","cost-changes","settings"]},"description":"Replaces the routing policy for retroactive cost changes and returns the effective settings (same shape as Get Cost Change Policy Settings). `trigger_policies` must be present; every trigger you include needs all four rule fields, and triggers you omit fall back to their defaults. Unknown trigger keys are rejected.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (inventory scope) and permission to manage accounting settings.\n\nBody fields:\n- trigger_policies (required, object keyed by trigger): landed_cost_bill, purchase_invoice_new, purchase_invoice_edited, po_line_edited, po_financials_edited, po_currency_changed, data_fix\n- trigger_policies.*.mode (required): auto, review or review_if_material\n- trigger_policies.*.auto_resolution (required): prospective, restate or restate_while_open\n- trigger_policies.*.materiality_amount (required, numeric, min 0)\n- trigger_policies.*.materiality_percent (required, numeric, 0-1000)\n- always_review_locked_impact (required, boolean): hold any change whose restate dates touch a locked period\n- always_review_closed_impact (required, boolean): hold any change touching a closed (not locked) period\n- always_review_synced_entries (required, boolean): hold any change that would reverse and replace entries already synced to the accounting provider","body":{"mode":"raw","raw":"{\n  \"trigger_policies\": {\n    \"landed_cost_bill\": {\n      \"mode\": \"review_if_material\",\n      \"auto_resolution\": \"prospective\",\n      \"materiality_amount\": 250,\n      \"materiality_percent\": 2.5\n    },\n    \"purchase_invoice_new\": {\n      \"mode\": \"auto\",\n      \"auto_resolution\": \"prospective\",\n      \"materiality_amount\": 100,\n      \"materiality_percent\": 5\n    },\n    \"purchase_invoice_edited\": {\n      \"mode\": \"auto\",\n      \"auto_resolution\": \"prospective\",\n      \"materiality_amount\": 100,\n      \"materiality_percent\": 5\n    },\n    \"po_line_edited\": {\n      \"mode\": \"auto\",\n      \"auto_resolution\": \"prospective\",\n      \"materiality_amount\": 100,\n      \"materiality_percent\": 5\n    },\n    \"po_financials_edited\": {\n      \"mode\": \"auto\",\n      \"auto_resolution\": \"prospective\",\n      \"materiality_amount\": 100,\n      \"materiality_percent\": 5\n    },\n    \"po_currency_changed\": {\n      \"mode\": \"review\",\n      \"auto_resolution\": \"prospective\",\n      \"materiality_amount\": 100,\n      \"materiality_percent\": 5\n    },\n    \"data_fix\": {\n      \"mode\": \"review\",\n      \"auto_resolution\": \"prospective\",\n      \"materiality_amount\": 100,\n      \"materiality_percent\": 5\n    }\n  },\n  \"always_review_locked_impact\": true,\n  \"always_review_closed_impact\": false,\n  \"always_review_synced_entries\": false\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/cost-changes/settings","host":["{{baseUrl}}"],"path":["api","cost-changes","settings"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"trigger_policies\": {\n      \"landed_cost_bill\": {\n        \"mode\": \"review_if_material\",\n        \"auto_resolution\": \"prospective\",\n        \"materiality_amount\": 250,\n        \"materiality_percent\": 2.5,\n        \"label\": \"Landed Cost Bill\"\n      },\n      \"purchase_invoice_new\": {\n        \"mode\": \"auto\",\n        \"auto_resolution\": \"prospective\",\n        \"materiality_amount\": 100,\n        \"materiality_percent\": 5,\n        \"label\": \"Invoice After Receipt\"\n      },\n      \"purchase_invoice_edited\": {\n        \"mode\": \"auto\",\n        \"auto_resolution\": \"prospective\",\n        \"materiality_amount\": 100,\n        \"materiality_percent\": 5,\n        \"label\": \"Invoice Edited\"\n      },\n      \"po_line_edited\": {\n        \"mode\": \"auto\",\n        \"auto_resolution\": \"prospective\",\n        \"materiality_amount\": 100,\n        \"materiality_percent\": 5,\n        \"label\": \"PO Price Edit\"\n      },\n      \"po_financials_edited\": {\n        \"mode\": \"auto\",\n        \"auto_resolution\": \"prospective\",\n        \"materiality_amount\": 100,\n        \"materiality_percent\": 5,\n        \"label\": \"PO Financials Edit\"\n      },\n      \"po_currency_changed\": {\n        \"mode\": \"review\",\n        \"auto_resolution\": \"prospective\",\n        \"materiality_amount\": 100,\n        \"materiality_percent\": 5,\n        \"label\": \"PO Currency Change\"\n      },\n      \"data_fix\": {\n        \"mode\": \"review\",\n        \"auto_resolution\": \"prospective\",\n        \"materiality_amount\": 100,\n        \"materiality_percent\": 5,\n        \"label\": \"Data Fix\"\n      }\n    },\n    \"always_review_locked_impact\": true,\n    \"always_review_closed_impact\": false,\n    \"always_review_synced_entries\": false\n  }\n}"}]}]},{"name":"Inventory Debt","item":[{"name":"List Inventory Debt Claims","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory/debt/claims","host":["{{baseUrl}}"],"path":["api","inventory","debt","claims"]},"description":"List inventory-debt claims - units that left a warehouse without stock behind them.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nEach claim is repaid automatically by subsequent receipts, oldest claim first, and the results are ordered that way by default.\n\nAuthentication: Requires Bearer token.\n\n**quantity** is what was originally owed, **outstanding_quantity** what remains, and **settled_quantity** the difference.\n\n**posted_clearing_amount** and **relieved_clearing_amount** track the money side, which reconciles independently of quantity: a claim settled in slices rounds per settlement, so `unrelieved_clearing_amount` - not `outstanding_quantity` x `estimated_unit_cost` - is the authoritative balance still carried.\n\n**estimated_unit_cost** is provisional. It is restated to the actual receipt cost when the claim settles, with the difference posted as a dated variance.\n\nUse `filter[age_bucket]` rather than a date range when triaging: stale claims are where the exposure accumulates.\n\nDEFAULT STATUS: when no filter[status] is supplied the list returns OUTSTANDING claims only. Pass filter[status]=all to include settled and cancelled claims, or a specific value (outstanding | settled | cancelled). Omitting filter[status] is the ordinary case, so it deliberately means 'still owed' rather than 'every status' - an unfiltered call returns work to be done, not history.\n\nSORTS: incurred_at, outstanding_quantity, quantity, estimated_unit_cost, posted_clearing_amount, relieved_clearing_amount, status, source, created_at, sku, warehouse_name, age_days. Prefix with '-' to reverse. Note age_days is the inverse of incurred_at (the oldest claim has the greatest age).\n\nADVANCED FILTERS: sku, product_name, warehouse_name, order_number, reason, status, source, quantity, outstanding_quantity, estimated_unit_cost, posted_clearing_amount, incurred_at - each with the standard operator suffixes.\n\nfilter[id]: an exact claim id, for linking directly to a single claim. Note that filter[status] defaults to `outstanding`, so pair it with filter[status]=all when the claim may already have settled.\n\nfilter[period]: an accounting month (YYYY-MM), returning claims incurred before that month ended. The month boundary is resolved server-side in your account's timezone, so it matches the period exactly rather than shifting by a UTC offset. Left with the default outstanding status it answers 'what debt was still owed when this period closed'."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory/debt/claims","host":["{{baseUrl}}"],"path":["api","inventory","debt","claims"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 511,\n      \"status\": \"outstanding\",\n      \"status_label\": \"Outstanding\",\n      \"source\": \"operator_elected\",\n      \"source_label\": \"Operator\",\n      \"operation\": \"sale\",\n      \"product\": {\n        \"id\": 811,\n        \"sku\": \"TT-G4-WAL\",\n        \"name\": \"Tabletop G4 - Walnut\"\n      },\n      \"warehouse\": {\n        \"id\": 3,\n        \"name\": \"Main - Los Angeles\"\n      },\n      \"sales_order\": {\n        \"id\": 918311,\n        \"sales_order_number\": \"SO-918311\"\n      },\n      \"quantity\": 2,\n      \"outstanding_quantity\": 2,\n      \"settled_quantity\": 0,\n      \"estimate_basis\": \"last_purchase_price\",\n      \"estimate_basis_label\": \"Last purchase price\",\n      \"estimated_unit_cost\": 184,\n      \"posted_clearing_amount\": 368,\n      \"relieved_clearing_amount\": 0,\n      \"unrelieved_clearing_amount\": 368,\n      \"incurred_at\": \"2026-09-05T17:12:04+00:00\",\n      \"age_days\": 23,\n      \"reason\": \"Customer escalation - replenishment PO due 10/03\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 10,\n  \"total\": 1\n}"}]},{"name":"Resolve Inventory Debt Policy","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory/debt/resolve-policy?product_id=811&warehouse_id=3","host":["{{baseUrl}}"],"path":["api","inventory","debt","resolve-policy"],"query":[{"key":"product_id","value":"811","description":"Product to evaluate. Required.","disabled":true},{"key":"warehouse_id","value":"3","description":"Warehouse to evaluate. Required.","disabled":true}]},"description":"Resolve the effective inventory-debt policy for a product at a warehouse, before attempting to ship it.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nInventory debt lets stock ship when on-hand would go below zero: the units are recorded as a real inventory movement at an estimated cost, and the resulting claim is settled automatically by the next receipt.\n\nAuthentication: Requires Bearer token.\n\n**policy** is one of:\n- `block` - the shipment is refused; the short quantity must be backordered.\n- `warn` - the shipment may proceed once acknowledged with a reason by a user holding the `inventory.debt.acknowledge` permission.\n- `allow` - the shipment proceeds and the debt is recorded without prompting.\n\n**source** says which level decided: `tenant`, `warehouse`, `product`, or `hard_exclusion`.\n\n**hard_exclusion_reason** is non-null when debt is impossible regardless of configuration - serial-tracked stock, lot or expiry-tracked stock, consigned stock, and operations that record a physical count. Present it to the user verbatim; no setting will change the outcome.\n\n**estimated_unit_cost** is the provisional cost a debt unit would carry, derived from `estimate_basis`. It is restated to the actual receipt cost when the claim settles, with the difference posted as a dated variance.\n\n**existing_outstanding_claims** reports how much this product already owes at this warehouse, so the caller can weigh adding to that exposure.\n- data.estimate_basis_used (string, nullable) - the basis that ACTUALLY produced the estimate, which is often not the preferred one\n- data.estimate_basis_used_label (string, nullable) - human label for the above\n- data.estimate_fell_back (boolean) - true when the preferred basis produced nothing and a later basis in the chain was used\n- data.estimate_chain (array of string) - the configured fallback order, most preferred first"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory/debt/resolve-policy?product_id=811&warehouse_id=3","host":["{{baseUrl}}"],"path":["api","inventory","debt","resolve-policy"],"query":[{"key":"product_id","value":"811","description":"Product to evaluate. Required.","disabled":true},{"key":"warehouse_id","value":"3","description":"Warehouse to evaluate. Required.","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"policy\": \"warn\",\n    \"policy_label\": \"Warn\",\n    \"source\": \"warehouse\",\n    \"hard_exclusion_reason\": null,\n    \"permits_debt\": true,\n    \"requires_acknowledgement\": true,\n    \"requires_reason\": true,\n    \"estimate_basis\": \"last_purchase_price\",\n    \"estimated_unit_cost\": 184,\n    \"permission\": \"inventory.debt.acknowledge\",\n    \"user_can_acknowledge\": true,\n    \"existing_outstanding_claims\": {\n      \"units\": 3,\n      \"count\": 1,\n      \"oldest_days\": 4\n    }\n  }\n}"}]},{"name":"Get Inventory Debt Claim","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/inventory/debt/claims/{inventoryDebtClaim}","host":["{{baseUrl}}"],"path":["api","inventory","debt","claims",":inventoryDebtClaim"],"variable":[{"key":"inventoryDebtClaim","value":"511","description":"Inventory debt claim ID"}]},"description":"Get a single inventory-debt claim with its settlement history.\n\n**Required scope: `inventory:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA claim records units that left a warehouse without stock behind them, and is repaid automatically by subsequent receipts, oldest claim first.\n\nAuthentication: Requires Bearer token.\n\n**quantity** is what was originally owed; **outstanding_quantity** is what remains; **settled_quantity** is the difference.\n\n**posted_clearing_amount** and **relieved_clearing_amount** track the money side, which is reconciled independently of quantity: a claim settled in slices rounds per settlement, so `unrelieved_clearing_amount` - not `outstanding_quantity` x `estimated_unit_cost` - is the authoritative balance still carried.\n\n**settlements** lists each repayment slice with the layer it drew from, the actual unit cost, and the variance recognised against the original estimate.\n\nReturns 404 if the claim does not exist."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/inventory/debt/claims/{inventoryDebtClaim}","host":["{{baseUrl}}"],"path":["api","inventory","debt","claims",":inventoryDebtClaim"],"variable":[{"key":"inventoryDebtClaim","value":"511","description":"Inventory debt claim ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 511,\n    \"status\": \"outstanding\",\n    \"status_label\": \"Outstanding\",\n    \"source\": \"operator_elected\",\n    \"source_label\": \"Operator\",\n    \"operation\": \"sale\",\n    \"product\": {\n      \"id\": 811,\n      \"sku\": \"TT-G4-WAL\",\n      \"name\": \"Tabletop G4 - Walnut\"\n    },\n    \"warehouse\": {\n      \"id\": 3,\n      \"name\": \"Main - Los Angeles\"\n    },\n    \"sales_order\": {\n      \"id\": 918311,\n      \"sales_order_number\": \"SO-918311\"\n    },\n    \"link_type\": \"App\\\\Models\\\\SalesOrderFulfillmentLine\",\n    \"link_id\": 77301,\n    \"inventory_movement_id\": 998123,\n    \"quantity\": 2,\n    \"outstanding_quantity\": 2,\n    \"settled_quantity\": 0,\n    \"estimate_basis\": \"last_purchase_price\",\n    \"estimate_basis_label\": \"Last purchase price\",\n    \"estimated_unit_cost\": 184,\n    \"posted_clearing_amount\": 368,\n    \"relieved_clearing_amount\": 0,\n    \"unrelieved_clearing_amount\": 368,\n    \"incurred_at\": \"2026-09-05T17:12:04+00:00\",\n    \"age_days\": 23,\n    \"incurred_by_user_id\": 4,\n    \"reason\": \"Customer escalation - replenishment PO due 10/03\",\n    \"approximate_date\": false,\n    \"external_shipment_key\": null,\n    \"cancelled_at\": null,\n    \"cancel_reason\": null,\n    \"settlements\": []\n  }\n}"}]},{"name":"Settle Inventory Debt Claims","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory/debt/claims/settle","host":["{{baseUrl}}"],"path":["api","inventory","debt","claims","settle"]},"description":"Settle the selected inventory debt claims against stock that has since been received.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nSettlement runs per (product, warehouse) POOL and repays strictly oldest-first, so selecting a newer claim also settles the older claims ahead of it. That ordering is deliberate: letting a newer claim jump the queue would date the cost variance wrongly.\n\nConsignment stock and layers dated after the debt was incurred are never used. Each repaid unit is restated from its provisional estimate to the layer's actual cost, and the difference posts as a dated cost variance.\n\nCall Preview Inventory Debt Settlement first to see exactly which layers would be consumed and at what cost. Where no receipt will ever repay the debt, use Create Catch-Up Stock Takes instead.\n\nRequest body:\n- claim_ids (array of integer, required, 1-200) - the claims to settle\n\nResponse 200:\n- data.settlements (integer) - settlement slices written\n- data.claims_settled (integer) - claims that moved out of Outstanding\n- data.pools (integer) - distinct product/warehouse pools run\n- data.message (string) - human summary","body":{"mode":"raw","raw":"{\n  \"claim_ids\": [\n    12,\n    13\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory/debt/claims/settle","host":["{{baseUrl}}"],"path":["api","inventory","debt","claims","settle"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"settlements\": 2,\n    \"claims_settled\": 1,\n    \"pools\": 1,\n    \"message\": \"1 claim(s) settled across 1 pool(s) in 2 slice(s).\"\n  }\n}"}]},{"name":"Preview Inventory Debt Settlement","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory/debt/claims/settle-preview","host":["{{baseUrl}}"],"path":["api","inventory","debt","claims","settle-preview"]},"description":"Read-only preview of what settling the selected inventory debt claims would consume, without consuming it.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nWalks the same date-eligible, non-consignment stock layers in the same order the settlement itself does, so the preview cannot promise one thing and the settlement do another. Each source layer also carries the positive inventory event that created it (the receipt, stock take or transfer), so the origin of the stock is traceable.\n\nNothing is written. Call Settle Inventory Debt Claims to commit.\n\nRequest body:\n- claim_ids (array of integer, required, 1-200) - the claims to preview\n\nResponse 200:\n- data.coverable_units (number) - units that would be repaid now\n- data.shortfall_units (number) - units that would stay outstanding\n- data.repayable_claims (integer) - claims with at least one covering layer\n- data.total_claims (integer) - outstanding claims in the selection\n- data.estimated_variance (number) - total cost variance that would post\n- data.claims[] (array) - per-claim breakdown\n  - claim_id (integer)\n  - product_id (integer)\n  - sku (string, nullable)\n  - warehouse_id (integer)\n  - warehouse_name (string, nullable)\n  - owed (number) - units still owed\n  - covered (number) - units this preview would repay\n  - estimated_unit_cost (number) - provisional cost carried until settlement\n  - sources[] (array) - the stock layers that would be consumed\n    - fifo_layer_id (integer)\n    - received_at (string, nullable, Y-m-d)\n    - quantity (number)\n    - unit_cost (number) - actual cost of the layer\n    - variance_per_unit (number) - actual minus estimate\n    - origin (object, nullable) - the positive inventory event that created the layer\n      - link_type (string, nullable)\n      - link_id (integer, nullable)\n      - name (string, nullable)\n      - parent_link_id (integer, nullable)\n      - detail_link_id (integer, nullable)\n      - reference (string, nullable)\n- data.claims[].product_id (integer) and data.claims[].warehouse_id (integer) - so the caller can link the claim's product and warehouse\n\n`selected_claims` is everything the caller asked about; `total_claims` counts only the OUTSTANDING ones, which are the only claims a settle run can move. `already_settled_claims` is the difference attributable to claims that were already repaid in full. Report those separately: a selection made entirely of settled claims returns `coverable_units: 0`, which is indistinguishable from \"no stock has landed\" unless you read `already_settled_claims`.","body":{"mode":"raw","raw":"{\n  \"claim_ids\": [\n    12,\n    13\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory/debt/claims/settle-preview","host":["{{baseUrl}}"],"path":["api","inventory","debt","claims","settle-preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"coverable_units\": 3,\n    \"shortfall_units\": 2,\n    \"repayable_claims\": 1,\n    \"total_claims\": 2,\n    \"selected_claims\": 3,\n    \"already_settled_claims\": 1,\n    \"estimated_variance\": 15,\n    \"claims\": [\n      {\n        \"claim_id\": 12,\n        \"product_id\": 811,\n        \"sku\": \"TT-G4-BLK\",\n        \"warehouse_id\": 3,\n        \"warehouse_name\": \"Main Warehouse\",\n        \"owed\": 5,\n        \"covered\": 3,\n        \"estimated_unit_cost\": 10,\n        \"sources\": [\n          {\n            \"fifo_layer_id\": 4471,\n            \"received_at\": \"2026-09-02\",\n            \"quantity\": 3,\n            \"unit_cost\": 15,\n            \"variance_per_unit\": 5,\n            \"origin\": {\n              \"link_type\": \"App\\\\Models\\\\InboundShipmentReceiptLine\",\n              \"link_id\": 902,\n              \"name\": \"inbound_shipment_receipt_line\",\n              \"parent_link_id\": 145,\n              \"detail_link_id\": 61,\n              \"reference\": \"PO-1042\"\n            }\n          }\n        ]\n      }\n    ]\n  }\n}"}]},{"name":"Preview Catch-Up Stock Takes","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory/debt/claims/stock-take-preview","host":["{{baseUrl}}"],"path":["api","inventory","debt","claims","stock-take-preview"]},"description":"Read-only preview of the catch-up stock takes that Create Catch-Up Stock Takes would raise, without raising them.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nA stock take cannot span warehouses, so one catch-up run fans out into ONE STOCK TAKE PER WAREHOUSE: a selection covering three warehouses produces three separate counts to review and finalise. This endpoint reports that fan-out up front, along with the exact product lines and quantities each count would carry.\n\nNothing is written. The preview and the create action share the same planning logic, so what is described here is what gets raised.\n\nRequest body:\n- claim_ids (array of integer, required, 1-200) - the claims to preview\n\nResponse 200:\n- data.warehouse_count (integer) - stock takes that would be created\n- data.claim_count (integer) - outstanding claims in the selection\n- data.total_units (number) - units that would be counted in overall\n- data.stock_takes[] (array) - one entry per warehouse\n  - warehouse_id (integer)\n  - warehouse_name (string, nullable)\n  - claim_count (integer) - claims rolled into this count\n  - line_count (integer) - distinct products on this count\n  - total_units (number) - units counted in at this warehouse\n  - lines[] (array)\n    - product_id (integer)\n    - sku (string, nullable)\n    - product_name (string, nullable)\n    - quantity (number) - positive; brings the owed units into stock\n\nResponse 422 when none of the selected claims are outstanding, so there is nothing to count in.","body":{"mode":"raw","raw":"{\n  \"claim_ids\": [\n    12,\n    13\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory/debt/claims/stock-take-preview","host":["{{baseUrl}}"],"path":["api","inventory","debt","claims","stock-take-preview"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"warehouse_count\": 2,\n    \"claim_count\": 2,\n    \"total_units\": 5,\n    \"stock_takes\": [\n      {\n        \"warehouse_id\": 3,\n        \"warehouse_name\": \"Main Warehouse\",\n        \"claim_count\": 1,\n        \"line_count\": 1,\n        \"total_units\": 3,\n        \"lines\": [\n          {\n            \"product_id\": 811,\n            \"sku\": \"TT-G4-BLK\",\n            \"product_name\": \"Titan Grip G4 - Black\",\n            \"quantity\": 3\n          }\n        ]\n      },\n      {\n        \"warehouse_id\": 7,\n        \"warehouse_name\": \"East Coast DC\",\n        \"claim_count\": 1,\n        \"line_count\": 1,\n        \"total_units\": 2,\n        \"lines\": [\n          {\n            \"product_id\": 904,\n            \"sku\": \"AX-220\",\n            \"product_name\": \"Apex 220 Cable\",\n            \"quantity\": 2\n          }\n        ]\n      }\n    ]\n  }\n}"}]},{"name":"Create Catch-Up Stock Takes","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/inventory/debt/claims/create-stock-take","host":["{{baseUrl}}"],"path":["api","inventory","debt","claims","create-stock-take"]},"description":"Raise DRAFT catch-up stock takes for the outstanding units on the selected inventory debt claims.\n\n**Required scope: `inventory:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nUse this for debt no incoming receipt will ever repay - a receiving error, or stock that was on the shelf all along. The count is POSITIVE: it brings the owed units into stock so the debt can be repaid from them. The claim's own movement already took those units out, so counting down again would deduct them twice.\n\nThis does NOT settle anything by itself. Each stock take is created as a draft; finalising it lands the stock and the ordinary settlement then repays the claims from it, exactly as a real receipt would. One settlement path, not two.\n\nA stock take cannot span warehouses, so one call creates one stock take per warehouse in the selection. Call Preview Catch-Up Stock Takes first to see that fan-out before committing.\n\nRequest body:\n- claim_ids (array of integer, required, 1-200) - the claims to count in\n\nResponse 200:\n- data.stock_takes[] (array) - the drafts created\n  - id (integer)\n  - warehouse_id (integer, nullable)\n  - warehouse_name (string, nullable)\n  - item_count (integer) - product lines on the draft\n  - status (string) - draft\n- data.message (string) - human summary\n\nResponse 422 when none of the selected claims are outstanding.","body":{"mode":"raw","raw":"{\n  \"claim_ids\": [\n    12,\n    13\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/inventory/debt/claims/create-stock-take","host":["{{baseUrl}}"],"path":["api","inventory","debt","claims","create-stock-take"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"stock_takes\": [\n      {\n        \"id\": 3391,\n        \"warehouse_id\": 3,\n        \"warehouse_name\": \"Main Warehouse\",\n        \"item_count\": 1,\n        \"status\": \"draft\"\n      },\n      {\n        \"id\": 3392,\n        \"warehouse_id\": 7,\n        \"warehouse_name\": \"East Coast DC\",\n        \"item_count\": 1,\n        \"status\": \"draft\"\n      }\n    ],\n    \"message\": \"2 draft stock take(s) created. Review and finalise to land the stock - the claims then settle from it automatically.\"\n  }\n}"}]}]},{"name":"Landed Cost Breakdown","item":[{"name":"Generate Landed Cost Breakdown Report","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/reporting/landed-cost-breakdown","host":["{{baseUrl}}"],"path":["api","reporting","landed-cost-breakdown"]},"description":"Dispatch a filtered, hierarchically-grouped report (brand > product) restricted to a specific sales channel and warehouse. Returns a `tracked_job_log_id`; fetch the payload from the result endpoint once the job completes.\n\n**Required scope: `reports:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nDemonstrates hierarchical grouping combined with filtering — useful for drilling into specific segments of the business.","body":{"mode":"raw","raw":"{\n  \"date_from\": \"2025-01-01\",\n  \"date_to\": \"2025-12-31\",\n  \"group_by\": [\n    \"brand\",\n    \"product\"\n  ],\n  \"sales_channel_ids\": [\n    1\n  ],\n  \"warehouse_ids\": [\n    1\n  ]\n}","options":{"raw":{"language":"json"}}}}},{"name":"Get Generated Report Result","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/landed-cost-breakdown/result/{trackedJobLog}","host":["{{baseUrl}}"],"path":["api","reporting","landed-cost-breakdown","result",":trackedJobLog"],"variable":[{"key":"trackedJobLog","value":"4821","description":"The tracked_job_log_id returned by the generate endpoint."}]},"description":"Fetch the generated landed cost breakdown payload for a completed generation job. Call this once the tracked job (from the generate endpoint) reaches `completed`.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Response structure:**\n- `group_by` — the grouping dimensions used.\n- `date_range` — `{ from, to }` for the report.\n- `categories` — the cost categories present in the report (`id`, `name`, `is_system`).\n- `groups` — hierarchical breakdown by the grouping dimensions, each with `totals` and nested `children`.\n- `grand_totals` — aggregate totals across all groups.\n\n**Status codes:**\n- `200` — payload returned.\n- `403` — the authenticated user did not generate this report.\n- `404` — the report is not ready yet (job still running) or the cached payload has expired (~1 hour TTL). Regenerate.\n\n**Authentication:** Requires a bearer token. Scoped to the user who generated the report."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/reporting/landed-cost-breakdown/result/{trackedJobLog}","host":["{{baseUrl}}"],"path":["api","reporting","landed-cost-breakdown","result",":trackedJobLog"],"variable":[{"key":"trackedJobLog","value":"4821","description":"The tracked_job_log_id returned by the generate endpoint."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"group_by\": [\n    \"sales_channel\"\n  ],\n  \"date_range\": {\n    \"from\": \"2025-01-01\",\n    \"to\": \"2025-12-31\"\n  },\n  \"categories\": [\n    {\n      \"id\": 1,\n      \"name\": \"Base Product Cost\",\n      \"is_system\": true\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Freight\",\n      \"is_system\": true\n    },\n    {\n      \"id\": 3,\n      \"name\": \"Customs Duty\",\n      \"is_system\": true\n    }\n  ],\n  \"groups\": [\n    {\n      \"key\": \"Amazon US\",\n      \"totals\": {\n        \"quantity_sold\": 1250,\n        \"base_product\": 45230,\n        \"freight\": 8540.25,\n        \"customs_duty\": 2150,\n        \"brokerage\": 425,\n        \"insurance\": 312.5,\n        \"handling\": 625,\n        \"taxes\": 4523,\n        \"other_costs\": 180.75,\n        \"total_landed_cost\": 61986.5\n      },\n      \"children\": []\n    },\n    {\n      \"key\": \"Shopify\",\n      \"totals\": {\n        \"quantity_sold\": 850,\n        \"base_product\": 28750,\n        \"freight\": 4250,\n        \"customs_duty\": 0,\n        \"brokerage\": 0,\n        \"insurance\": 187.5,\n        \"handling\": 425,\n        \"taxes\": 2875,\n        \"other_costs\": 95.25,\n        \"total_landed_cost\": 36582.75\n      },\n      \"children\": []\n    }\n  ],\n  \"grand_totals\": {\n    \"quantity_sold\": 2100,\n    \"base_product\": 73980,\n    \"freight\": 12790.25,\n    \"customs_duty\": 2150,\n    \"brokerage\": 425,\n    \"insurance\": 500,\n    \"handling\": 1050,\n    \"taxes\": 7398,\n    \"other_costs\": 276,\n    \"total_landed_cost\": 98569.25\n  }\n}"}]},{"name":"Export Landed Cost Breakdown (CSV)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/reporting/landed-cost-breakdown/export?date_from=2025-01-01&date_to=2025-12-31&group_by[]=sales_channel","host":["{{baseUrl}}"],"path":["api","reporting","landed-cost-breakdown","export"],"query":[{"key":"date_from","value":"2025-01-01","description":"Start date for the report","disabled":true},{"key":"date_to","value":"2025-12-31","description":"End date for the report","disabled":true},{"key":"group_by[]","value":"sales_channel","description":"First grouping level","disabled":true}]},"description":"Export a detailed breakdown with multiple grouping levels as CSV. The CSV includes a column for each grouping dimension (Sales Channel, Brand, Product) followed by all cost breakdown columns. Ideal for pivot-table analysis in a spreadsheet.\n\n**Required scope: `reports:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."}}]},{"name":"Loyalty","item":[{"name":"Get Loyalty Balance","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/loyalty","host":["{{baseUrl}}"],"path":["api","customers",":customer","loyalty"],"variable":[{"key":"customer","value":"842","description":"The customer id."}]},"description":"The customer's current loyalty points balance and its dollar redemption value, plus whether the loyalty program is enabled.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (PAT).\nPermission: authenticated user (no extra permission required for read).\n\nReturns 200 with `{ data: { customer_id, points_balance, redemption_value, program_enabled } }`:\n- `points_balance` (integer) — the running sum of the customer's loyalty ledger.\n- `redemption_value` (float) — the dollar value of that balance at the current redemption rate.\n- `program_enabled` (boolean) — whether the loyalty program is active for the tenant."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/{customer}/loyalty","host":["{{baseUrl}}"],"path":["api","customers",":customer","loyalty"],"variable":[{"key":"customer","value":"842","description":"The customer id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"customer_id\": 842,\n    \"points_balance\": 1250,\n    \"redemption_value\": 12.5,\n    \"program_enabled\": true\n  }\n}"}]},{"name":"Get Loyalty History","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/loyalty/history","host":["{{baseUrl}}"],"path":["api","customers",":customer","loyalty","history"],"variable":[{"key":"customer","value":"842","description":"The customer id."}]},"description":"Paginated loyalty ledger history for a customer, newest first. Each entry records a signed points movement, the resulting balance, why it was written, and who created it.\n\n**Required scope: `customers:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (PAT).\nPermission: authenticated user (no extra permission required for read).\n\nEntry `type` values: `earn`, `redemption`, `adjustment`, `expiry`, `reversal`. `is_earn` is true when the entry adds points (points > 0).\n\nPagination: `per_page` (default 10), `page` (default 1). Response includes standard Laravel pagination metadata."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/customers/{customer}/loyalty/history","host":["{{baseUrl}}"],"path":["api","customers",":customer","loyalty","history"],"variable":[{"key":"customer","value":"842","description":"The customer id."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 512,\n      \"customer_id\": 842,\n      \"reference\": \"LP-000512\",\n      \"points\": -300,\n      \"balance_after\": 1250,\n      \"type\": \"redemption\",\n      \"type_label\": \"Redeemed\",\n      \"is_earn\": false,\n      \"notes\": null,\n      \"expires_at\": null,\n      \"source_type\": \"App\\\\Models\\\\PosTransaction\",\n      \"source_id\": 1001,\n      \"created_by_user_id\": 7,\n      \"created_by_name\": \"Jane Cashier\",\n      \"created_at\": \"2026-07-06T15:10:00+00:00\"\n    },\n    {\n      \"id\": 508,\n      \"customer_id\": 842,\n      \"reference\": \"LP-000508\",\n      \"points\": 50,\n      \"balance_after\": 1550,\n      \"type\": \"earn\",\n      \"type_label\": \"Earned\",\n      \"is_earn\": true,\n      \"notes\": null,\n      \"expires_at\": \"2027-07-06T00:00:00+00:00\",\n      \"source_type\": \"App\\\\Models\\\\PosTransaction\",\n      \"source_id\": 998,\n      \"created_by_user_id\": 7,\n      \"created_by_name\": \"Jane Cashier\",\n      \"created_at\": \"2026-07-06T14:45:00+00:00\"\n    },\n    {\n      \"id\": 500,\n      \"customer_id\": 842,\n      \"reference\": \"LP-000500\",\n      \"points\": 500,\n      \"balance_after\": 1500,\n      \"type\": \"adjustment\",\n      \"type_label\": \"Adjustment\",\n      \"is_earn\": true,\n      \"notes\": \"Goodwill points for a delayed order.\",\n      \"expires_at\": null,\n      \"source_type\": null,\n      \"source_id\": null,\n      \"created_by_user_id\": 7,\n      \"created_by_name\": \"Jane Cashier\",\n      \"created_at\": \"2026-07-06T14:30:00+00:00\"\n    }\n  ],\n  \"current_page\": 1,\n  \"first_page_url\": \"{{protocol}}{{domain}}/api/customers/842/loyalty/history?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"{{protocol}}{{domain}}/api/customers/842/loyalty/history?page=1\",\n  \"next_page_url\": null,\n  \"path\": \"{{protocol}}{{domain}}/api/customers/842/loyalty/history\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 3,\n  \"total\": 3\n}"}]},{"name":"Adjust Loyalty Points","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/customers/{customer}/loyalty/adjust","host":["{{baseUrl}}"],"path":["api","customers",":customer","loyalty","adjust"],"variable":[{"key":"customer","value":"842","description":"The customer id."}]},"description":"Manually add or remove loyalty points for a customer. Writes a signed `adjustment` ledger entry — a positive `points` value adds, a negative value removes.\n\n**Required scope: `customers:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token (PAT).\nPermission: `customers.update`.\n\nRequest body fields:\n- `points` (integer, required, `not_in:0`) — signed points to apply. Positive adds, negative removes. Error message when 0: \"The points adjustment must be non-zero.\"\n- `notes` (string, nullable, max 2000) — internal note for the adjustment.\n\nA negative adjustment that would drive the balance below zero is rejected with 422 (`InsufficientLoyaltyPointsException`).\n\nReturns 201 with `{ data: <ledger entry>, points_balance: <int>, message: \"Loyalty points adjusted.\" }`.","body":{"mode":"raw","raw":"{\n  \"points\": 500,\n  \"notes\": \"Goodwill points for a delayed order.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/customers/{customer}/loyalty/adjust","host":["{{baseUrl}}"],"path":["api","customers",":customer","loyalty","adjust"],"variable":[{"key":"customer","value":"842","description":"The customer id."}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 500,\n    \"customer_id\": 842,\n    \"reference\": \"LP-000500\",\n    \"points\": 500,\n    \"balance_after\": 1500,\n    \"type\": \"adjustment\",\n    \"type_label\": \"Adjustment\",\n    \"is_earn\": true,\n    \"notes\": \"Goodwill points for a delayed order.\",\n    \"expires_at\": null,\n    \"source_type\": null,\n    \"source_id\": null,\n    \"created_by_user_id\": 7,\n    \"created_by_name\": \"Jane Cashier\",\n    \"created_at\": \"2026-07-06T14:30:00+00:00\"\n  },\n  \"points_balance\": 1500,\n  \"message\": \"Loyalty points adjusted.\"\n}"}]},{"name":"Get Loyalty Settings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/loyalty/settings","host":["{{baseUrl}}"],"path":["api","loyalty","settings"]},"description":"The tenant's loyalty program configuration.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token (PAT).\nPermission: `pos.operate`.\n\nReturns 200 with `{ data: { enabled, points_per_dollar, redemption_value_per_point, min_redemption_points } }`:\n- `enabled` (boolean) — whether the program is active (default false).\n- `points_per_dollar` (float) — points earned per $1 of sale total before tax (default 1.0).\n- `redemption_value_per_point` (float) — dollar value of one point when redeemed (default 0.01).\n- `min_redemption_points` (integer) — minimum points a customer must hold before any can be redeemed (default 0)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/loyalty/settings","host":["{{baseUrl}}"],"path":["api","loyalty","settings"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"enabled\": true,\n    \"points_per_dollar\": 1,\n    \"redemption_value_per_point\": 0.01,\n    \"min_redemption_points\": 100\n  }\n}"}]},{"name":"Update Loyalty Settings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/loyalty/settings","host":["{{baseUrl}}"],"path":["api","loyalty","settings"]},"description":"Update the tenant's loyalty program configuration. All fields are required and fully replace the stored settings.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthentication: Requires Bearer token (PAT).\nPermission: `pos.manage`.\n\nRequest body fields (all required):\n- `enabled` (boolean, required) — whether the program is active.\n- `points_per_dollar` (numeric, required, min 0) — points earned per $1 of sale total before tax.\n- `redemption_value_per_point` (numeric, required, min 0) — dollar value of one point when redeemed.\n- `min_redemption_points` (integer, required, min 0) — minimum points required before redemption.\n\nReturns 200 with `{ data: { ...settings }, message: \"Loyalty settings updated.\" }`.","body":{"mode":"raw","raw":"{\n  \"enabled\": true,\n  \"points_per_dollar\": 1,\n  \"redemption_value_per_point\": 0.01,\n  \"min_redemption_points\": 100\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/loyalty/settings","host":["{{baseUrl}}"],"path":["api","loyalty","settings"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"enabled\": true,\n    \"points_per_dollar\": 1,\n    \"redemption_value_per_point\": 0.01,\n    \"min_redemption_points\": 100\n  },\n  \"message\": \"Loyalty settings updated.\"\n}"}]}]},{"name":"OAuth","item":[{"name":"OAuth Callback","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/x-www-form-urlencoded"}],"url":{"raw":"{{baseUrl}}/api/magento2/oauth/callback","host":["{{baseUrl}}"],"path":["api","magento2","oauth","callback"]},"description":"**Public, unauthenticated endpoint** — Magento Admin POSTs the OAuth 1.0a activation payload here when the merchant clicks **Activate** on a Magento Integration.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Flow on success:**\n1. We immediately return `200 { success: true, oauth_verifier: ... }` so Magento marks the integration Active.\n2. We then asynchronously POST to `{store_base_url}/oauth/token/request` to get a temporary request token, and `{store_base_url}/oauth/token/access` (with the verifier) to upgrade it into the permanent access token + secret. This deferred-exchange pattern (via `fastcgi_finish_request()` or `register_shutdown_function`) avoids deadlocking Magento's PHP-FPM worker pool — Magento's own Laminas HTTP client expects to read our response before it can serve our outbound OAuth calls.\n3. Structured outcomes (success/failure + last-4-of-token) are logged to `laravel.log` so install operations can be debugged.\n\n**Body parameters** (form-urlencoded, as Magento Admin POSTs):\n- `oauth_consumer_key` (required, string) — Magento Integration consumer key.\n- `oauth_consumer_secret` (required, string) — Magento Integration consumer secret.\n- `oauth_verifier` (required, string) — one-time verifier for the token exchange.\n- `store_base_url` (required, string) — Magento storefront URL.\n\n**Behavior:**\n- If any field is missing, the endpoint still returns HTTP 200 (so Magento Admin doesn't surface a misleading 'post to consumer failed' error) but with `success: false` and a message; the OAuth exchange is skipped.\n- The endpoint never throws — exchange failures are logged but not propagated to the response (Magento has already left the conversation by then).\n\n**Authentication:** None — this is a public webhook. CSRF is bypassed by the wildcard `*/callback` rule in `bootstrap/app.php`.","body":{"mode":"raw","raw":"{\n  \"oauth_consumer_key\": \"abc123def456ghi789jkl012\",\n  \"oauth_consumer_secret\": \"mno345pqr678stu901vwx234\",\n  \"oauth_verifier\": \"yz567abc890def123ghi456\",\n  \"store_base_url\": \"https://shop.example.com\"\n}","options":{"raw":{"language":"text"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/oauth/callback","host":["{{baseUrl}}"],"path":["api","magento2","oauth","callback"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"success\": true,\n  \"oauth_verifier\": \"yz567abc890def123ghi456\"\n}"}]}]},{"name":"Transactions","item":[{"name":"List Transactions","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/transactions","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","transactions"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"}]},"description":"Paginated UNION of invoices + credit memos. Each row carries a `type` discriminator and a `magento_admin_url` deep-link that points at the correct admin section (invoice vs credit memo) on the storefront.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Path params:**\n- `magento2IntegrationInstance` (required, integer) — Magento 2 IntegrationInstance ID.\n\n**Notes:**\n- Implementation uses an in-memory merge of the two Eloquent queries (suitable for typical merchant volumes). For high-volume tenants the repository will swap to `DB::query()->fromSub(...->unionAll(...))` without changing the API contract.\n- The order is eager-loaded so the cell renderer doesn't N+1.\n- Sort is fixed at `magento_created_at DESC` for now (the merged paginator doesn't yet honour Spatie's `sort` param)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/transactions","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","transactions"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 803,\n      \"integration_instance_id\": 42,\n      \"magento_entity_id\": 5034,\n      \"magento_increment_id\": \"1000005034\",\n      \"type\": \"invoice\",\n      \"state\": \"paid\",\n      \"grand_total\": 198.45,\n      \"currency\": \"USD\",\n      \"payment_method\": \"stripe_payments\",\n      \"order_id\": 921,\n      \"order\": {\n        \"id\": 921,\n        \"magento_entity_id\": 700912,\n        \"magento_increment_id\": \"100000921\",\n        \"customer_email\": \"buyer@example.com\"\n      },\n      \"is_pushed_to_accounting\": false,\n      \"pushed_to_accounting_at\": null,\n      \"is_reconciled\": false,\n      \"reconciled_at\": null,\n      \"accounting_sync_enabled\": true,\n      \"magento_admin_url\": \"https://shop.example.com/admin/sales/invoice/view/invoice_id/5034\",\n      \"magento_created_at\": \"2026-06-10T18:01:11+00:00\",\n      \"magento_updated_at\": null,\n      \"created_at\": \"2026-06-10T18:02:01+00:00\",\n      \"updated_at\": \"2026-06-10T18:02:01+00:00\"\n    }\n  ],\n  \"first_page_url\": \"...?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"...?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://sku-io-test/api/magento2/integration-instances/42/transactions\",\n  \"per_page\": 10,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"Get Transaction","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/transactions/{txn}?type=invoice","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","transactions",":txn"],"query":[{"key":"type","value":"invoice","description":"Discriminator — one of: invoice, credit_memo. Default: invoice.","disabled":true}],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"},{"key":"txn","value":"803","description":"Internal magento2_invoices.id or magento2_credit_memos.id (disambiguated by `type` query param)"}]},"description":"Single transaction detail. The `{txn}` id can refer to either a `magento2_invoices.id` or a `magento2_credit_memos.id`; the `type` query string disambiguates.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAdds (vs the list row): extended order context (subtotals, tax, discounts, shipping), `fees[]`, `total_fees` / `total_fee_tax`, and `raw_magento_payload` (the exact body Magento returned).\n\n**Error responses:**\n- `404` — id does not exist for the supplied `type` in this instance.\n- `401` — caller is not authenticated."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/transactions/{txn}?type=invoice","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","transactions",":txn"],"query":[{"key":"type","value":"invoice","description":"Discriminator — one of: invoice, credit_memo. Default: invoice.","disabled":true}],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"},{"key":"txn","value":"803","description":"Internal magento2_invoices.id or magento2_credit_memos.id (disambiguated by `type` query param)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 803,\n    \"integration_instance_id\": 42,\n    \"magento_entity_id\": 5034,\n    \"magento_increment_id\": \"1000005034\",\n    \"type\": \"invoice\",\n    \"state\": \"paid\",\n    \"grand_total\": 198.45,\n    \"currency\": \"USD\",\n    \"payment_method\": \"stripe_payments\",\n    \"order_id\": 921,\n    \"order\": {\n      \"id\": 921,\n      \"magento_entity_id\": 700912,\n      \"magento_increment_id\": \"100000921\",\n      \"customer_email\": \"buyer@example.com\"\n    },\n    \"is_pushed_to_accounting\": false,\n    \"pushed_to_accounting_at\": null,\n    \"is_reconciled\": false,\n    \"reconciled_at\": null,\n    \"accounting_sync_enabled\": true,\n    \"magento_admin_url\": \"https://shop.example.com/admin/sales/invoice/view/invoice_id/5034\",\n    \"magento_created_at\": \"2026-06-10T18:01:11+00:00\",\n    \"magento_updated_at\": null,\n    \"created_at\": \"2026-06-10T18:02:01+00:00\",\n    \"updated_at\": \"2026-06-10T18:02:01+00:00\",\n    \"fees\": [],\n    \"total_fees\": 0,\n    \"total_fee_tax\": 0,\n    \"raw_magento_payload\": {\n      \"entity_id\": 5034,\n      \"increment_id\": \"1000005034\",\n      \"grand_total\": 198.45\n    }\n  }\n}"}]},{"name":"Get Transaction Filter Options","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/transactions/filter-options","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","transactions","filter-options"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"}]},"description":"Aggregated dropdown options for the transactions filter panel — currencies (pulled live from the linked orders), per-type counts, and per-state counts. Empty / never-synced instances surface empty lists.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/transactions/filter-options","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","transactions","filter-options"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"currencies\": [\n      \"USD\",\n      \"EUR\"\n    ],\n    \"types\": [\n      {\n        \"value\": \"invoice\",\n        \"label\": \"Invoice\",\n        \"count\": 38\n      },\n      {\n        \"value\": \"credit_memo\",\n        \"label\": \"Credit Memo\",\n        \"count\": 4\n      }\n    ],\n    \"states\": [\n      {\n        \"value\": \"paid\",\n        \"label\": \"Paid\",\n        \"count\": 32\n      },\n      {\n        \"value\": \"open\",\n        \"label\": \"Open\",\n        \"count\": 6\n      },\n      {\n        \"value\": \"refunded\",\n        \"label\": \"Refunded\",\n        \"count\": 4\n      }\n    ]\n  }\n}"}]},{"name":"Refresh Transactions","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/transactions/refresh","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","transactions","refresh"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"}]},"description":"Dispatch the tracked job that refreshes invoices + credit memos for the instance. Returns the `tracked_job_log_id` so the frontend can pivot the global JobProgressTray to it. Job name prefix is `Refresh Magento 2 Transactions: ` — the page's `lastCompletedJob` watcher matches on this prefix.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPhase 2C scope: dispatch harness only — the actual `/V1/invoices` + `/V1/creditmemo` walking lands in Phase 2D."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/transactions/refresh","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","transactions","refresh"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"tracked_job_log_id\": 91901\n  },\n  \"message\": \"Transaction sync queued.\"\n}"}]},{"name":"Refresh Single Transaction","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/transactions/{txn}/refresh?type=invoice","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","transactions",":txn","refresh"],"query":[{"key":"type","value":"invoice","description":"Discriminator — one of: invoice, credit_memo. Default: invoice.","disabled":true}],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"},{"key":"txn","value":"803","description":"Internal magento2_invoices.id or magento2_credit_memos.id (disambiguated by `type` query param)"}]},"description":"Re-fetch a single transaction from Magento — typically used when the merchant reconciled / captured / refunded the row in Magento admin and wants SKU.io to pick up the change without a full refresh.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nCurrently a no-op stub (Phase 2C) — the row's `updated_at` is touched so downstream watchers see activity. Real refresh lands in Phase 2D."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/transactions/{txn}/refresh?type=invoice","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","transactions",":txn","refresh"],"query":[{"key":"type","value":"invoice","description":"Discriminator — one of: invoice, credit_memo. Default: invoice.","disabled":true}],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"},{"key":"txn","value":"803","description":"Internal magento2_invoices.id or magento2_credit_memos.id (disambiguated by `type` query param)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 803,\n    \"integration_instance_id\": 42,\n    \"magento_entity_id\": 5034,\n    \"magento_increment_id\": \"1000005034\",\n    \"type\": \"invoice\",\n    \"state\": \"paid\",\n    \"grand_total\": 198.45,\n    \"currency\": \"USD\",\n    \"payment_method\": \"stripe_payments\",\n    \"order_id\": 921,\n    \"order\": {\n      \"id\": 921,\n      \"magento_entity_id\": 700912,\n      \"magento_increment_id\": \"100000921\",\n      \"customer_email\": \"buyer@example.com\"\n    },\n    \"is_pushed_to_accounting\": false,\n    \"pushed_to_accounting_at\": null,\n    \"is_reconciled\": false,\n    \"reconciled_at\": null,\n    \"accounting_sync_enabled\": true,\n    \"magento_admin_url\": \"https://shop.example.com/admin/sales/invoice/view/invoice_id/5034\",\n    \"magento_created_at\": \"2026-06-10T18:01:11+00:00\",\n    \"magento_updated_at\": null,\n    \"created_at\": \"2026-06-10T18:02:01+00:00\",\n    \"updated_at\": \"2026-06-10T18:02:01+00:00\",\n    \"fees\": [],\n    \"total_fees\": 0,\n    \"total_fee_tax\": 0,\n    \"raw_magento_payload\": {\n      \"entity_id\": 5034,\n      \"increment_id\": \"1000005034\",\n      \"grand_total\": 198.45\n    }\n  },\n  \"message\": \"Transaction refresh queued.\"\n}"}]},{"name":"Push Transaction to Accounting","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/transactions/{txn}/push-to-accounting?type=invoice","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","transactions",":txn","push-to-accounting"],"query":[{"key":"type","value":"invoice","description":"Discriminator — one of: invoice, credit_memo. Default: invoice.","disabled":true}],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"},{"key":"txn","value":"803","description":"Internal magento2_invoices.id or magento2_credit_memos.id (disambiguated by `type` query param)"}]},"description":"Push the transaction to the configured accounting integration (QuickBooks Online / Xero / etc.). Synchronous — returns the updated row inline. Repeated calls overwrite `pushed_to_accounting_at` so the UI can show 'pushed N minutes ago' deterministically.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPhase 2C scope: timestamps the row only. Phase 2D will wire the real accounting-connector dispatch."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/transactions/{txn}/push-to-accounting?type=invoice","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","transactions",":txn","push-to-accounting"],"query":[{"key":"type","value":"invoice","description":"Discriminator — one of: invoice, credit_memo. Default: invoice.","disabled":true}],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"},{"key":"txn","value":"803","description":"Internal magento2_invoices.id or magento2_credit_memos.id (disambiguated by `type` query param)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 803,\n    \"integration_instance_id\": 42,\n    \"magento_entity_id\": 5034,\n    \"magento_increment_id\": \"1000005034\",\n    \"type\": \"invoice\",\n    \"state\": \"paid\",\n    \"grand_total\": 198.45,\n    \"currency\": \"USD\",\n    \"payment_method\": \"stripe_payments\",\n    \"order_id\": 921,\n    \"order\": {\n      \"id\": 921,\n      \"magento_entity_id\": 700912,\n      \"magento_increment_id\": \"100000921\",\n      \"customer_email\": \"buyer@example.com\"\n    },\n    \"is_pushed_to_accounting\": true,\n    \"pushed_to_accounting_at\": \"2026-06-16T07:30:00+00:00\",\n    \"is_reconciled\": false,\n    \"reconciled_at\": null,\n    \"accounting_sync_enabled\": true,\n    \"magento_admin_url\": \"https://shop.example.com/admin/sales/invoice/view/invoice_id/5034\",\n    \"magento_created_at\": \"2026-06-10T18:01:11+00:00\",\n    \"magento_updated_at\": null,\n    \"created_at\": \"2026-06-10T18:02:01+00:00\",\n    \"updated_at\": \"2026-06-10T18:02:01+00:00\",\n    \"fees\": [],\n    \"total_fees\": 0,\n    \"total_fee_tax\": 0,\n    \"raw_magento_payload\": {\n      \"entity_id\": 5034,\n      \"increment_id\": \"1000005034\",\n      \"grand_total\": 198.45\n    }\n  },\n  \"message\": \"Transaction pushed to accounting.\"\n}"}]},{"name":"Toggle Reconciliation","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/transactions/{txn}/reconcile","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","transactions",":txn","reconcile"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"},{"key":"txn","value":"803","description":"Internal magento2_invoices.id or magento2_credit_memos.id (disambiguated by `type` query param)"}]},"description":"Toggle the manual reconciliation flag on the transaction. The note is optional and persists alongside the timestamp; setting `is_reconciled: false` clears both fields.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Body params:**\n- `is_reconciled` (required, boolean) — true to set, false to clear.\n- `note` (optional, string, max 1000) — free-text reconciliation note.\n- `type` (optional, string) — discriminator. One of: invoice, credit_memo. Default: invoice.\n\n**Error responses:**\n- `404` — id does not exist for the supplied `type` in this instance.\n- `422` — body validation failed.","body":{"mode":"raw","raw":"{\n  \"is_reconciled\": true,\n  \"note\": \"Matched to QBO entry #4810\",\n  \"type\": \"invoice\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/transactions/{txn}/reconcile","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","transactions",":txn","reconcile"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID"},{"key":"txn","value":"803","description":"Internal magento2_invoices.id or magento2_credit_memos.id (disambiguated by `type` query param)"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 803,\n    \"integration_instance_id\": 42,\n    \"magento_entity_id\": 5034,\n    \"magento_increment_id\": \"1000005034\",\n    \"type\": \"invoice\",\n    \"state\": \"paid\",\n    \"grand_total\": 198.45,\n    \"currency\": \"USD\",\n    \"payment_method\": \"stripe_payments\",\n    \"order_id\": 921,\n    \"order\": {\n      \"id\": 921,\n      \"magento_entity_id\": 700912,\n      \"magento_increment_id\": \"100000921\",\n      \"customer_email\": \"buyer@example.com\"\n    },\n    \"is_pushed_to_accounting\": false,\n    \"pushed_to_accounting_at\": null,\n    \"is_reconciled\": true,\n    \"reconciled_at\": \"2026-06-16T07:31:00+00:00\",\n    \"accounting_sync_enabled\": true,\n    \"magento_admin_url\": \"https://shop.example.com/admin/sales/invoice/view/invoice_id/5034\",\n    \"magento_created_at\": \"2026-06-10T18:01:11+00:00\",\n    \"magento_updated_at\": null,\n    \"created_at\": \"2026-06-10T18:02:01+00:00\",\n    \"updated_at\": \"2026-06-10T18:02:01+00:00\",\n    \"fees\": [],\n    \"total_fees\": 0,\n    \"total_fee_tax\": 0,\n    \"raw_magento_payload\": {\n      \"entity_id\": 5034,\n      \"increment_id\": \"1000005034\",\n      \"grand_total\": 198.45\n    }\n  },\n  \"message\": \"Transaction reconciled.\"\n}"}]}]},{"name":"Shipments","item":[{"name":"List Shipments","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/shipments","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","shipments"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]},"description":"Paginated Magento 2 shipments for the instance, newest first (by magento_created_at, then id).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Response contract (matches the frontend Magento2Shipment type):** `shipment_number` (shipment's own Magento increment), `magento_order_id` (internal magento2_orders.id — drives the order link + tooltip), `magento_increment_id` (ORDER increment — the user-facing reference), `status` (`pushed` | `synced`), `carrier` (alias of `tracking_carrier`), `source_code` (MSI source).\n\n**Authentication:** Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/shipments","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","shipments"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 2,\n      \"magento2_order_id\": 23,\n      \"magento_entity_id\": 3,\n      \"shipment_number\": \"000000003\",\n      \"magento_order_id\": 23,\n      \"magento_increment_id\": \"000000023\",\n      \"status\": \"pushed\",\n      \"tracking_number\": \"1Z999AA10123456784\",\n      \"tracking_carrier\": \"ups\",\n      \"carrier\": \"ups\",\n      \"source_code\": \"default\",\n      \"magento_created_at\": \"2026-07-04T15:11:20+00:00\",\n      \"created_at\": \"2026-07-04T15:11:21+00:00\",\n      \"updated_at\": \"2026-07-04T15:11:21+00:00\",\n      \"pushed_to_magento_at\": \"2026-07-04T15:11:21+00:00\",\n      \"sku_sales_order_fulfillment_id\": 5,\n      \"order\": {\n        \"id\": 23,\n        \"magento_increment_id\": \"000000023\",\n        \"customer_email\": \"buyer@example.com\",\n        \"grand_total\": \"9.9900\",\n        \"currency_code\": \"USD\"\n      }\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Get Shipment","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/shipments/{shipment}","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","shipments",":shipment"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"shipment","value":"2","description":"Internal Magento2Shipment ID."}]},"description":"Fetch a single shipment (same contract as the list row) plus the full `raw_payload` from Magento. 404 when the shipment does not belong to the supplied instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\n**Authentication:** Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/shipments/{shipment}","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","shipments",":shipment"],"variable":[{"key":"magento2IntegrationInstance","value":"42","description":"Internal IntegrationInstance ID."},{"key":"shipment","value":"2","description":"Internal Magento2Shipment ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 2,\n    \"magento2_order_id\": 23,\n    \"magento_entity_id\": 3,\n    \"shipment_number\": \"000000003\",\n    \"magento_order_id\": 23,\n    \"magento_increment_id\": \"000000023\",\n    \"status\": \"pushed\",\n    \"tracking_number\": \"1Z999AA10123456784\",\n    \"tracking_carrier\": \"ups\",\n    \"carrier\": \"ups\",\n    \"source_code\": \"default\",\n    \"magento_created_at\": \"2026-07-04T15:11:20+00:00\",\n    \"created_at\": \"2026-07-04T15:11:21+00:00\",\n    \"updated_at\": \"2026-07-04T15:11:21+00:00\",\n    \"pushed_to_magento_at\": \"2026-07-04T15:11:21+00:00\",\n    \"sku_sales_order_fulfillment_id\": 5,\n    \"order\": {\n      \"id\": 23,\n      \"magento_increment_id\": \"000000023\",\n      \"customer_email\": \"buyer@example.com\",\n      \"grand_total\": \"9.9900\",\n      \"currency_code\": \"USD\"\n    },\n    \"raw_payload\": {\n      \"entity_id\": 3,\n      \"increment_id\": \"000000003\"\n    }\n  }\n}"}]}]},{"name":"Inventory Sources (MSI)","item":[{"name":"List Cached MSI Sources","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/inventory-sources-cache","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","inventory-sources-cache"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"ID of the Magento 2 integration instance."}]},"description":"Returns the paginated list of cached Magento MSI sources for the instance.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthenticated. Requires a Bearer token.\n\nResponse fields per row:\n- `id` — cache row id\n- `integration_instance_id` — owning integration instance\n- `source_code` — Magento MSI source_code (unique per instance)\n- `name` — human-friendly source name\n- `enabled` — whether the source is enabled in Magento\n- `country_id`, `region`, `city`, `postcode`, `contact_email` — location/contact metadata\n- `master_of_stock` — per-source sync direction; one of `sku.io`, `magento2`, `disabled` (defaults to `sku.io`). See PATCH `…/inventory-sources/{sourceCode}/master-of-stock`.\n- `last_fetched_at` — ISO8601 timestamp of the most recent refresh\n\nAllowed sorts: `source_code`, `name`, `enabled`, `updated_at`. Default sort: `source_code` ASC."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/inventory-sources-cache","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","inventory-sources-cache"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"ID of the Magento 2 integration instance."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1,\n      \"integration_instance_id\": 1,\n      \"source_code\": \"default\",\n      \"name\": \"Default Source\",\n      \"enabled\": true,\n      \"country_id\": \"US\",\n      \"region\": \"California\",\n      \"city\": \"San Francisco\",\n      \"postcode\": \"94103\",\n      \"contact_email\": \"ops@example.test\",\n      \"last_fetched_at\": \"2026-06-20T14:32:11+00:00\",\n      \"created_at\": \"2026-06-12T09:00:00+00:00\",\n      \"master_of_stock\": \"sku.io\"\n    },\n    {\n      \"id\": 2,\n      \"integration_instance_id\": 1,\n      \"source_code\": \"east_dc\",\n      \"name\": \"East Coast Warehouse\",\n      \"enabled\": true,\n      \"country_id\": \"US\",\n      \"region\": \"New Jersey\",\n      \"city\": \"Newark\",\n      \"postcode\": \"07102\",\n      \"contact_email\": null,\n      \"last_fetched_at\": \"2026-06-20T14:32:11+00:00\",\n      \"created_at\": \"2026-06-12T09:00:00+00:00\",\n      \"master_of_stock\": \"sku.io\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 25,\n  \"total\": 2,\n  \"from\": 1,\n  \"to\": 2\n}"}]},{"name":"Refresh MSI Sources from Magento","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/inventory-sources/refresh","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","inventory-sources","refresh"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"ID of the Magento 2 integration instance."}]},"description":"Synchronously fetches MSI sources from Magento via `GET /rest/V1/inventory/sources` and upserts them into the local cache.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthenticated. Requires a Bearer token. Empty JSON body (`{}`).\n\nSide effects:\n- Issues one Saloon request to Magento (no tracked job, no queue).\n- Upserts each returned source by `(integration_instance_id, source_code)`.\n- Updates the row's `updated_at` so the UI can render \"last refreshed at\".\n\nWhen Magento has MSI disabled, it returns HTTP 400. The connector translates this into an empty result — the endpoint still returns 200 with `fetched: 0`, `written: 0` so the UI can surface a friendly message instead of an error.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/inventory-sources/refresh","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","inventory-sources","refresh"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"ID of the Magento 2 integration instance."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Magento 2 MSI sources refreshed.\",\n  \"data\": {\n    \"fetched\": 2,\n    \"written\": 2,\n    \"last_fetched_at\": \"2026-06-20T14:32:11+00:00\"\n  }\n}"}]},{"name":"Update Master of Stock for a Source","request":{"method":"PATCH","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/inventory-sources/{sourceCode}/master-of-stock","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","inventory-sources",":sourceCode","master-of-stock"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"Magento 2 integration instance ID"},{"key":"sourceCode","value":"default","description":"Magento MSI source_code (e.g. 'default', 'warehouse_east')"}]},"description":"Update the Master of Stock direction for a single Magento MSI source.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nPer the sales-channel standard §7.10, Master of Stock lives per-location (per MSI source for Magento 2), not as a single global value on the integration instance.\n\n**Authentication:** Requires Bearer token.\n\n**Request body:**\n- `master_of_stock` (string, required) — one of `sku.io`, `magento2`, `disabled`.\n  - `sku.io` — SKU.io pushes on-hand to this Magento source on a schedule.\n  - `magento2` — RESERVED. Magento→SKU.io direction is not implemented yet; the UI exposes it as a disabled \"(contact support)\" option.\n  - `disabled` — no automatic sync for this source.\n\n**Responses:**\n- `200 OK` — returns `{ data: { source_code, master_of_stock } }`.\n- `404 Not Found` — the MSI source is not cached for this instance. Refresh sources on Settings → Locations first.\n- `422 Unprocessable Entity` — `master_of_stock` missing or not one of the allowed values.","body":{"mode":"raw","raw":"{\n  \"master_of_stock\": \"sku.io\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/inventory-sources/{sourceCode}/master-of-stock","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","inventory-sources",":sourceCode","master-of-stock"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"Magento 2 integration instance ID"},{"key":"sourceCode","value":"default","description":"Magento MSI source_code (e.g. 'default', 'warehouse_east')"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Master of Stock updated.\",\n  \"data\": {\n    \"source_code\": \"default\",\n    \"master_of_stock\": \"sku.io\"\n  }\n}"}]}]},{"name":"Warehouse Mappings","item":[{"name":"List Warehouse Mappings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/warehouse-mappings","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","warehouse-mappings"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"ID of the Magento 2 integration instance."}]},"description":"Returns the cached MSI inventory sources for the instance joined to any persisted SKU.io warehouse mappings, plus the full list of SKU.io warehouses available to map to.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthenticated. Requires a Bearer token.\n\nResponse fields:\n- `data.sources[]` — one row per cached MSI source. `warehouse_id`/`warehouse_name` are null when the source is not yet mapped.\n- `data.warehouses[]` — every SKU.io warehouse the user can pick.\n- `data.mapped_count` / `data.unmapped_count` — counts derived from `sources`.\n\nThe page renders the entire table from this single response — no separate calls for the warehouse picker are required."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/warehouse-mappings","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","warehouse-mappings"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"ID of the Magento 2 integration instance."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"sources\": [\n      {\n        \"source_code\": \"default\",\n        \"source_name\": \"Default Warehouse\",\n        \"enabled\": true,\n        \"country_id\": \"US\",\n        \"region\": \"CA\",\n        \"city\": \"Los Angeles\",\n        \"warehouse_id\": 12,\n        \"warehouse_name\": \"LA Main\"\n      },\n      {\n        \"source_code\": \"east_dc\",\n        \"source_name\": \"East Coast DC\",\n        \"enabled\": true,\n        \"country_id\": \"US\",\n        \"region\": \"NJ\",\n        \"city\": \"Newark\",\n        \"warehouse_id\": null,\n        \"warehouse_name\": null\n      }\n    ],\n    \"warehouses\": [\n      {\n        \"id\": 12,\n        \"name\": \"LA Main\"\n      },\n      {\n        \"id\": 13,\n        \"name\": \"NJ Hub\"\n      }\n    ],\n    \"mapped_count\": 1,\n    \"unmapped_count\": 1\n  }\n}"}]},{"name":"Bulk Replace Warehouse Mappings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/warehouse-mappings","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","warehouse-mappings"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"ID of the Magento 2 integration instance."}]},"description":"Bulk-replaces warehouse mappings for the instance in a single transaction.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthenticated. Requires a Bearer token.\n\nRequest body (`mappings[]`):\n- `source_code` (string, required) — must exist in `magento2_inventory_sources_cache` for this instance. Refresh from the Locations tab first if the source is missing.\n- `warehouse_id` (integer or null, required) — must exist in `warehouses`. `null` removes the mapping for that source.\n\nDuplicate `source_code` entries in the same payload are rejected (422).\n\nReturns the freshly-rebuilt index payload so the UI can re-render without an extra GET.","body":{"mode":"raw","raw":"{\n  \"mappings\": [\n    {\n      \"source_code\": \"default\",\n      \"warehouse_id\": 12\n    },\n    {\n      \"source_code\": \"east_dc\",\n      \"warehouse_id\": 13\n    },\n    {\n      \"source_code\": \"legacy_pop_up\",\n      \"warehouse_id\": null\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/warehouse-mappings","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","warehouse-mappings"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"ID of the Magento 2 integration instance."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"sources\": [\n      {\n        \"source_code\": \"default\",\n        \"source_name\": \"Default Warehouse\",\n        \"enabled\": true,\n        \"country_id\": \"US\",\n        \"region\": \"CA\",\n        \"city\": \"Los Angeles\",\n        \"warehouse_id\": 12,\n        \"warehouse_name\": \"LA Main\"\n      },\n      {\n        \"source_code\": \"east_dc\",\n        \"source_name\": \"East Coast DC\",\n        \"enabled\": true,\n        \"country_id\": \"US\",\n        \"region\": \"NJ\",\n        \"city\": \"Newark\",\n        \"warehouse_id\": 13,\n        \"warehouse_name\": \"NJ Hub\"\n      }\n    ],\n    \"warehouses\": [\n      {\n        \"id\": 12,\n        \"name\": \"LA Main\"\n      },\n      {\n        \"id\": 13,\n        \"name\": \"NJ Hub\"\n      }\n    ],\n    \"mapped_count\": 2,\n    \"unmapped_count\": 0\n  },\n  \"message\": \"Warehouse mappings saved successfully\"\n}"}]}]},{"name":"Shipping Method Mappings","item":[{"name":"List Shipping Method Mappings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/shipping-method-mappings","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","shipping-method-mappings"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"ID of the Magento 2 integration instance."}]},"description":"Returns the existing Magento -> SKU.io shipping method mappings, the distinct Magento shipping methods observed on past imported orders for this instance, and the catalogue of SKU.io ShippingMethod records that can be picked from the row-level select.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthenticated. Requires a Bearer token.\n\nResponse fields:\n- `data.mappings[]` — union of observed methods and any persisted mappings. `sku_shipping_method_id`/`sku_shipping_method_name` are null when the method is not yet mapped. `magento_method_label` is the human-friendly description from `shipping_description` on the original order (when available).\n- `data.observed_methods[]` — raw list of distinct `{carrier}_{method}` codes seen on past orders, paired with the latest `shipping_description` observed.\n- `data.sku_shipping_methods[]` — every SKU.io ShippingMethod available to pick from. Rendered as friendly labels in the row-level select via `full_name` (\"Carrier - Method\").\n\nThe page renders the entire table from this single response — no separate call for the shipping-method catalogue is required."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/shipping-method-mappings","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","shipping-method-mappings"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"ID of the Magento 2 integration instance."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"mappings\": [\n      {\n        \"id\": 42,\n        \"magento_method_code\": \"flatrate_flatrate\",\n        \"magento_method_label\": \"Flat Rate - Fixed\",\n        \"sku_shipping_method_id\": 12,\n        \"sku_shipping_method_name\": \"USPS - Priority Mail\"\n      },\n      {\n        \"id\": null,\n        \"magento_method_code\": \"ups_GND\",\n        \"magento_method_label\": \"United Parcel Service - Ground\",\n        \"sku_shipping_method_id\": null,\n        \"sku_shipping_method_name\": null\n      },\n      {\n        \"id\": null,\n        \"magento_method_code\": \"fedex_FEDEX_2_DAY\",\n        \"magento_method_label\": \"Federal Express - 2 Day\",\n        \"sku_shipping_method_id\": null,\n        \"sku_shipping_method_name\": null\n      }\n    ],\n    \"observed_methods\": [\n      {\n        \"magento_method_code\": \"flatrate_flatrate\",\n        \"magento_method_label\": \"Flat Rate - Fixed\"\n      },\n      {\n        \"magento_method_code\": \"ups_GND\",\n        \"magento_method_label\": \"United Parcel Service - Ground\"\n      },\n      {\n        \"magento_method_code\": \"fedex_FEDEX_2_DAY\",\n        \"magento_method_label\": \"Federal Express - 2 Day\"\n      }\n    ],\n    \"sku_shipping_methods\": [\n      {\n        \"id\": 12,\n        \"full_name\": \"USPS - Priority Mail\"\n      },\n      {\n        \"id\": 13,\n        \"full_name\": \"UPS - Ground\"\n      },\n      {\n        \"id\": 14,\n        \"full_name\": \"FedEx - 2 Day Air\"\n      }\n    ]\n  }\n}"}]},{"name":"Bulk Replace Shipping Method Mappings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/shipping-method-mappings","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","shipping-method-mappings"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"ID of the Magento 2 integration instance."}]},"description":"Bulk-replaces the shipping method mappings for the instance in a single transaction. Uses **replace** semantics:\n- Rows present in the payload with a non-null `sku_shipping_method_id` are upserted.\n- Rows present with a null `sku_shipping_method_id` are removed (the user cleared the row-level select).\n- Rows whose `magento_method_code` is absent from the payload are removed (delete-by-omission).\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nAuthenticated. Requires a Bearer token.\n\nRequest body (`mappings[]`):\n- `magento_carrier_code` (string, optional, max 64) — informational; the canonical key is `magento_method_code`.\n- `magento_method_code` (string, required, max 128) — the Magento `{carrier}_{method}` identifier (e.g. `flatrate_flatrate`, `ups_GND`, `fedex_FEDEX_2_DAY`).\n- `sku_shipping_method_id` (integer or null, optional) — must exist in `shipping_methods`. `null` removes the mapping.\n- `sku_shipping_method_name` (string, optional, max 255) — informational label used by the UI / activity log; not authoritative.","body":{"mode":"raw","raw":"{\n  \"mappings\": [\n    {\n      \"magento_carrier_code\": \"flatrate\",\n      \"magento_method_code\": \"flatrate_flatrate\",\n      \"sku_shipping_method_id\": 12,\n      \"sku_shipping_method_name\": \"USPS - Priority Mail\"\n    },\n    {\n      \"magento_carrier_code\": \"ups\",\n      \"magento_method_code\": \"ups_GND\",\n      \"sku_shipping_method_id\": 13,\n      \"sku_shipping_method_name\": \"UPS - Ground\"\n    },\n    {\n      \"magento_carrier_code\": \"fedex\",\n      \"magento_method_code\": \"fedex_FEDEX_2_DAY\",\n      \"sku_shipping_method_id\": null,\n      \"sku_shipping_method_name\": null\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/shipping-method-mappings","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","shipping-method-mappings"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"ID of the Magento 2 integration instance."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 42,\n      \"magento_method_code\": \"flatrate_flatrate\",\n      \"sku_shipping_method_id\": 12\n    },\n    {\n      \"id\": 43,\n      \"magento_method_code\": \"ups_GND\",\n      \"sku_shipping_method_id\": 13\n    }\n  ]\n}"}]}]},{"name":"Payment Method Mappings","item":[{"name":"List Payment Method Mappings","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/payment-method-mappings","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","payment-method-mappings"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"ID of the Magento 2 integration instance."}]},"description":"Returns the Magento 2 -> SKU.io payment-method mapping table for this instance, plus the catalogue of SKU.io payment types that can be mapped to.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nThe mapping rows are the union of every payment method observed on past imported orders and every persisted mapping — methods seen on orders that are not yet mapped appear with `sku_payment_type_id: null`, and persisted-but-no-longer-observed methods still appear so stale mappings can be revisited or cleared.\n\nEach mapping row contains:\n- `id` — the persisted mapping row ID, or null when the method has been observed on orders but not yet mapped.\n- `magento_method` — the Magento 2 payment method code (e.g. `checkmo`, `paypal_express`, `banktransfer`).\n- `magento_method_title` — the human-friendly method title observed on orders (falls back to the code).\n- `sku_payment_type_id` / `sku_payment_type_name` — the SKU.io payment type the method maps to; null when unmapped.\n- `unpaid_disposition` — how orders that arrive UNPAID with this payment method are imported: null (inherit the store-level payment-pending policy), `open` (import normally, no hold), `draft` (import without allocating stock), or `reserve` (allocate stock and hold fulfillment until payment arrives, optionally expiring per the store-level reservation expiry).\n- `accounting_sync` — when this method's orders sync to the connected accounting provider: null (inherit the store-level trigger), `always` (sync once confirmed regardless of payment), `when_paid` (sync only after payment arrives), or `never` (this method's orders are excluded from accounting sync).\n\nResponse also includes:\n- `data.available_payment_types[]` — every active SKU.io payment type (`id`, `name`).\n- `meta` — `total_mappings`, `mapped_count`, `unmapped_count` counters.\n\nAuthenticated. Requires a Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/payment-method-mappings","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","payment-method-mappings"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"ID of the Magento 2 integration instance."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"mappings\": [\n      {\n        \"id\": 31,\n        \"magento_method\": \"checkmo\",\n        \"magento_method_title\": \"Check / Money order\",\n        \"sku_payment_type_id\": 4,\n        \"sku_payment_type_name\": \"Check\",\n        \"unpaid_disposition\": \"reserve\",\n        \"accounting_sync\": \"when_paid\"\n      },\n      {\n        \"id\": 32,\n        \"magento_method\": \"paypal_express\",\n        \"magento_method_title\": \"PayPal Express Checkout\",\n        \"sku_payment_type_id\": 2,\n        \"sku_payment_type_name\": \"PayPal\",\n        \"unpaid_disposition\": null,\n        \"accounting_sync\": null\n      },\n      {\n        \"id\": null,\n        \"magento_method\": \"banktransfer\",\n        \"magento_method_title\": \"Bank Transfer Payment\",\n        \"sku_payment_type_id\": null,\n        \"sku_payment_type_name\": null,\n        \"unpaid_disposition\": null,\n        \"accounting_sync\": null\n      }\n    ],\n    \"available_payment_types\": [\n      {\n        \"id\": 1,\n        \"name\": \"Cash\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"PayPal\"\n      },\n      {\n        \"id\": 3,\n        \"name\": \"Credit Card\"\n      },\n      {\n        \"id\": 4,\n        \"name\": \"Check\"\n      }\n    ]\n  },\n  \"meta\": {\n    \"total_mappings\": 3,\n    \"mapped_count\": 2,\n    \"unmapped_count\": 1\n  }\n}"}]},{"name":"Bulk Replace Payment Method Mappings","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/payment-method-mappings","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","payment-method-mappings"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"ID of the Magento 2 integration instance."}]},"description":"Bulk-replaces the payment-method mapping set for this instance. Any persisted row whose `magento_method` is NOT in the supplied payload is deleted; the rest are upserted — so send the complete set of mappings you want kept, not a delta.\n\n**Not yet available to API tokens**\nThis endpoint currently requires session authentication; Personal Access Token scope support is in progress.\n\nBody parameters:\n- `mappings` (array, required — may be empty to clear all mappings). Each row:\n  - `mappings.*.magento_method` (string, required, max 128) — the Magento 2 payment method code.\n  - `mappings.*.sku_payment_type_id` (integer or null) — the SKU.io payment type to map to; null records the method as known but unmapped.\n  - `mappings.*.unpaid_disposition` (string or null, optional) — per-method override for how orders arriving unpaid with this method are imported: null inherits the store-level payment-pending policy, `open` imports normally, `draft` imports without allocating stock, `reserve` allocates stock and holds fulfillment with an expiry per the store-level reservation settings.\n  - `mappings.*.accounting_sync` (string or null, optional) — per-method override for when this method's orders sync to the accounting provider: null inherits the store-level trigger, `always`, `when_paid`, or `never`.\n\nReturns the same payload as the list endpoint, rebuilt after the update.\n\nAuthenticated. Requires a Bearer token.","body":{"mode":"raw","raw":"{\n  \"mappings\": [\n    {\n      \"magento_method\": \"checkmo\",\n      \"sku_payment_type_id\": 4,\n      \"unpaid_disposition\": \"reserve\",\n      \"accounting_sync\": \"when_paid\"\n    },\n    {\n      \"magento_method\": \"paypal_express\",\n      \"sku_payment_type_id\": 2,\n      \"unpaid_disposition\": null,\n      \"accounting_sync\": null\n    },\n    {\n      \"magento_method\": \"banktransfer\",\n      \"sku_payment_type_id\": null,\n      \"unpaid_disposition\": \"draft\",\n      \"accounting_sync\": \"never\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/magento2/integration-instances/{magento2IntegrationInstance}/payment-method-mappings","host":["{{baseUrl}}"],"path":["api","magento2","integration-instances",":magento2IntegrationInstance","payment-method-mappings"],"variable":[{"key":"magento2IntegrationInstance","value":"1","description":"ID of the Magento 2 integration instance."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"mappings\": [\n      {\n        \"id\": 31,\n        \"magento_method\": \"checkmo\",\n        \"magento_method_title\": \"Check / Money order\",\n        \"sku_payment_type_id\": 4,\n        \"sku_payment_type_name\": \"Check\",\n        \"unpaid_disposition\": \"reserve\",\n        \"accounting_sync\": \"when_paid\"\n      },\n      {\n        \"id\": 32,\n        \"magento_method\": \"paypal_express\",\n        \"magento_method_title\": \"PayPal Express Checkout\",\n        \"sku_payment_type_id\": 2,\n        \"sku_payment_type_name\": \"PayPal\",\n        \"unpaid_disposition\": null,\n        \"accounting_sync\": null\n      },\n      {\n        \"id\": null,\n        \"magento_method\": \"banktransfer\",\n        \"magento_method_title\": \"Bank Transfer Payment\",\n        \"sku_payment_type_id\": null,\n        \"sku_payment_type_name\": null,\n        \"unpaid_disposition\": \"draft\",\n        \"accounting_sync\": \"never\"\n      }\n    ],\n    \"available_payment_types\": [\n      {\n        \"id\": 1,\n        \"name\": \"Cash\"\n      },\n      {\n        \"id\": 2,\n        \"name\": \"PayPal\"\n      },\n      {\n        \"id\": 3,\n        \"name\": \"Credit Card\"\n      },\n      {\n        \"id\": 4,\n        \"name\": \"Check\"\n      }\n    ]\n  },\n  \"meta\": {\n    \"total_mappings\": 3,\n    \"mapped_count\": 2,\n    \"unmapped_count\": 1\n  }\n}"}]}]},{"name":"BOMs","item":[{"name":"List BOMs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms?per_page=25","host":["{{baseUrl}}"],"path":["api","manufacturing","boms"],"query":[{"key":"per_page","value":"25","description":"Results per page (default 10).","disabled":true}]},"description":"Paginated list of BOMs. Supports Spatie filters (status, product_id, search) and sort.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."}},{"name":"Create BOM","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms","host":["{{baseUrl}}"],"path":["api","manufacturing","boms"]},"description":"Create a new BOM with output lines, component lines, and (optional) operations.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Consumption method fields:**\n- `default_consumption_method` (optional, enum: `forward_flush` | `backflush`) — BOM-level default inherited by component lines that omit their own `consumption_method`. Null means no default (lines fall back to `forward_flush`).\n- `component_lines[].consumption_method` (optional, enum: `forward_flush` | `backflush`) — how the component is issued when production is recorded. `forward_flush` (the default) waits for the operator to issue the component explicitly with an actual consumed quantity; `backflush` auto-consumes it from the produced output quantity multiplied by the BOM per-unit usage, with no operator entry required. An explicit `null` behaves exactly like omitting the field — the line inherits `default_consumption_method`, falling back to `forward_flush` when the BOM has no default. The method is snapshotted onto each manufacturing-order component line at confirmation, so later BOM edits never change how an already-open order consumes.\n\n**Operation cost fields:** each `operations[]` row accepts `time_minutes`, `cost_per_hour`, and `fixed_cost` (optional, numeric, min 0) — a flat per-run cost added on top of time-based labour. Fixed cost is capitalized onto finished goods the same way labour is, but is never marked up by work-center overhead.\nAuth: requires Bearer token.\n\n`cost_classification` (optional, `labor`|`overhead`, default `labor`) sets the expense classification independently of the rate basis: `labor` posts the operation's variable cost to the Labor bucket (marked up by the work center's overhead %); `overhead` posts machine/tooling-style cost to the Overhead bucket with no additional markup.\n\nValidation: a component line's `product_id` may not equal the BOM's own `product_id` or any `output_lines.*.product_id` — a BOM cannot consume its own output as a component. Violations return 422 with an error on `component_lines.{index}.product_id`.\n\nUnits: `uom_id` is optional on every output and component line. When omitted, the line is measured in that line product's own stock unit, so a product stocked by weight, volume or length never needs a conversion it does not have. Send `uom_id` only to override that deliberately — it must be a unit enabled on the product, or the request is rejected with a 422 naming the unit and product.\n\n**Contractor-supplied components:** set `component_lines[].is_contractor_supplied` to `true` when the contract manufacturer provides that component from their own stock. It stays in the recipe and in lot genealogy, but is never allocated from your inventory, never reported as short, and never costed into the finished good — its cost is already inside the contractor's price on the purchase order. Defaults to `false`; omit the key on an update and the stored value is left unchanged.","body":{"mode":"raw","raw":"{\n  \"product_id\": 1,\n  \"name\": \"Default Recipe v1\",\n  \"version_notes\": \"Initial recipe\",\n  \"status\": \"draft\",\n  \"effective_from\": \"2026-01-01\",\n  \"effective_to\": null,\n  \"is_default\": true,\n  \"is_phantom\": false,\n  \"expected_yield_percent\": 98,\n  \"cost_allocation_method\": \"by_value\",\n  \"default_consumption_method\": \"forward_flush\",\n  \"output_lines\": [\n    {\n      \"product_id\": 1,\n      \"role\": \"primary\",\n      \"quantity\": 1,\n      \"uom_id\": 1,\n      \"sort_order\": 0\n    }\n  ],\n  \"component_lines\": [\n    {\n      \"product_id\": 2,\n      \"quantity\": 2,\n      \"uom_id\": 1,\n      \"expected_scrap_percent\": 2,\n      \"consumption_method\": \"forward_flush\",\n      \"is_optional\": false,\n      \"sort_order\": 0,\n      \"is_contractor_supplied\": false\n    },\n    {\n      \"product_id\": 3,\n      \"quantity\": 1,\n      \"uom_id\": 1,\n      \"consumption_method\": \"backflush\",\n      \"sort_order\": 1,\n      \"is_contractor_supplied\": false\n    }\n  ],\n  \"operations\": [\n    {\n      \"sequence\": 10,\n      \"name\": \"Mix\",\n      \"work_center_id\": 1,\n      \"operation_type\": \"setup\",\n      \"time_minutes\": 30,\n      \"cost_per_hour\": 45,\n      \"fixed_cost\": 0,\n      \"cost_classification\": \"labor\",\n      \"instructions\": \"Mix components thoroughly.\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/manufacturing/boms","host":["{{baseUrl}}"],"path":["api","manufacturing","boms"]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 12,\n  \"product_id\": 1,\n  \"product_variant_id\": null,\n  \"name\": \"Default Recipe v1\",\n  \"version_number\": 1,\n  \"version_notes\": \"Initial recipe\",\n  \"status\": \"draft\",\n  \"effective_from\": \"2026-01-01\",\n  \"effective_to\": null,\n  \"is_default\": true,\n  \"is_phantom\": false,\n  \"expected_yield_percent\": 98,\n  \"manufacturing_leadtime_days\": null,\n  \"cost_allocation_method\": \"by_value\",\n  \"default_consumption_method\": \"forward_flush\",\n  \"created_by_id\": 1,\n  \"component_lines\": [\n    {\n      \"id\": 31,\n      \"bom_id\": 12,\n      \"product_id\": 2,\n      \"product_variant_id\": null,\n      \"quantity\": 2,\n      \"uom_id\": 1,\n      \"expected_scrap_percent\": 2,\n      \"consumption_method\": \"forward_flush\",\n      \"is_optional\": false,\n      \"notes\": null,\n      \"sort_order\": 0,\n      \"estimated_cost\": 0.03,\n      \"is_contractor_supplied\": false\n    },\n    {\n      \"id\": 32,\n      \"bom_id\": 12,\n      \"product_id\": 3,\n      \"product_variant_id\": null,\n      \"quantity\": 1,\n      \"uom_id\": 1,\n      \"expected_scrap_percent\": null,\n      \"consumption_method\": \"backflush\",\n      \"is_optional\": false,\n      \"notes\": null,\n      \"sort_order\": 1,\n      \"estimated_cost\": 0.03,\n      \"is_contractor_supplied\": false\n    }\n  ],\n  \"output_lines\": [\n    {\n      \"id\": 21,\n      \"bom_id\": 12,\n      \"product_id\": 1,\n      \"product_variant_id\": null,\n      \"role\": \"primary\",\n      \"quantity\": 1,\n      \"uom_id\": 1,\n      \"manual_cost_percent\": null,\n      \"byproduct_valuation_method\": null,\n      \"byproduct_valuation_amount\": null,\n      \"sort_order\": 0,\n      \"estimated_cost\": 0.03\n    }\n  ],\n  \"operations\": [\n    {\n      \"id\": 7,\n      \"bom_id\": 12,\n      \"sequence\": 10,\n      \"name\": \"Mix\",\n      \"work_center_id\": 1,\n      \"operation_type\": \"setup\",\n      \"time_minutes\": 30,\n      \"cost_per_hour\": 45,\n      \"fixed_cost\": 0,\n      \"cost_classification\": \"labor\",\n      \"instructions\": \"Mix components thoroughly.\"\n    }\n  ],\n  \"archived_at\": null,\n  \"created_at\": \"2026-07-01T10:15:00.000000Z\",\n  \"updated_at\": \"2026-07-01T10:15:00.000000Z\"\n}"}]},{"name":"Bulk Delete BOMs","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms","host":["{{baseUrl}}"],"path":["api","manufacturing","boms"]},"description":"Permanently delete multiple BOMs in one call.\nAuth: requires Bearer token.\nBody: ids (required, array of BOM ids, min 1). Rows that fail to delete are counted in failCount instead of aborting the batch.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"DELETE","url":{"raw":"{{baseUrl}}/api/manufacturing/boms","host":["{{baseUrl}}"],"path":["api","manufacturing","boms"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Deleted 2 BOM(s)\",\n  \"data\": {\n    \"totalCount\": 2,\n    \"failCount\": 0\n  }\n}"}]},{"name":"Get BOM","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{id}","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Get a single BOM with its product, component lines, output lines, operations (with work centers), and creator, plus `manufacturing_orders_count`.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe BOM carries `default_consumption_method` (`forward_flush` | `backflush` | null) — the default inherited by component lines that don't set their own — and each `component_lines[]` row carries its effective `consumption_method`. `forward_flush` components are issued explicitly by the operator when production is recorded; `backflush` components are auto-consumed from the produced output quantity multiplied by the BOM per-unit usage.\nAuth: requires Bearer token.\n\n**Lot traceability.** The response carries `lot_traceability_gap`: null when there is no gap, otherwise `{output_sku, components:[{id, sku, name}]}` naming the components that are not lot-tracked while the output is. Their consumption will not appear in the finished lot's genealogy, so a recall cannot trace them. Non-blocking — the recipe is valid — but worth surfacing wherever the recipe is shown.\n\n**Contractor-supplied components:** set `component_lines[].is_contractor_supplied` to `true` when the contract manufacturer provides that component from their own stock. It stays in the recipe and in lot genealogy, but is never allocated from your inventory, never reported as short, and never costed into the finished good — its cost is already inside the contractor's price on the purchase order. Defaults to `false`; omit the key on an update and the stored value is left unchanged."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{id}","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 12,\n  \"product_id\": 1,\n  \"product_variant_id\": null,\n  \"product\": {\n    \"id\": 1,\n    \"sku\": \"KIT-A\",\n    \"name\": \"Widget Kit A\",\n    \"stock_uom\": {\n      \"id\": 11,\n      \"product_id\": 1,\n      \"uom_id\": 1,\n      \"unit_of_measure\": {\n        \"id\": 1,\n        \"code\": \"EA\",\n        \"name\": \"Each\",\n        \"uom_type\": \"count\",\n        \"is_system\": true\n      },\n      \"is_stock_uom\": true,\n      \"is_purchase_uom\": true,\n      \"is_sales_uom\": true,\n      \"conversion_factor\": 1,\n      \"display_precision\": 0,\n      \"has_transactional_history\": true\n    }\n  },\n  \"name\": \"Default Recipe v1\",\n  \"version_number\": 1,\n  \"version_notes\": \"Initial recipe\",\n  \"status\": \"active\",\n  \"effective_from\": \"2026-01-01\",\n  \"effective_to\": null,\n  \"is_default\": true,\n  \"is_phantom\": false,\n  \"expected_yield_percent\": 98,\n  \"manufacturing_leadtime_days\": 2,\n  \"cost_allocation_method\": \"by_value\",\n  \"default_consumption_method\": \"forward_flush\",\n  \"created_by_id\": 1,\n  \"created_by\": {\n    \"id\": 1,\n    \"name\": \"Dev User\"\n  },\n  \"component_lines\": [\n    {\n      \"id\": 31,\n      \"bom_id\": 12,\n      \"product_id\": 2,\n      \"product_variant_id\": null,\n      \"product\": {\n        \"id\": 2,\n        \"sku\": \"RM-BEAN\",\n        \"name\": \"Raw Coffee Beans\",\n        \"weight\": 0.5,\n        \"weight_unit\": \"kg\",\n        \"stock_uom\": null\n      },\n      \"quantity\": 2,\n      \"uom_id\": 1,\n      \"unit_of_measure\": {\n        \"id\": 1,\n        \"code\": \"EA\",\n        \"name\": \"Each\",\n        \"uom_type\": \"count\",\n        \"is_system\": true\n      },\n      \"expected_scrap_percent\": 2,\n      \"consumption_method\": \"forward_flush\",\n      \"is_optional\": false,\n      \"notes\": null,\n      \"sort_order\": 0,\n      \"estimated_cost\": 0.03,\n      \"is_contractor_supplied\": false\n    },\n    {\n      \"id\": 32,\n      \"bom_id\": 12,\n      \"product_id\": 3,\n      \"product_variant_id\": null,\n      \"product\": {\n        \"id\": 3,\n        \"sku\": \"PKG-POUCH\",\n        \"name\": \"250g Pouch\",\n        \"weight\": 0.02,\n        \"weight_unit\": \"kg\",\n        \"stock_uom\": null\n      },\n      \"quantity\": 1,\n      \"uom_id\": 1,\n      \"unit_of_measure\": {\n        \"id\": 1,\n        \"code\": \"EA\",\n        \"name\": \"Each\",\n        \"uom_type\": \"count\",\n        \"is_system\": true\n      },\n      \"expected_scrap_percent\": null,\n      \"consumption_method\": \"backflush\",\n      \"is_optional\": false,\n      \"notes\": null,\n      \"sort_order\": 1,\n      \"estimated_cost\": 0.03,\n      \"is_contractor_supplied\": false\n    }\n  ],\n  \"output_lines\": [\n    {\n      \"id\": 21,\n      \"bom_id\": 12,\n      \"product_id\": 1,\n      \"product_variant_id\": null,\n      \"product\": {\n        \"id\": 1,\n        \"sku\": \"KIT-A\",\n        \"name\": \"Widget Kit A\",\n        \"weight\": 1.2,\n        \"weight_unit\": \"kg\",\n        \"stock_uom\": null\n      },\n      \"role\": \"primary\",\n      \"quantity\": 1,\n      \"uom_id\": 1,\n      \"unit_of_measure\": {\n        \"id\": 1,\n        \"code\": \"EA\",\n        \"name\": \"Each\",\n        \"uom_type\": \"count\",\n        \"is_system\": true\n      },\n      \"manual_cost_percent\": null,\n      \"byproduct_valuation_method\": null,\n      \"byproduct_valuation_amount\": null,\n      \"sort_order\": 0,\n      \"estimated_cost\": 0.03\n    }\n  ],\n  \"operations\": [\n    {\n      \"id\": 7,\n      \"bom_id\": 12,\n      \"sequence\": 10,\n      \"name\": \"Mix\",\n      \"work_center_id\": 1,\n      \"work_center\": {\n        \"id\": 1,\n        \"name\": \"Mixing Station\",\n        \"overhead_rate_percent\": 10\n      },\n      \"operation_type\": \"setup\",\n      \"time_minutes\": 30,\n      \"cost_per_hour\": 45,\n      \"fixed_cost\": 75,\n      \"instructions\": \"Mix components thoroughly.\"\n    }\n  ],\n  \"manufacturing_orders_count\": 3,\n  \"draft_manufacturing_orders_count\": 2,\n  \"archived_at\": null,\n  \"created_at\": \"2026-07-01T10:15:00.000000Z\",\n  \"updated_at\": \"2026-07-02T08:30:00.000000Z\",\n  \"lot_traceability_gap\": null\n}"}]},{"name":"Update BOM","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{id}","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Update a BOM. All fields are optional — omitted fields keep their current values.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`default_consumption_method` (nullable, enum: `forward_flush` | `backflush`) sets the BOM-level default inherited by component lines that omit their own `consumption_method`; a per-line `consumption_method` always overrides it. A component line submitted with an explicit `consumption_method: null` behaves exactly like omitting the field — a new line inherits the BOM default (falling back to `forward_flush`), and an existing line keeps its current method. Changing consumption methods only affects manufacturing orders confirmed after the change — each order snapshots the method per component line at confirmation, so open orders keep consuming the way they were confirmed.\nAuth: requires Bearer token.\n\n`cost_classification` (optional, `labor`|`overhead`, default `labor`) sets the expense classification independently of the rate basis: `labor` posts the operation's variable cost to the Labor bucket (marked up by the work center's overhead %); `overhead` posts machine/tooling-style cost to the Overhead bucket with no additional markup.\n\nValidation: a component line's `product_id` may not equal the BOM's own `product_id` or any `output_lines.*.product_id` — a BOM cannot consume its own output as a component. Violations return 422 with an error on `component_lines.{index}.product_id`.\n\nUnits: `uom_id` is optional on every output and component line. When omitted, the line is measured in that line product's own stock unit, so a product stocked by weight, volume or length never needs a conversion it does not have. Send `uom_id` only to override that deliberately — it must be a unit enabled on the product, or the request is rejected with a 422 naming the unit and product.\n\n**Contractor-supplied components:** set `component_lines[].is_contractor_supplied` to `true` when the contract manufacturer provides that component from their own stock. It stays in the recipe and in lot genealogy, but is never allocated from your inventory, never reported as short, and never costed into the finished good — its cost is already inside the contractor's price on the purchase order. Defaults to `false`; omit the key on an update and the stored value is left unchanged.","body":{"mode":"raw","raw":"{\n  \"name\": \"Default Recipe v2\",\n  \"version_notes\": \"Adjusted scrap\",\n  \"cost_allocation_method\": \"by_quantity\",\n  \"default_consumption_method\": \"backflush\",\n  \"component_lines\": [\n    {\n      \"id\": 11,\n      \"product_id\": 2,\n      \"quantity\": 2.5,\n      \"uom_id\": 1,\n      \"expected_scrap_percent\": 3,\n      \"consumption_method\": \"forward_flush\",\n      \"is_contractor_supplied\": false\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{id}","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 12,\n  \"product_id\": 1,\n  \"product_variant_id\": null,\n  \"name\": \"Default Recipe v2\",\n  \"version_number\": 1,\n  \"version_notes\": \"Adjusted scrap\",\n  \"status\": \"active\",\n  \"effective_from\": \"2026-01-01\",\n  \"effective_to\": null,\n  \"is_default\": true,\n  \"is_phantom\": false,\n  \"expected_yield_percent\": 98,\n  \"manufacturing_leadtime_days\": 2,\n  \"cost_allocation_method\": \"by_quantity\",\n  \"default_consumption_method\": \"backflush\",\n  \"created_by_id\": 1,\n  \"component_lines\": [\n    {\n      \"id\": 11,\n      \"bom_id\": 12,\n      \"product_id\": 2,\n      \"product_variant_id\": null,\n      \"quantity\": 2.5,\n      \"uom_id\": 1,\n      \"expected_scrap_percent\": 3,\n      \"consumption_method\": \"forward_flush\",\n      \"is_optional\": false,\n      \"notes\": null,\n      \"sort_order\": 0,\n      \"estimated_cost\": 0.03,\n      \"is_contractor_supplied\": false\n    }\n  ],\n  \"output_lines\": [\n    {\n      \"id\": 21,\n      \"bom_id\": 12,\n      \"product_id\": 1,\n      \"product_variant_id\": null,\n      \"role\": \"primary\",\n      \"quantity\": 1,\n      \"uom_id\": 1,\n      \"manual_cost_percent\": null,\n      \"byproduct_valuation_method\": null,\n      \"byproduct_valuation_amount\": null,\n      \"sort_order\": 0,\n      \"estimated_cost\": 0.03\n    }\n  ],\n  \"operations\": [],\n  \"archived_at\": null,\n  \"created_at\": \"2026-07-01T10:15:00.000000Z\",\n  \"updated_at\": \"2026-07-02T11:40:00.000000Z\"\n}"}]},{"name":"Delete BOM","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{id}","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Delete BOM\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."}},{"name":"Duplicate BOM","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{id}/duplicate","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":id","duplicate"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Deep-clones a BOM into a new draft (output lines, component lines, operations). The new BOM's name is the source's name with \" (Copy)\" appended; if that name is already taken for the same product/variant the suffix becomes \" (Copy 2)\", \" (Copy 3)\", etc.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}}},{"name":"Save BOM as New Version","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{bom}/save-as-new-version","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":bom","save-as-new-version"],"variable":[{"key":"bom","value":"1","description":"The bom ID."}]},"description":"Create a new version of the BOM from the submitted fields, leaving the source version untouched. Accepts the same fields as Update BOM. The new version is created as Active with version_number set to the next number for the same product/variant.\nAuth: requires Bearer token.\nPath param: bom = source BOM id.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`update_draft_mos` (boolean, optional, default false): when true, every still-in-Draft manufacturing order that references the old version is moved onto the new version. Started/completed orders keep their pinned version.\n\n**Contractor-supplied components:** set `component_lines[].is_contractor_supplied` to `true` when the contract manufacturer provides that component from their own stock. It stays in the recipe and in lot genealogy, but is never allocated from your inventory, never reported as short, and never costed into the finished good — its cost is already inside the contractor's price on the purchase order. Defaults to `false`; omit the key on an update and the stored value is left unchanged.","body":{"mode":"raw","raw":"{\n  \"name\": \"Default Recipe\",\n  \"version_notes\": \"Reduced component scrap\",\n  \"effective_from\": \"2026-07-01\",\n  \"cost_allocation_method\": \"by_weight\",\n  \"default_consumption_method\": \"forward_flush\",\n  \"output_lines\": [\n    {\n      \"product_id\": 1,\n      \"role\": \"primary\",\n      \"quantity\": 1,\n      \"uom_id\": 1\n    }\n  ],\n  \"component_lines\": [\n    {\n      \"product_id\": 2,\n      \"quantity\": 2.5,\n      \"uom_id\": 1,\n      \"expected_scrap_percent\": 3,\n      \"consumption_method\": \"forward_flush\",\n      \"is_contractor_supplied\": false\n    }\n  ],\n  \"update_draft_mos\": true\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{bom}/save-as-new-version","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":bom","save-as-new-version"],"variable":[{"key":"bom","value":"1","description":"The bom ID."}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 15,\n  \"product_id\": 1,\n  \"product_variant_id\": null,\n  \"name\": \"Default Recipe\",\n  \"version_number\": 3,\n  \"version_notes\": \"Reduced component scrap\",\n  \"status\": \"active\",\n  \"effective_from\": \"2026-07-01\",\n  \"effective_to\": null,\n  \"is_default\": false,\n  \"is_phantom\": false,\n  \"expected_yield_percent\": null,\n  \"manufacturing_leadtime_days\": 2,\n  \"cost_allocation_method\": \"by_weight\",\n  \"default_consumption_method\": \"forward_flush\",\n  \"created_by_id\": 1,\n  \"component_lines\": [\n    {\n      \"id\": 61,\n      \"product_id\": 2,\n      \"quantity\": 2.5,\n      \"uom_id\": 1,\n      \"expected_scrap_percent\": 3,\n      \"consumption_method\": \"forward_flush\",\n      \"is_contractor_supplied\": false\n    }\n  ],\n  \"output_lines\": [\n    {\n      \"id\": 44,\n      \"product_id\": 1,\n      \"role\": \"primary\",\n      \"quantity\": 1,\n      \"uom_id\": 1\n    }\n  ],\n  \"operations\": [],\n  \"manufacturing_orders_count\": 0,\n  \"draft_manufacturing_orders_count\": 0,\n  \"archived_at\": null,\n  \"created_at\": \"2026-07-01T14:22:09.000000Z\",\n  \"updated_at\": \"2026-07-01T14:22:09.000000Z\"\n}"}]},{"name":"Activate BOM","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{id}/activate","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":id","activate"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Transitions a draft BOM to active. Optionally overrides effective dates.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nResponse shape: `{ \"data\": <BOM>, \"warnings\": [] }`. `warnings` is a list of non-blocking lot-traceability messages — populated when the BOM's finished good is lot-tracked but one or more components are not (their consumption would not appear in lot genealogy). Activation still succeeds.","body":{"mode":"raw","raw":"{\n  \"effective_from\": \"2026-04-19\",\n  \"effective_to\": null\n}","options":{"raw":{"language":"json"}}}}},{"name":"Deactivate BOM","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{id}/deactivate","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":id","deactivate"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Transitions an Active BOM back to Draft so it can be edited without archiving it (distinct from Archive, which retires the BOM). If the BOM was the default for its product/variant, the next eligible BOM is auto-promoted. Returns the refreshed BOM object under `data` (200), plus a root-level `promoted_default` object ({id, name, version_number}) naming the newly-promoted default — or `null` if nothing was promoted. The optional `reason` (nullable string, max 1000) is recorded on the BOM activity log.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"reason\": \"Needs a recipe correction before re-activating\"\n}","options":{"raw":{"language":"json"}}}}},{"name":"Archive BOM","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{id}/archive","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":id","archive"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Archives an Active BOM, marking it as retired (sets `archived_at`). If the archived BOM was the default for its product/variant, the next eligible BOM is auto-promoted to default. Returns the refreshed BOM object under `data` (200), plus a root-level `promoted_default` object ({id, name, version_number}) naming the newly-promoted default — or `null` if nothing was promoted. The frontend uses this to show \"BOM archived — {name} v{version} is now the default\". The optional `reason` (nullable string, max 1000) is recorded on the BOM activity log.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"reason\": \"Superseded by v2\"\n}","options":{"raw":{"language":"json"}}}}},{"name":"Restore BOM","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{id}/restore","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":id","restore"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Restores an archived BOM back to Draft status. Clears `archived_at` and removes the `is_default` flag so it does not auto-promote on next activation. The caller can then re-activate from Draft when ready. Returns the refreshed BOM object (200). Returns 500 if the BOM is not currently archived. The optional `reason` (nullable string, max 1000) is recorded on the BOM activity log.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"reason\": \"Customer asked us to bring this recipe back.\"\n}","options":{"raw":{"language":"json"}}}}},{"name":"Bulk Archive BOMs","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/bulk-archive","host":["{{baseUrl}}"],"path":["api","manufacturing","boms","bulk-archive"]},"description":"Archives multiple BOMs in one call. Body: `ids` (required array of BOM IDs, min 1; each must exist in `boms`). Only BOMs currently in `active` status are archived; BOMs in any other status are skipped. Each archived BOM that was a default triggers auto-promotion of the next eligible BOM for its product/variant. Returns a `message` summary and a `data` object: `{ totalCount, archivedCount, skippedCount, failCount }`.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/manufacturing/boms/bulk-archive","host":["{{baseUrl}}"],"path":["api","manufacturing","boms","bulk-archive"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Archived 2 BOM(s) (1 skipped)\",\n  \"data\": {\n    \"totalCount\": 3,\n    \"archivedCount\": 2,\n    \"skippedCount\": 1,\n    \"failCount\": 0\n  }\n}"}]},{"name":"Bulk Restore BOMs","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/bulk-restore","host":["{{baseUrl}}"],"path":["api","manufacturing","boms","bulk-restore"]},"description":"Restores multiple archived BOMs back to Draft in one call. Body: `ids` (required array of BOM IDs, min 1; each must exist in `boms`). Only BOMs currently in `archived` status are restored; BOMs in any other status are skipped. Returns a `message` summary and a `data` object: `{ totalCount, restoredCount, skippedCount, failCount }`.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"ids\": [\n    1,\n    2,\n    3\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/manufacturing/boms/bulk-restore","host":["{{baseUrl}}"],"path":["api","manufacturing","boms","bulk-restore"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Restored 3 BOM(s)\",\n  \"data\": {\n    \"totalCount\": 3,\n    \"restoredCount\": 3,\n    \"skippedCount\": 0,\n    \"failCount\": 0\n  }\n}"}]},{"name":"Set BOM as Default","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{id}/set-default","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":id","set-default"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Promotes the given BOM to be the default for its product/variant. Any other BOM currently marked default for the same product (and variant, if scoped) is automatically demoted to `is_default = false`. This is the single-record endpoint backing the kebab \"Set as Default\" action; for status transitions use `activate`/`archive` instead. Returns the refreshed BOM object (200). No request body is required.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{id}/set-default","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":id","set-default"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 14,\n  \"product_id\": 822,\n  \"product_variant_id\": null,\n  \"name\": \"Vanilla Latte v3\",\n  \"version_number\": 3,\n  \"version_notes\": null,\n  \"status\": \"active\",\n  \"effective_from\": \"2026-05-28\",\n  \"effective_to\": null,\n  \"is_default\": true,\n  \"is_phantom\": false,\n  \"expected_yield_percent\": null,\n  \"cost_allocation_method\": \"by_weight\",\n  \"created_by_id\": 1,\n  \"archived_at\": null,\n  \"created_at\": \"2026-05-28T09:14:11.000000Z\",\n  \"updated_at\": \"2026-05-28T11:02:48.000000Z\"\n}"}]},{"name":"Get Current Default for BOM","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{id}/current-default","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":id","current-default"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns the BOM currently marked default for the same product/variant as the given BOM, excluding the given BOM itself. Used by the UI to name the existing default in a \"Set as Default\" confirmation dialog (e.g. \"This will replace 'Vanilla Latte v2' as the default.\"). Returns `{ \"data\": { id, name, version_number } }` when another default exists; returns `{ \"data\": null }` when there is no other default for that product/variant.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{id}/current-default","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":id","current-default"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 12,\n    \"name\": \"Vanilla Latte v2\",\n    \"version_number\": 2\n  }\n}"}]},{"name":"Explode BOM","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{id}/explode?quantity=10","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":id","explode"],"query":[{"key":"quantity","value":"10","disabled":true}],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Recursively explodes a BOM into its full multi-level component list for a target quantity.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."}},{"name":"List BOM Activity Log","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{bom}/activity-log","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":bom","activity-log"],"variable":[{"key":"bom","value":"1","description":"The bom ID."}]},"description":"Paginated audit trail of changes made to the BOM and its lines - who changed what and when, newest first.\nAuth: requires Bearer token.\nPath param: bom = BOM id.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/boms/{bom}/activity-log","host":["{{baseUrl}}"],"path":["api","manufacturing","boms",":bom","activity-log"],"variable":[{"key":"bom","value":"1","description":"The bom ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"current_page\": 1,\n  \"data\": [\n    {\n      \"id\": 911,\n      \"description\": \"updated\",\n      \"event\": \"updated\",\n      \"subject_type\": \"Bom\",\n      \"subject_id\": 15,\n      \"properties\": {\n        \"attributes\": {\n          \"name\": \"Default Recipe v2\"\n        },\n        \"old\": {\n          \"name\": \"Default Recipe\"\n        }\n      },\n      \"causer_name\": \"Jane Smith\",\n      \"created_at\": \"2026-07-01T14:22:09.000000Z\"\n    }\n  ],\n  \"first_page_url\": \"https://your-domain.sku.io/api/manufacturing/boms/15/activity-log?page=1\",\n  \"from\": 1,\n  \"last_page\": 1,\n  \"last_page_url\": \"https://your-domain.sku.io/api/manufacturing/boms/15/activity-log?page=1\",\n  \"links\": [],\n  \"next_page_url\": null,\n  \"path\": \"https://your-domain.sku.io/api/manufacturing/boms/15/activity-log\",\n  \"per_page\": 15,\n  \"prev_page_url\": null,\n  \"to\": 1,\n  \"total\": 1\n}"}]},{"name":"Export BOMs","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/list/export","host":["{{baseUrl}}"],"path":["api","manufacturing","boms","list","export"]},"description":"Download bills of materials as an XLSX or CSV file.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequires a Personal Access Token with the manufacturing export permission.\n\nA BOM has three independent child collections, so `content_mode` picks which one a row represents. A BOM with no rows in the selected collection still produces one row, with the child columns left blank.\n\nDate-only columns (effective_from, effective_to) are exported as stated. Timestamp columns (created_at, updated_at, archived_at) are converted from UTC to the account timezone so the file matches what the application shows.\n\nThe `workbook` mode is the round-trip shape: its four sheets carry exactly the headings the three import templates accept, so an exported workbook can be edited and imported back without rebuilding the file.\n\nResponds with a binary spreadsheet, not JSON."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/boms/list/export","host":["{{baseUrl}}"],"path":["api","manufacturing","boms","list","export"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary spreadsheet file)"}]},{"name":"Download BOM Import Template","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/import/template","host":["{{baseUrl}}"],"path":["api","manufacturing","boms","import","template"]},"description":"Download the BOM import template — a three-sheet XLSX carrying the accepted columns, a field guide, and reference values from the account (units of measure, existing BOM names, a sample of product SKUs).\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequires a Personal Access Token with the manufacturing import permission.\n\nOne row is one component line. Rows sharing a product SKU and BOM name form one BOM; the BOM-level columns are read from the first valid row of each group. Required columns: product_sku, component_sku, quantity.\n\nUpload the filled file through the data-import endpoints using entity type `boms`.\n\nResponds with a binary spreadsheet, not JSON."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/boms/import/template","host":["{{baseUrl}}"],"path":["api","manufacturing","boms","import","template"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary spreadsheet file)"}]},{"name":"Download BOM Output Lines Import Template","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/outputs/import/template","host":["{{baseUrl}}"],"path":["api","manufacturing","boms","outputs","import","template"]},"description":"Download the import template for BOM output lines — the primary product plus any co-products, by-products and scrap a build yields.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequires a Personal Access Token with the manufacturing import permission.\n\nOne row is one output line, attached to a BOM that already exists. Importing output rows replaces that BOM's entire output set, so a line left out of the file is removed, and each BOM in the file must carry exactly one row with role `primary`. Required columns: product_sku, output_sku, role, quantity.\n\nUpload the filled file through the data-import endpoints using entity type `bom_outputs`.\n\nResponds with a binary spreadsheet, not JSON."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/boms/outputs/import/template","host":["{{baseUrl}}"],"path":["api","manufacturing","boms","outputs","import","template"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary spreadsheet file)"}]},{"name":"Download BOM Operations Import Template","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/boms/operations/import/template","host":["{{baseUrl}}"],"path":["api","manufacturing","boms","operations","import","template"]},"description":"Download the import template for BOM operations — the ordered routing steps a build runs through, with their work centres, times and costs.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nRequires a Personal Access Token with the manufacturing import permission.\n\nOne row is one operation, attached to a BOM that already exists. Importing operation rows replaces that BOM's entire routing, so an operation left out of the file is removed. Sequence must be unique within a BOM. Required columns: product_sku, sequence, operation_name.\n\nUpload the filled file through the data-import endpoints using entity type `bom_operations`.\n\nResponds with a binary spreadsheet, not JSON."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/boms/operations/import/template","host":["{{baseUrl}}"],"path":["api","manufacturing","boms","operations","import","template"]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"(binary spreadsheet file)"}]}]},{"name":"Manufacturing Orders","item":[{"name":"List Manufacturing Orders","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders?per_page=25","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders"],"query":[{"key":"per_page","value":"25","description":"Results per page (default 10).","disabled":true}]},"description":"Paginated list of Manufacturing Orders. Supports Spatie QueryBuilder filters, sorts, and pagination.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Filters** (all optional, all use `filter[key]=value` syntax):\n- `filter[status]` — exact match against `draft|confirmed|in_progress|completed|closed|cancelled`\n- `filter[warehouse_id]` — exact match\n- `filter[is_simple_mode]` — `1` to show only Simple Mode MOs (created via /simple-assemble)\n- `filter[is_disassemble]` — `1` to show only disassembly MOs (Simple Mode with action=disassemble)\n\n**Sort:** `sort=<field>`, prefix with `-` for DESC (e.g. `-created_at`). Defaults to `-created_at`. Allowed sorts: id, mo_number, status, priority, production_method, scheduled_start, scheduled_end, started_at, completed_at, planned_quantity, completed_quantity, created_at, updated_at, warehouse_name, contractor_supplier_name, assigned_to. `warehouse_name`, `contractor_supplier_name`, and `assigned_to` sort by the related warehouse / supplier / assignee name (LEFT JOIN).\n**Pagination:** `page`, `per_page` (default 10).\n\nResponse items are manufacturing order list object (trimmed payload — no component_lines/output_lines). `primary_output_name` / `primary_output_product_id` resolve the primary output line's product, falling back to the BOM output product for draft MOs (so the list can link the Output cell to the product detail page)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders?per_page=25","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders"],"query":[{"key":"per_page","value":"25","description":"Results per page (default 10).","disabled":true}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 1024,\n      \"mo_number\": \"MO-000042\",\n      \"status\": \"confirmed\",\n      \"bom_id\": 88,\n      \"bom_name\": \"Premium Hoodie Assembly v3\",\n      \"warehouse_id\": 2,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"destination_warehouse_id\": 2,\n      \"destination_warehouse_name\": \"Main Warehouse\",\n      \"primary_output_name\": \"Premium Cotton Hoodie - Black - L\",\n      \"primary_output_product_id\": 4551,\n      \"planned_quantity\": 250,\n      \"completed_quantity\": 0,\n      \"production_method\": \"in_house\",\n      \"contractor_supplier_id\": null,\n      \"contractor_supplier_name\": null,\n      \"outsourced_purchase_order_id\": null,\n      \"sales_order_id\": null,\n      \"sales_order_line_id\": null,\n      \"priority\": 10,\n      \"scheduled_start\": \"2026-07-05T08:00:00.000000Z\",\n      \"scheduled_end\": \"2026-07-08T17:00:00.000000Z\",\n      \"started_at\": null,\n      \"completed_at\": null,\n      \"closed_at\": null,\n      \"cancelled_at\": null,\n      \"is_simple_mode\": false,\n      \"is_disassemble\": false,\n      \"internal_reference\": \"Batch Q3-A\",\n      \"assigned_to_id\": 7,\n      \"assigned_to\": {\n        \"id\": 7,\n        \"name\": \"Jamie Rivera\"\n      },\n      \"cost_breakdown\": null,\n      \"has_shortage\": false,\n      \"created_at\": \"2026-07-01T14:32:10.000000Z\",\n      \"updated_at\": \"2026-07-02T09:15:44.000000Z\",\n      \"archived_at\": null\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 5,\n  \"per_page\": 25,\n  \"total\": 118,\n  \"from\": 1,\n  \"to\": 25\n}"}]},{"name":"Create Manufacturing Order","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders"]},"description":"Create a draft MO from a BOM. Hydrates component + output + operation snapshot lines from the BOM.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nFor outsourced orders (production_method: outsourced) the request also accepts: contractor_supplier_id (required for outsourced, integer, must be a supplier flagged as a manufacturing contractor); outsourced_cost_basis (optional, string, one of: toll, hybrid) - how the contractor's product charge is costed into the finished output. toll = you own all component materials and the charge is a processing cost only; hybrid = the contractor supplies additional materials of their own and the charge is capitalized into output cost. When omitted the contractor's default_outsourced_cost_basis applies, falling back to hybrid; service_fee_amount (optional, numeric, min 0). These three fields are rejected for in_house orders.\n\n**Per-unit tolling rate (`service_fee_rate`):** instead of a flat `service_fee_amount`, you can supply a per-unit rate. The backing purchase order carries a projected fee of `rate x planned_quantity`, and each receipt re-trues the fee line to `rate x max(planned_quantity, total produced)` so cost recognition always matches quantity actually received - including over- and under-production. If omitted, the contractor's default rate applies when one is configured.\n\n**Upstream planning (optional, default off).** Send `plan_upstream: true` to also raise the draft orders that supply this one. It applies only when the product being made is contract-manufactured: the stages feeding it are raised recursively, each planned at required ÷ that stage's expected yield (needing 11,280 of a 94%-yield stage plans 12,000), routed to ship into the warehouse that will consume it, and only for the shortfall after stock on hand. Everything raised is a draft, so nothing reaches a vendor until it is confirmed. Omit the field, or send `false`, to create exactly the order you posted — which is what an ordinary multi-level recipe does either way.","body":{"mode":"raw","raw":"{\n  \"bom_id\": 1,\n  \"warehouse_id\": 1,\n  \"destination_warehouse_id\": 1,\n  \"planned_quantity\": 100,\n  \"production_method\": \"in_house\",\n  \"contractor_supplier_id\": 12,\n  \"outsourced_cost_basis\": \"toll\",\n  \"service_fee_rate\": 1.25,\n  \"priority\": 10,\n  \"scheduled_start\": \"2026-04-22\",\n  \"scheduled_end\": \"2026-04-24\",\n  \"notes\": \"Standard production run\",\n  \"internal_reference\": \"BATCH-001\",\n  \"plan_upstream\": true\n}","options":{"raw":{"language":"json"}}}}},{"name":"Get Manufacturing Order","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Return the full Manufacturing Order with its BOM, component/output lines, operations, warehouses, and events.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**BOM line counts (P5/P6):** The nested `bom` object includes `component_lines_count`, `output_lines_count`, and `operations_count` (eager-loaded via `withCount`). For **draft** MOs — which have no materialised `component_lines`/`output_lines`/`operations` rows yet (those are created by ConfirmMoAction) — clients should fall back to these BOM counts when rendering tab badges and the footer summary so the user sees what the MO *will* contain once confirmed.\n\nOnce production has been recorded, `cost_breakdown` is populated with cumulative cost categories: `materials`, `labor`, `overhead`, `fixed` (flat per-run operation costs — never marked up by overhead), `service_fee`, `landed_cost` (outsourced orders only), `total`, `allocation_method`, `recorded_at`, and per-output allocation rows in `outputs[]`. It is null until the first production record.\n\nEach `component_lines[]` row carries `consumption_method` (`forward_flush` | `backflush`) — snapshotted from the BOM at confirmation — and each `operations[]` row carries `fixed_cost` plus `cost_source` (`none` | `actual` | `estimated` | `manual`) — the provenance of its `actual_cost`: `estimated` flags a cost that fell back to the planned duration because no time was logged, and `manual` marks an explicitly supplied cost that is never auto-recalculated (only another explicit cost, or an explicitly logged positive time, replaces it).\n\n**Outsourced cost basis:** `outsourced_cost_basis` is the order's explicit override (`toll` | `hybrid`, null = inherit) and `resolved_outsourced_cost_basis` is the basis actually used for costing (override, else the contractor's `default_outsourced_cost_basis`, else `hybrid`). `resolved_outsourced_cost_basis` is null for in-house orders. The nested `contractor_supplier` object also carries the contractor's `default_outsourced_cost_basis`.\n\n**Serial fields.** `component_lines[].product` and `output_lines[].product` include `tracks_serials` (boolean) and `serial_generation_mode` (`none`, `manual` or `auto`), so a client can tell which lines take `serials` on Record Production.\n\nFor an order raised from a sales order line, `sales_order` (`{id, sales_order_number}`) and `sales_order_line` (`{id, product_id, sku, description}`) name its origin alongside `sales_order_id` / `sales_order_line_id`."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 18,\n  \"mo_number\": \"MO-000018\",\n  \"status\": \"draft\",\n  \"bom_id\": 2,\n  \"bom\": {\n    \"id\": 2,\n    \"product_id\": 412,\n    \"product\": {\n      \"id\": 412,\n      \"sku\": \"WIDGET-A\",\n      \"name\": \"Widget A — Finished\"\n    },\n    \"name\": \"Widget A BOM\",\n    \"version_number\": 1,\n    \"status\": \"active\",\n    \"effective_from\": \"2026-01-01\",\n    \"effective_to\": null,\n    \"is_default\": true,\n    \"is_phantom\": false,\n    \"cost_allocation_method\": \"by_weight\",\n    \"component_lines_count\": 3,\n    \"output_lines_count\": 1,\n    \"operations_count\": 2,\n    \"archived_at\": null,\n    \"created_at\": \"2026-05-20T10:00:00.000000Z\",\n    \"updated_at\": \"2026-05-20T10:00:00.000000Z\"\n  },\n  \"warehouse_id\": 1,\n  \"warehouse\": {\n    \"id\": 1,\n    \"name\": \"Main Warehouse\"\n  },\n  \"destination_warehouse_id\": 1,\n  \"destination_warehouse\": {\n    \"id\": 1,\n    \"name\": \"Main Warehouse\"\n  },\n  \"planned_quantity\": 10,\n  \"completed_quantity\": 0,\n  \"production_method\": \"in_house\",\n  \"contractor_supplier_id\": null,\n  \"outsourced_cost_basis\": null,\n  \"resolved_outsourced_cost_basis\": null,\n  \"outsourced_purchase_order_id\": null,\n  \"sales_order_id\": null,\n  \"sales_order_line_id\": null,\n  \"priority\": 100,\n  \"scheduled_start\": null,\n  \"scheduled_end\": null,\n  \"started_at\": null,\n  \"completed_at\": null,\n  \"closed_at\": null,\n  \"cancelled_at\": null,\n  \"cancelled_reason_code\": null,\n  \"cancelled_reason_notes\": null,\n  \"is_simple_mode\": false,\n  \"is_disassemble\": false,\n  \"notes\": null,\n  \"internal_reference\": null,\n  \"cost_breakdown\": null,\n  \"created_by_id\": 1,\n  \"created_by\": {\n    \"id\": 1,\n    \"name\": \"Dev User\"\n  },\n  \"assigned_to_id\": null,\n  \"component_lines\": [],\n  \"output_lines\": [],\n  \"operations\": [],\n  \"events\": [],\n  \"created_at\": \"2026-05-28T09:00:00.000000Z\",\n  \"updated_at\": \"2026-05-28T09:00:00.000000Z\",\n  \"archived_at\": null\n}"}]},{"name":"Update Manufacturing Order","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Update Manufacturing Order\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n`warehouse_id`, `destination_warehouse_id` and `planned_quantity` are only applied while the order is Draft — from Confirmed onward component requirements and allocations are already sized to them, so the values are pinned.","body":{"mode":"raw","raw":"{\n  \"planned_quantity\": 120,\n  \"priority\": 20,\n  \"notes\": \"Increased to cover backorders\"\n}","options":{"raw":{"language":"json"}}}}},{"name":"Delete Manufacturing Order","request":{"method":"DELETE","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Only draft MOs may be deleted.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."}},{"name":"Confirm Manufacturing Order","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/confirm","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","confirm"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Draft -> Confirmed. Reserves components. Optionally lock in FIFO layer picks keyed by mo_component_line_id.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"priority\": 10,\n  \"scheduled_start\": \"2026-04-22\",\n  \"scheduled_end\": \"2026-04-24\",\n  \"lot_picks\": {\n    \"1\": [\n      101,\n      102\n    ]\n  }\n}","options":{"raw":{"language":"json"}}}}},{"name":"Preview Manufacturing Order Confirmation","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{manufacturingOrder}/confirm-preview","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":manufacturingOrder","confirm-preview"],"variable":[{"key":"manufacturingOrder","value":"1","description":"The manufacturing order ID."}]},"description":"Dry run of Confirm for a Draft manufacturing order. Computes per-component shortages and the largest planned quantity current inventory could support end-to-end, without changing any state. For orders that are no longer Draft, the response contains empty shortages and a reason explaining that the preview only applies before confirmation.\nAuth: requires Bearer token.\nPath param: manufacturingOrder = manufacturing order id.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nThe response also carries `lot_warnings` — non-blocking lot-traceability messages populated when the order's finished good is lot-tracked but one or more BOM components are not (their consumption would not appear in lot genealogy)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{manufacturingOrder}/confirm-preview","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":manufacturingOrder","confirm-preview"],"variable":[{"key":"manufacturingOrder","value":"1","description":"The manufacturing order ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"shortages\": [\n    {\n      \"product_id\": 2,\n      \"product_sku\": \"CMP-001\",\n      \"product_name\": \"Widget Frame\",\n      \"required_quantity\": 250,\n      \"allocated_quantity\": 180,\n      \"short_by_quantity\": 70,\n      \"uom_code\": \"EA\"\n    }\n  ],\n  \"max_producible_quantity\": 72,\n  \"requested_quantity\": 100,\n  \"lot_warnings\": []\n}"}]},{"name":"Unconfirm Manufacturing Order","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{manufacturingOrder}/unconfirm","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":manufacturingOrder","unconfirm"],"variable":[{"key":"manufacturingOrder","value":"1","description":"The manufacturing order ID."}]},"description":"Take the order back to Draft so its planned quantity and warehouses can be edited again: releases its component inventory allocations and discards the BOM-derived component lines, output lines, and operations, which are rebuilt at the current planned quantity on the next Confirm. Operations added manually after confirmation are kept.\nAllowed from Confirmed, and from In Progress while nothing has been produced — starting an order records only a status and a start time, so it is reversible until production is recorded. An In Progress order also has its start time cleared.\nRefused with 422 once work has been recorded against the order. The response carries a `code`: `production_recorded` (production has been recorded, or completed quantity is above zero), `components_consumed` (component consumption or scrap has been recorded), `operations_worked` (a BOM-derived operation has been started, completed, or costed), or `status` (the order is in a status with no path back to Draft, e.g. Partially Completed or Completed).\nAuth: requires Bearer token.\nPath param: manufacturingOrder = manufacturing order id.\nBody: reason (optional, string, max 1000) recorded on the order's event log together with the status the order came back from.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"reason\": \"Confirmed against the wrong warehouse\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{manufacturingOrder}/unconfirm","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":manufacturingOrder","unconfirm"],"variable":[{"key":"manufacturingOrder","value":"1","description":"The manufacturing order ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 42,\n  \"mo_number\": \"MO-00042\",\n  \"status\": \"draft\",\n  \"bom_id\": 15,\n  \"warehouse_id\": 3,\n  \"destination_warehouse_id\": 3,\n  \"planned_quantity\": 100,\n  \"completed_quantity\": 0,\n  \"production_method\": \"in_house\",\n  \"contractor_supplier_id\": null,\n  \"outsourced_purchase_order_id\": null,\n  \"sales_order_id\": null,\n  \"sales_order_line_id\": null,\n  \"priority\": 0,\n  \"scheduled_start\": null,\n  \"scheduled_end\": null,\n  \"started_at\": null,\n  \"completed_at\": null,\n  \"closed_at\": null,\n  \"cancelled_at\": null,\n  \"cancelled_reason_code\": null,\n  \"cancelled_reason_notes\": null,\n  \"is_simple_mode\": false,\n  \"is_disassemble\": false,\n  \"notes\": null,\n  \"internal_reference\": null,\n  \"cost_breakdown\": null,\n  \"created_by_id\": 1,\n  \"assigned_to_id\": null,\n  \"created_at\": \"2026-06-28T10:15:00.000000Z\",\n  \"updated_at\": \"2026-07-01T14:22:09.000000Z\",\n  \"archived_at\": null\n}"}]},{"name":"Start Manufacturing Order","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/start","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","start"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Confirmed -> In Progress.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nBefore gating, any backordered (planned) component reservations are hardened from live stock automatically, so material received since confirmation unblocks the start without manual steps. Each shortage row in the 422 response includes the backordered remainder (`planned_quantity`, `planned_stock_quantity`) and its purchase-order coverage (`covered_by_po`, `purchase_order_id`, `coverage_eta`) so the wait is explainable.","body":{"mode":"raw","raw":"{\n  \"started_at\": \"2026-04-22T08:00:00Z\",\n  \"assigned_to_id\": 1,\n  \"notes\": \"Production run kicked off\"\n}","options":{"raw":{"language":"json"}}}}},{"name":"Record Production","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/record-production","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","record-production"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Record actual component consumption, output produced, and operation actuals for an in-progress MO. Posts inventory movements (consumption + receipts) and creates FIFO layers for outputs.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Lot tracking validation (B23/B24):**\n- `outputs[].batch_number` is **required** when the output product is `is_lot_tracked = true`.\n- `outputs[].expiry_date` is **required** when the output product has `lot_tracking_method = fefo`.\n- Validation errors are returned per-output with dot notation, e.g. `outputs.0.batch_number`.\n\nThe MO Show endpoint (`GET /api/manufacturing/manufacturing-orders/{id}`) exposes `output_lines[].product` and `component_lines[].product` with `is_lot_tracked`, `lot_tracking_method`, `shelf_life_days`, `near_expiry_threshold_days`, and `min_remaining_shelf_life_days` so clients can drive UI gating before submitting.\n\n**Component lot consumption override (FEFO suggest-with-override):**\n- Each `components[]` entry may carry an optional `lot_allocation[]` of `{fifo_layer_id, quantity}` to choose exactly which lots of a lot-tracked component are consumed. Omit it to consume by automatic FEFO/FIFO.\n- The allocation must reconcile to that component's total draw this run (`actual_consumed_quantity + actual_scrap_quantity`), reference active FIFO layers of that component product + the MO's source warehouse, and respect the tenant's lot-override policy (under `enforce`, consuming expired stock or a later-expiry lot for a FEFO product is hard-blocked).\n- The legacy `components[].fifo_layer_ids[]` (restrict-eligibility, FEFO-ordered) is still accepted; `lot_allocation[]` takes precedence when both are present.\n- Validation errors are returned per row with dot notation, e.g. `components.0.lot_allocation.1.fifo_layer_id`.\n\n**Serial numbers (serialized products):**\n- `components[].serials[]` (optional): the serials of the component units used. They become `consumed`, and the scrap share becomes `scrapped`. Units without a named serial use the oldest serials in stock at the MO's source warehouse. Under Require exact match, a named serial that isn't in stock there fails with **422**, `code = serials`.\n- `outputs[].serials[]` (optional): the serials of the units produced. An Auto-generation product generates any not entered.\n- Reversing the production iteration puts the component serials back in stock and removes the output serials.\n\n**Over-consumption guard (BQ8):** if `components[].actual_consumed_quantity` exceeds the quantity reserved (Allocated) for that component line at Start, the call returns **422** with `code = over_consumption` and an `offending[]` array (per-component `requested_quantity` vs `allocated_quantity`) instead of a 500. The whole call rolls back — MO status and consumption are unchanged.\n\nDestination bin: each output may optionally set `warehouse_location_id` — the bin the produced quantity is received into. When omitted, the assembly's preferred bin (or the warehouse's default storage location) is used.\n\n**Request `components` (present, may be empty):** the `components` array must always be present, but it no longer needs at least one entry. Only `forward_flush` component lines need entries — omit `backflush` lines entirely (they are consumed automatically, prorated to the recorded output), and submit `\"components\": []` when every component line on the order is backflush. Including an entry for a backflush line acts as a manual override of the automatic quantity for that run.\n\n**Backflush components:** component lines with `consumption_method = backflush` are consumed automatically — for each backflush line without an explicit `components[]` entry, the recorded output is converted to that line's BOM-implied usage (per-unit usage multiplied by cumulative primary output produced — deliberately uncapped, so over-yield beyond the planned quantity consumes proportionally more — minus what earlier production records already consumed) and issued through the same FIFO/lot machinery as operator entries. Consumption beyond the quantity reserved at confirmation automatically tops up the line's allocation from free (unreserved) stock; when free stock cannot cover the excess the request fails with the same structured 422s as a manual over-issue (`code = insufficient_inventory` when the stock physically does not exist, `code = over_consumption` when it exists but is reserved by other documents) and the whole call rolls back. Submitting an explicit `components[]` entry for a backflush line overrides the automatic quantity for that run. `forward_flush` lines are only consumed when an explicit entry is submitted.\n\n**Response `cost_breakdown`:** the returned manufacturing order carries a cumulative `cost_breakdown` object: `materials` (FIFO value consumed so far), `labor` (time-based operation labour), `overhead` (labour multiplied by the work-center overhead percent), `fixed` (flat per-run operation costs — never marked up by overhead), `service_fee` and `landed_cost` (outsourced orders only), `total`, `allocation_method`, `recorded_at`, and per-output allocation rows in `outputs[]`.\n\n**Operation costing without a timer:** each `operations[]` row in the response reports `fixed_cost`, `actual_cost`, and `cost_source` — `none` (no cost recorded), `actual` (from logged time or a flat fixed-cost operation), `estimated` (fell back to the planned duration because no time was logged), or `manual` (an explicitly supplied cost that is never auto-recalculated — only another explicit cost, or an explicitly logged positive time, replaces it).\n\n**Insufficient stock guard:** if a component's FIFO layers in the MO's source warehouse cannot cover the quantity to consume (forward-flush or backflush), the call returns **422** with `code = insufficient_inventory` and an `offending[]` array (`product_id`, `product_sku`, `product_name`, `warehouse_id`, `requested_quantity`, `available_quantity`). The whole call rolls back — no movements, FIFO layers, or production iterations are written.\n\n**Explicit lot allocation shortages:** shortage failures inside an explicit `lot_allocation[]` return the same **422** `code = insufficient_inventory` envelope (previously an internal error). A named lot that is no longer an active layer for the component at consumption time, or a lot that cannot cover its requested quantity, reports the usual `product_id`, `product_sku`, `product_name`, `warehouse_id`, `requested_quantity`, `available_quantity` row. A lot-level overdraw detected mid-consumption (e.g. duplicate entries naming the same lot whose combined quantity exceeds its availability) identifies the lot with `fifo_layer_id` and reports the layer's real state at the failed draw — `layer_original_quantity`, `layer_fulfilled_quantity` (includes quantities drawn earlier in the same rolled-back call), and `layer_remaining_quantity` — rather than a fabricated requested/available pair. The whole call rolls back.\n\n**Downward cost revision guard:** operation cost revisions between production records are recognized as SIGNED deltas on the next iteration's journal — negative buckets post as balanced flipped legs (e.g. a debit to Direct Labor Applied) and a net-zero reclassification still posts its offsetting legs. When a negative conversion delta exceeds the value this run's output FIFO layers can absorb (a layer's cost would drop below zero), the call returns **422** with `code = cost_revision_exceeds_layer_value`, the signed `conversion_delta`, and an `offending[]` array (`output_line_id`, `fifo_layer_id`, `product_id`, `product_sku`, `product_name`, `layer_cost`, `revision_share`, `resulting_cost`). The whole call rolls back — reverse the over-costed earlier iteration and re-record it with the corrected operation costs.\n\n**created_lots.** The response reports the lots this production run created, each with its `fifo_layer_id` and `batch_number`. Use it to attach the batch production record (21 CFR 111.260), a finished-goods Certificate of Analysis, or a line photo to the lot once the run is recorded, via `POST /api/v2/fifo-layers/{fifoLayer}/documents`. It is scoped to THIS run — a later run against the same manufacturing order reports only its own lots.","body":{"mode":"raw","raw":"{\n  \"completed_quantity\": 100,\n  \"production_date\": \"2026-04-23\",\n  \"notes\": \"Full run completed\",\n  \"components\": [\n    {\n      \"mo_component_line_id\": 1,\n      \"actual_consumed_quantity\": 200,\n      \"actual_scrap_quantity\": 4,\n      \"lot_allocation\": [\n        {\n          \"fifo_layer_id\": 101,\n          \"quantity\": 150\n        },\n        {\n          \"fifo_layer_id\": 102,\n          \"quantity\": 54\n        }\n      ],\n      \"scrap_reason_code\": \"spillage\"\n    },\n    {\n      \"mo_component_line_id\": 2,\n      \"actual_consumed_quantity\": 100,\n      \"actual_scrap_quantity\": 0\n    }\n  ],\n  \"outputs\": [\n    {\n      \"mo_output_line_id\": 1,\n      \"actual_produced_quantity\": 100,\n      \"batch_number\": \"LOT-2026-04-001\",\n      \"expiry_date\": \"2027-04-23\",\n      \"catch_weight\": null,\n      \"warehouse_location_id\": 15\n    }\n  ],\n  \"operation_actuals\": [\n    {\n      \"mo_operation_id\": 1,\n      \"actual_time_minutes\": 35,\n      \"operator_id\": 1,\n      \"status\": \"completed\",\n      \"notes\": \"Ran 5 minutes over\"\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/record-production","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","record-production"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 14,\n  \"mo_number\": \"MO-000014\",\n  \"status\": \"in_progress\",\n  \"bom_id\": 12,\n  \"warehouse_id\": 1,\n  \"destination_warehouse_id\": 1,\n  \"planned_quantity\": 100,\n  \"completed_quantity\": 100,\n  \"production_method\": \"in_house\",\n  \"contractor_supplier_id\": null,\n  \"outsourced_purchase_order_id\": null,\n  \"sales_order_id\": null,\n  \"sales_order_line_id\": null,\n  \"priority\": 0,\n  \"scheduled_start\": \"2026-04-22T08:00:00.000000Z\",\n  \"scheduled_end\": \"2026-04-24T17:00:00.000000Z\",\n  \"started_at\": \"2026-04-22T08:05:00.000000Z\",\n  \"completed_at\": null,\n  \"closed_at\": null,\n  \"cancelled_at\": null,\n  \"cancelled_reason_code\": null,\n  \"cancelled_reason_notes\": null,\n  \"is_simple_mode\": false,\n  \"is_disassemble\": false,\n  \"notes\": \"Full run completed\",\n  \"internal_reference\": null,\n  \"cost_breakdown\": {\n    \"materials\": 1240.5,\n    \"labor\": 26.25,\n    \"overhead\": 2.625,\n    \"fixed\": 75,\n    \"service_fee\": 0,\n    \"landed_cost\": 0,\n    \"total\": 1344.375,\n    \"allocation_method\": \"by_value\",\n    \"outputs\": [\n      {\n        \"mo_output_line_id\": 1,\n        \"product_id\": 1,\n        \"role\": \"primary\",\n        \"produced_quantity\": 100,\n        \"allocated_cost\": 1344.375,\n        \"allocated_this_step\": 1344.375,\n        \"unit_cost\": 13.44375\n      }\n    ],\n    \"recorded_at\": \"2026-04-23T14:12:09+00:00\"\n  },\n  \"created_by_id\": 1,\n  \"assigned_to_id\": null,\n  \"component_lines\": [\n    {\n      \"id\": 1,\n      \"manufacturing_order_id\": 14,\n      \"bom_component_line_id\": 31,\n      \"product_id\": 2,\n      \"product_variant_id\": null,\n      \"expected_quantity\": 200,\n      \"actual_consumed_quantity\": 200,\n      \"expected_scrap_quantity\": 4,\n      \"actual_scrap_quantity\": 4,\n      \"scrap_reason_code\": \"spillage\",\n      \"uom_id\": 1,\n      \"factor_to_stock\": 1,\n      \"consumption_method\": \"forward_flush\",\n      \"consumption_posted_at\": \"2026-04-23T14:12:09.000000Z\",\n      \"estimated_cost\": 100\n    },\n    {\n      \"id\": 2,\n      \"manufacturing_order_id\": 14,\n      \"bom_component_line_id\": 32,\n      \"product_id\": 3,\n      \"product_variant_id\": null,\n      \"expected_quantity\": 100,\n      \"actual_consumed_quantity\": 100,\n      \"expected_scrap_quantity\": 0,\n      \"actual_scrap_quantity\": 0,\n      \"scrap_reason_code\": null,\n      \"uom_id\": 1,\n      \"factor_to_stock\": 1,\n      \"consumption_method\": \"backflush\",\n      \"consumption_posted_at\": \"2026-04-23T14:12:09.000000Z\",\n      \"estimated_cost\": 100\n    }\n  ],\n  \"output_lines\": [\n    {\n      \"id\": 1,\n      \"manufacturing_order_id\": 14,\n      \"bom_output_line_id\": 21,\n      \"product_id\": 1,\n      \"product_variant_id\": null,\n      \"role\": \"primary\",\n      \"expected_quantity\": 100,\n      \"actual_produced_quantity\": 100,\n      \"uom_id\": 1,\n      \"allocated_cost\": 1344.375,\n      \"batch_number\": \"LOT-2026-04-001\",\n      \"expiry_date\": \"2027-04-23\"\n    }\n  ],\n  \"operations\": [\n    {\n      \"id\": 1,\n      \"manufacturing_order_id\": 14,\n      \"source\": \"from_bom\",\n      \"bom_operation_id\": 7,\n      \"sequence\": 10,\n      \"name\": \"Mix\",\n      \"type\": \"setup\",\n      \"operation_type\": \"setup\",\n      \"expected_duration_minutes\": 30,\n      \"hourly_rate\": 45,\n      \"status\": \"completed\",\n      \"scheduled_start\": null,\n      \"started_at\": null,\n      \"completed_at\": \"2026-04-23T14:12:09.000000Z\",\n      \"actual_time_minutes\": 35,\n      \"operator_id\": 1,\n      \"default_operator_id\": null,\n      \"work_center_id\": 1,\n      \"fixed_cost\": 75,\n      \"actual_cost\": 101.25,\n      \"cost_source\": \"actual\",\n      \"notes\": \"Ran 5 minutes over\",\n      \"is_modified_from_bom\": false\n    }\n  ],\n  \"events\": [],\n  \"created_at\": \"2026-04-20T09:00:00.000000Z\",\n  \"updated_at\": \"2026-04-23T14:12:09.000000Z\",\n  \"archived_at\": null,\n  \"created_lots\": [\n    {\n      \"fifo_layer_id\": 20514,\n      \"batch_number\": \"MO-812171-2026-08-19\"\n    }\n  ]\n}"}]},{"name":"Complete Manufacturing Order","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/complete","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","complete"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"In Progress -> Completed. Finalizes variance calculations.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}}},{"name":"Cancel Manufacturing Order","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/cancel","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","cancel"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Cancel a manufacturing order. Transitions any non-closed status → Cancelled and releases any inventory reservations.\n\n**Required body fields:**\n- `reason_code` (string, required) — must be one of the 5 allowed values:\n  - `plan_changed` — Plan changed\n  - `materials_unavailable` — Materials unavailable\n  - `customer_cancellation` — Customer cancellation\n  - `equipment_issue` — Equipment issue\n  - `other` — Other\n\n**Optional body fields:**\n- `notes` (string, nullable, max 2000) — Free-text reason explanation\n- `reason_notes` (string, nullable, max 2000) — **Alias for `notes`** — accepted for API consistency. If `notes` is absent and `reason_notes` is provided, it is copied into `notes` during validation. Sending both means `notes` wins.\n\n**Returns 422 if `reason_code` is missing or not in the allowed list.**\n**Authentication:** Bearer token required.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"reason_code\": \"customer_cancellation\",\n  \"notes\": \"Sales order cancelled upstream\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/cancel","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","cancel"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 1,\n  \"manufacturing_order_number\": \"MO-000001\",\n  \"status\": \"cancelled\",\n  \"bom_id\": 12,\n  \"warehouse_id\": 3,\n  \"quantity_planned\": \"10.000000\",\n  \"quantity_produced\": \"0.000000\",\n  \"scheduled_start\": \"2026-06-01T00:00:00.000000Z\",\n  \"scheduled_end\": \"2026-06-03T00:00:00.000000Z\",\n  \"started_at\": null,\n  \"completed_at\": null,\n  \"closed_at\": \"2026-05-28T14:32:11.000000Z\",\n  \"notes\": null,\n  \"created_at\": \"2026-05-25T09:00:00.000000Z\",\n  \"updated_at\": \"2026-05-28T14:32:11.000000Z\"\n}"}]},{"name":"Close Manufacturing Order","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/close","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","close"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Completed -> Closed. Locks the MO from further changes.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"notes\": \"Costing locked\"\n}","options":{"raw":{"language":"json"}}}}},{"name":"Reopen Manufacturing Order","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/reopen","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","reopen"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Closed -> Partially Completed (or Completed if completed_quantity >= planned_quantity). Required reason (string, max 2000) is captured in the Reopened event payload.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).","body":{"mode":"raw","raw":"{\n  \"reason\": \"Correcting component consumption\"\n}","options":{"raw":{"language":"json"}}}}},{"name":"Generate Accounting Transaction (MO)","request":{"method":"POST","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/generate-accounting-transaction","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","generate-accounting-transaction"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Generate (or rebuild) the WIP-routed GL journals for this MO — ONE journal per production iteration, each dated to that iteration's production date (so multi-day / multi-period production is period-accurate). Conversion costs (labor + overhead + outsourced service) are capitalized into finished goods through Work In Process:\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n- Dr Work In Process (total)\n- Cr Raw Materials Inventory (materials), Cr Direct Labor Applied (labor), Cr Manufacturing Overhead Applied (overhead), Cr Accrued Purchases (outsourced service fee)\n- Dr Finished Goods Inventory (total), Cr Work In Process (total)\n\nWIP nets to zero. Each journal links polymorphically to the MO and is tagged with its mo_production_iteration_id. Requires the Work In Process / Direct Labor Applied / Manufacturing Overhead Applied nominal codes to be mapped (Settings -> Nominal Code Mappings). No request body. Returns an ARRAY of accounting transaction object (one per iteration) with their journal lines. Each iteration's journal is also posted automatically as production is recorded (when accounting is enabled), and removed if the iteration is reversed."},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/generate-accounting-transaction","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","generate-accounting-transaction"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"message\": \"Production journals generated successfully.\",\n  \"data\": [\n    {\n      \"id\": 5012,\n      \"display_status\": {\n        \"label\": \"Synced\",\n        \"color\": \"success\"\n      },\n      \"total\": 37,\n      \"reference\": \"Production MO-000001 #1: QA-COFFEE-250\",\n      \"transaction_date\": \"2026-06-05T00:00:00+00:00\",\n      \"type\": \"manufacturing_order\"\n    }\n  ]\n}"}]},{"name":"MO Events (Audit Log)","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/events","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","events"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns audit log of every status transition and significant action on the MO.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."}},{"name":"List MO Inventory Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/inventory-movements","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","inventory-movements"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Paginated list of inventory movements created by this Manufacturing Order. Includes movements linked to the MO itself, to any of its component lines (consumption + scrap) and to any of its output lines (production output).\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Query parameters:**\n- `per_page` — items per page (default: 15)\n- `filter[search]` — search by product SKU or name"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/inventory-movements","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","inventory-movements"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 4012,\n      \"type\": \"production_output\",\n      \"inventory_status\": \"on_hand\",\n      \"quantity\": 10,\n      \"reference\": \"MO-000014\",\n      \"link_type\": \"App\\\\Models\\\\MoOutputLine\",\n      \"link_id\": 88,\n      \"parent_link_id\": null,\n      \"detail_link_id\": null,\n      \"product_id\": 412,\n      \"product_sku\": \"KIT-A\",\n      \"product_name\": \"Widget Kit\",\n      \"warehouse_id\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"unit_cost\": 7.25,\n      \"layer\": {\n        \"fifo_layer_id\": 19,\n        \"name\": null\n      },\n      \"fifo_layer_id\": 19,\n      \"inventory_movement_date\": \"2026-04-15T09:30:00.000000Z\",\n      \"created_at\": \"2026-04-15T09:30:00.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"List MO FIFO Layers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/fifo-layers","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","fifo-layers"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Paginated list of FIFO layers CREATED by this Manufacturing Order (production output layers). Layers consumed by this MO are surfaced on each layer's own detail view.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Query parameters:**\n- `per_page` — items per page (default: 15)\n- `filter[search]` — search by product SKU or name"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/fifo-layers","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","fifo-layers"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 19,\n      \"product_id\": 412,\n      \"warehouse_id\": 1,\n      \"batch_number\": \"LOT-2026-04-001\",\n      \"manufacture_date\": \"2026-04-15\",\n      \"expiry_date\": \"2027-04-15\",\n      \"original_quantity\": 50,\n      \"fulfilled_quantity\": 12,\n      \"quantity_on_hand\": 38,\n      \"available_quantity\": 38,\n      \"avg_cost\": 7.25,\n      \"total_cost\": 362.5,\n      \"fifo_layer_date\": \"2026-04-15T00:00:00.000000Z\",\n      \"created_at\": \"2026-04-15T09:30:00.000000Z\",\n      \"link_type\": \"App\\\\Models\\\\MoOutputLine\",\n      \"link_id\": 88\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Get Manufacturing Order Output Line","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/mo-output-lines/{id}","host":["{{baseUrl}}"],"path":["api","manufacturing","mo-output-lines",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Show a single Manufacturing Output line — the canonical detail view used by the standalone Manufacturing Output page.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the output-line resource plus three derived blocks:\n- `fifo_layer` — the FIFO layer this output created (one per output, max)\n- `inventory_movement` — the single production-output movement created when the line posted\n- `layer_movements` — every movement that touched the FIFO layer this output produced: the production movement itself plus any downstream consumption (sales, transfers, further manufacturing). Drives the Movements tab.\n- `production_date` — top-level canonical date (movement's `inventory_movement_date` when available, else MO `completed_at`)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/mo-output-lines/{id}","host":["{{baseUrl}}"],"path":["api","manufacturing","mo-output-lines",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 88,\n    \"manufacturing_order_id\": 14,\n    \"bom_output_line_id\": 42,\n    \"product_id\": 412,\n    \"role\": \"primary\",\n    \"expected_quantity\": 10,\n    \"actual_produced_quantity\": 10,\n    \"allocated_cost\": 72.5,\n    \"batch_number\": \"LOT-2026-04-001\",\n    \"expiry_date\": \"2027-04-15\",\n    \"manufacturing_order\": {\n      \"id\": 14,\n      \"mo_number\": \"MO-000014\",\n      \"status\": \"completed\"\n    },\n    \"product\": {\n      \"id\": 412,\n      \"sku\": \"KIT-A\",\n      \"name\": \"Widget Kit\"\n    },\n    \"unit_of_measure\": {\n      \"id\": 1,\n      \"code\": \"EA\",\n      \"name\": \"Each\"\n    },\n    \"fifo_layer\": {\n      \"id\": 19,\n      \"original_quantity\": 50,\n      \"available_quantity\": 38,\n      \"fulfilled_quantity\": 12,\n      \"avg_cost\": 7.25,\n      \"total_cost\": 362.5,\n      \"fifo_layer_date\": \"2026-04-15T00:00:00.000000Z\",\n      \"created_at\": \"2026-04-15T09:30:00.000000Z\"\n    },\n    \"inventory_movement\": {\n      \"id\": 4012,\n      \"type\": \"production_output\",\n      \"inventory_status\": \"on_hand\",\n      \"quantity\": 10,\n      \"inventory_movement_date\": \"2026-04-15T09:30:00.000000Z\",\n      \"created_at\": \"2026-04-15T09:30:00.000000Z\"\n    },\n    \"production_date\": \"2026-04-15T09:30:00.000000Z\"\n  }\n}"}]},{"name":"Get Manufacturing Order Component Line","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/mo-component-lines/{id}","host":["{{baseUrl}}"],"path":["api","manufacturing","mo-component-lines",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Show a single Manufacturing Consumption (component) line — the detail view used by the standalone Manufacturing Consumption page.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nReturns the component-line resource plus:\n- `inventory_movements` — every movement attached to this consumption line (one consumption row per FIFO layer drawn, plus optionally one scrap row)\n- `layers_drawn` — denormalised summary of the FIFO layers consumed (with quantity taken, unit cost, layer date)\n- `consumption_date` — top-level canonical date (component `consumption_posted_at`, falling back to first movement date or MO `completed_at`)\n\nWhen allocation data is loaded, each component line includes its reservation state: `allocated_quantity` (hard-reserved), `planned_quantity` (backordered remainder awaiting stock), `fulfilled_quantity` (already consumed) — each also in stock units — plus `coverage` (the covering purchase order and ETA, or null) and `readiness` (`ready`, `partial`, `backordered`, or `contractor`)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/mo-component-lines/{id}","host":["{{baseUrl}}"],"path":["api","manufacturing","mo-component-lines",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 211,\n    \"manufacturing_order_id\": 14,\n    \"bom_component_line_id\": 305,\n    \"product_id\": 305,\n    \"expected_quantity\": 22,\n    \"expected_scrap_quantity\": 2,\n    \"actual_consumed_quantity\": 22,\n    \"actual_scrap_quantity\": 2,\n    \"scrap_reason_code\": null,\n    \"is_optional\": false,\n    \"consumption_method\": \"backflush\",\n    \"consumption_posted_at\": \"2026-04-15T09:30:00.000000Z\",\n    \"estimated_cost\": 100,\n    \"manufacturing_order\": {\n      \"id\": 14,\n      \"mo_number\": \"MO-000014\",\n      \"status\": \"completed\"\n    },\n    \"product\": {\n      \"id\": 305,\n      \"sku\": \"WIDGET-A\",\n      \"name\": \"Widget Component A\"\n    },\n    \"unit_of_measure\": {\n      \"id\": 1,\n      \"code\": \"EA\",\n      \"name\": \"Each\"\n    },\n    \"factor_to_stock\": 1,\n    \"consumption_date\": \"2026-04-15T09:30:00.000000Z\",\n    \"inventory_movements\": [\n      {\n        \"id\": 4010,\n        \"type\": \"manufacturing_consumption\",\n        \"inventory_status\": \"on_hand\",\n        \"quantity\": -20,\n        \"fifo_layer_id\": 14,\n        \"unit_cost\": 3,\n        \"inventory_movement_date\": \"2026-04-15T09:30:00.000000Z\"\n      },\n      {\n        \"id\": 4011,\n        \"type\": \"manufacturing_scrap\",\n        \"inventory_status\": \"on_hand\",\n        \"quantity\": -2,\n        \"fifo_layer_id\": 14,\n        \"unit_cost\": 3,\n        \"inventory_movement_date\": \"2026-04-15T09:30:00.000000Z\"\n      }\n    ],\n    \"layers_drawn\": [\n      {\n        \"fifo_layer_id\": 14,\n        \"quantity_taken\": 20,\n        \"unit_cost\": 3,\n        \"fifo_layer_date\": \"2026-03-01T00:00:00.000000Z\",\n        \"movement_id\": 4010,\n        \"movement_type\": \"manufacturing_consumption\"\n      },\n      {\n        \"fifo_layer_id\": 14,\n        \"quantity_taken\": 2,\n        \"unit_cost\": 3,\n        \"fifo_layer_date\": \"2026-03-01T00:00:00.000000Z\",\n        \"movement_id\": 4011,\n        \"movement_type\": \"manufacturing_scrap\"\n      }\n    ],\n    \"is_contractor_supplied\": false,\n    \"allocated_quantity\": 4,\n    \"planned_quantity\": 2,\n    \"fulfilled_quantity\": 0,\n    \"allocated_stock_quantity\": 4,\n    \"planned_stock_quantity\": 2,\n    \"fulfilled_stock_quantity\": 0,\n    \"coverage\": {\n      \"po_number\": \"PO-1187\",\n      \"purchase_order_id\": 341,\n      \"eta\": \"2026-09-12\",\n      \"is_tight\": false\n    },\n    \"readiness\": \"partial\"\n  }\n}"}]},{"name":"List Output Line Inventory Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/mo-output-lines/{id}/inventory-movements","host":["{{baseUrl}}"],"path":["api","manufacturing","mo-output-lines",":id","inventory-movements"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Paginated inventory movements scoped to a single MO output line — every movement that touches the FIFO layer this output produced. Includes the production-output movement itself and any downstream consumption of the batch (sales, transfers, further manufacturing). Backs the Movements tab on the Manufacturing Output detail page.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Query parameters:**\n- `per_page` — items per page (default: 15)\n- `filter[search]` — search by product SKU or name"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/mo-output-lines/{id}/inventory-movements","host":["{{baseUrl}}"],"path":["api","manufacturing","mo-output-lines",":id","inventory-movements"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 8,\n      \"type\": \"manufacturing_output\",\n      \"inventory_status\": \"on_hand\",\n      \"quantity\": 10,\n      \"reference\": null,\n      \"link_type\": \"App\\\\Models\\\\MoOutputLine\",\n      \"link_id\": 1,\n      \"parent_link_id\": null,\n      \"detail_link_id\": null,\n      \"product_id\": 4,\n      \"product_sku\": \"QA-COFFEE-250\",\n      \"product_name\": \"Coffee 250g Pouch\",\n      \"warehouse_id\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"unit_cost\": 3.58,\n      \"layer\": {\n        \"fifo_layer_id\": 5,\n        \"name\": null\n      },\n      \"fifo_layer_id\": 5,\n      \"inventory_movement_date\": \"2026-06-02T20:36:22.000000Z\",\n      \"created_at\": \"2026-06-02T20:36:22.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"List Output Line FIFO Layers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/mo-output-lines/{id}/fifo-layers","host":["{{baseUrl}}"],"path":["api","manufacturing","mo-output-lines",":id","fifo-layers"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Paginated FIFO layers scoped to a single MO output line. Always returns at most one row — the layer this output produced — but shapes the response as a paginated collection so the frontend can reuse the same list view contract as the MO-scoped equivalent.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Query parameters:**\n- `per_page` — items per page (default: 15)\n- `filter[search]` — search by product SKU or name"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/mo-output-lines/{id}/fifo-layers","host":["{{baseUrl}}"],"path":["api","manufacturing","mo-output-lines",":id","fifo-layers"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5,\n      \"product_id\": 4,\n      \"warehouse_id\": 1,\n      \"batch_number\": \"MO-000001-2026-06-02\",\n      \"manufacture_date\": \"2026-06-02\",\n      \"expiry_date\": null,\n      \"original_quantity\": 10,\n      \"fulfilled_quantity\": 0,\n      \"available_quantity\": 10,\n      \"avg_cost\": 3.58,\n      \"total_cost\": 35.81,\n      \"fifo_layer_date\": \"2026-06-02T00:00:00.000000Z\",\n      \"created_at\": \"2026-06-02T20:36:22.000000Z\",\n      \"link_type\": \"App\\\\Models\\\\MoOutputLine\",\n      \"link_id\": 1\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"List MO Production Iterations","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/production-iterations","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","production-iterations"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Paginated list of production iterations for this Manufacturing Order. Each iteration is one call to Record Production; includes restore-cost information and a reversibility flag.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Query parameters:**\n- `per_page` — items per page (default: 15)\n- `sort` — sort field. Prefix with `-` for DESC. Allowed: id, iteration_number, production_date, output_quantity, total_input_cost, created_at. Default: `-iteration_number`\n- `filter[is_reversed]` — true/false\n- `filter[iteration_number]` — exact match\n- `filter[search]` — LIKE on iteration_number / reverse_reason\n- `filter[date_from]`, `filter[date_to]` — production_date range (Y-m-d)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/production-iterations","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","production-iterations"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": \"0fa1c2bb-8b1b-4c5a-9d63-1f4b5e6d7c80\",\n      \"manufacturing_order_id\": 1,\n      \"iteration_number\": 1,\n      \"created_by\": {\n        \"id\": 1,\n        \"name\": \"Kalvin Mizzi\"\n      },\n      \"production_date\": \"2026-06-02T20:36:22.000000Z\",\n      \"output_quantity\": 10,\n      \"total_input_cost\": 35.81,\n      \"total_allocated_cost\": 35.81,\n      \"is_reversed\": false,\n      \"reversed_at\": null,\n      \"reversed_by\": null,\n      \"reverse_reason\": null,\n      \"reversible\": true,\n      \"non_reversible_reason\": null,\n      \"movement_count\": 5,\n      \"output_fifo_layer_id\": 5,\n      \"created_at\": \"2026-06-02T20:36:22.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Get Production Iteration","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/production-iterations/{id}","host":["{{baseUrl}}"],"path":["api","manufacturing","production-iterations",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Returns a single production iteration with all relations eagerly loaded (manufacturing order, created_by user, reversed_by user) plus the output FIFO layer expanded. Backs the standalone Production Iteration detail page.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Path parameters:**\n- `moProductionIteration` (string, UUID) — the iteration's UUID primary key. Iteration IDs are globally unique so no MO scope is required.\n\n**Notes:**\n- `reversible` / `non_reversible_reason` reflect whether the iteration can still be reversed (e.g. blocked by downstream consumption of its output layer).\n- `output_fifo_layer` is the single FIFO layer this iteration produced (nullable for outsourced or partial iterations)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/production-iterations/{id}","host":["{{baseUrl}}"],"path":["api","manufacturing","production-iterations",":id"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": \"0fa1c2bb-8b1b-4c5a-9d63-1f4b5e6d7c80\",\n    \"manufacturing_order_id\": 1,\n    \"manufacturing_order\": {\n      \"id\": 1,\n      \"mo_number\": \"MO-000001\",\n      \"status\": \"in_progress\"\n    },\n    \"iteration_number\": 1,\n    \"created_by\": {\n      \"id\": 1,\n      \"name\": \"Administrator\"\n    },\n    \"production_date\": \"2026-06-03T04:32:22.000000Z\",\n    \"output_quantity\": 10,\n    \"total_input_cost\": 35.81,\n    \"total_allocated_cost\": 35.81,\n    \"is_reversed\": false,\n    \"reversed_at\": null,\n    \"reversed_by\": null,\n    \"reverse_reason\": null,\n    \"reversible\": true,\n    \"non_reversible_reason\": null,\n    \"movement_count\": 4,\n    \"output_fifo_layer_id\": 5,\n    \"output_fifo_layer\": {\n      \"id\": 5,\n      \"original_quantity\": 10,\n      \"available_quantity\": 10,\n      \"fulfilled_quantity\": 0,\n      \"avg_cost\": 3.581,\n      \"total_cost\": 35.81,\n      \"fifo_layer_date\": \"2026-06-03T04:32:22.000000Z\",\n      \"created_at\": \"2026-06-03T04:32:22.000000Z\"\n    },\n    \"created_at\": \"2026-06-03T04:32:22.000000Z\"\n  }\n}"}]},{"name":"List Production Iteration Inventory Movements","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/production-iterations/{id}/inventory-movements","host":["{{baseUrl}}"],"path":["api","manufacturing","production-iterations",":id","inventory-movements"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Paginated inventory movements scoped to a single production iteration — every movement created by the underlying Record Production call (component consumption, scrap, and output). Scope is `inventory_movements.production_iteration_id = {iteration.id}`. Backs the Movements tab on the Production Iteration detail page.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Path parameters:**\n- `moProductionIteration` (string, UUID) — the iteration's UUID primary key.\n\n**Query parameters (Spatie QueryBuilder):**\n- `per_page` — items per page (default: 15)\n- `sort` — sort field. Prefix with `-` for DESC. Allowed: `id`, `inventory_movement_date`, `type`, `inventory_status`, `quantity`, `reference`, `created_at`. Default: `-id`\n- `filter[type]` — exact match (e.g. `manufacturing_consumption`, `manufacturing_output`, `manufacturing_scrap`)\n- `filter[inventory_status]` — exact match (e.g. `on_hand`, `scrapped`)\n- `filter[link_type]` — exact FQN model class (e.g. `App\\Models\\MoOutputLine`, `App\\Models\\MoComponentLine`)\n- `filter[warehouse_id]` — exact match\n- `filter[fifo_layer_id]` — exact match\n- `filter[search]` — LIKE on product SKU or product name\n- `filter[inventory_movement_date_from]`, `filter[inventory_movement_date_to]` — date range (Y-m-d)\n- `filter[created_at_from]`, `filter[created_at_to]` — date range (Y-m-d)"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/production-iterations/{id}/inventory-movements","host":["{{baseUrl}}"],"path":["api","manufacturing","production-iterations",":id","inventory-movements"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 8,\n      \"type\": \"manufacturing_output\",\n      \"inventory_status\": \"on_hand\",\n      \"quantity\": 10,\n      \"reference\": null,\n      \"link_type\": \"App\\\\Models\\\\MoOutputLine\",\n      \"link_id\": 1,\n      \"parent_link_id\": null,\n      \"detail_link_id\": null,\n      \"product_id\": 4,\n      \"product_sku\": \"QA-COFFEE-250\",\n      \"product_name\": \"Coffee 250g Pouch\",\n      \"warehouse_id\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"unit_cost\": 3.581,\n      \"layer\": {\n        \"fifo_layer_id\": 5,\n        \"name\": null\n      },\n      \"fifo_layer_id\": 5,\n      \"inventory_movement_date\": \"2026-06-03T04:32:22.000000Z\",\n      \"created_at\": \"2026-06-03T04:32:22.000000Z\"\n    },\n    {\n      \"id\": 7,\n      \"type\": \"manufacturing_consumption\",\n      \"inventory_status\": \"on_hand\",\n      \"quantity\": -25,\n      \"reference\": null,\n      \"link_type\": \"App\\\\Models\\\\MoComponentLine\",\n      \"link_id\": 3,\n      \"parent_link_id\": null,\n      \"detail_link_id\": null,\n      \"product_id\": 7,\n      \"product_sku\": \"QA-BEAN-1KG\",\n      \"product_name\": \"Raw Coffee Bean 1kg\",\n      \"warehouse_id\": 1,\n      \"warehouse_name\": \"Main Warehouse\",\n      \"unit_cost\": 1.2,\n      \"layer\": {\n        \"fifo_layer_id\": 2,\n        \"name\": null\n      },\n      \"fifo_layer_id\": 2,\n      \"inventory_movement_date\": \"2026-06-03T04:32:22.000000Z\",\n      \"created_at\": \"2026-06-03T04:32:22.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 4,\n  \"from\": 1,\n  \"to\": 4\n}"}]},{"name":"List Production Iteration FIFO Layers","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/production-iterations/{id}/fifo-layers","host":["{{baseUrl}}"],"path":["api","manufacturing","production-iterations",":id","fifo-layers"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Paginated FIFO layers scoped to a single production iteration — every layer (output + any auxiliary) created by the underlying Record Production call. Scope is `fifo_layers.production_iteration_id = {iteration.id}`. Mirrors the MO-output-line FIFO-layers endpoint so the frontend list view can reuse the same contract.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Path parameters:**\n- `moProductionIteration` (string, UUID) — the iteration's UUID primary key.\n\n**Query parameters (Spatie QueryBuilder):**\n- `per_page` — items per page (default: 15)\n- `sort` — sort field. Prefix with `-` for DESC. Allowed: `id`, `fifo_layer_date`, `original_quantity`, `fulfilled_quantity`, `avg_cost`, `total_cost`, `created_at`. Default: `-created_at`\n- `filter[warehouse_id]` — exact match\n- `filter[product_id]` — exact match\n- `filter[search]` — LIKE on product SKU or product name\n- `filter[fifo_layer_date_from]`, `filter[fifo_layer_date_to]` — date range (Y-m-d)\n- `filter[avg_cost_min]`, `filter[avg_cost_max]` — avg_cost range"},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/production-iterations/{id}/fifo-layers","host":["{{baseUrl}}"],"path":["api","manufacturing","production-iterations",":id","fifo-layers"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"id\": 5,\n      \"product_id\": 4,\n      \"warehouse_id\": 1,\n      \"batch_number\": \"MO-000001-2026-06-03\",\n      \"manufacture_date\": \"2026-06-03\",\n      \"expiry_date\": null,\n      \"original_quantity\": 10,\n      \"available_quantity\": 10,\n      \"fulfilled_quantity\": 0,\n      \"avg_cost\": 3.581,\n      \"total_cost\": 35.81,\n      \"fifo_layer_date\": \"2026-06-03T04:32:22.000000Z\",\n      \"link_type\": \"App\\\\Models\\\\MoOutputLine\",\n      \"link_id\": 1,\n      \"created_at\": \"2026-06-03T04:32:22.000000Z\",\n      \"updated_at\": \"2026-06-03T04:32:22.000000Z\"\n    }\n  ],\n  \"current_page\": 1,\n  \"last_page\": 1,\n  \"per_page\": 15,\n  \"total\": 1,\n  \"from\": 1,\n  \"to\": 1\n}"}]},{"name":"Reverse Production Iteration","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/production-iterations/{id2}/reverse","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","production-iterations",":id2","reverse"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]},"description":"Reverses a production iteration: restores input FIFO layers, deletes the output layer (if not yet drawn from), rolls back actuals on the component/output lines, decrements `completed_quantity` on the MO, and writes a `production_reversed` audit event. Refuses with 422 if already reversed, mismatched MO, or downstream-drawn.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Request body:**\n- `reason` (string, optional, max 1000) — free-text reason for the reversal\n\n**Failure modes (422 JSON with discriminator `code`):**\n- `iteration_already_reversed`\n- `downstream_draw_blocks_reverse` — carries `details.layer_id`, `details.fulfilled_quantity`, `details.original_quantity`\n- `iteration_belongs_to_other_mo`\n\n**Success:** 200 with the refreshed manufacturing order object.","body":{"mode":"raw","raw":"{\n  \"reason\": \"Quality control rejected this batch\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/production-iterations/{id2}/reverse","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","production-iterations",":id2","reverse"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"id\": 1,\n  \"mo_number\": \"MO-000001\",\n  \"status\": \"in_progress\",\n  \"bom_id\": 5,\n  \"planned_quantity\": 100,\n  \"completed_quantity\": 0,\n  \"production_method\": \"in_house\",\n  \"started_at\": \"2026-06-02T20:30:00.000000Z\",\n  \"completed_at\": null,\n  \"cost_breakdown\": null,\n  \"created_at\": \"2026-06-02T20:00:00.000000Z\",\n  \"updated_at\": \"2026-06-02T20:38:00.000000Z\"\n}"}]},{"name":"Get MO Cost Allocation Breakdown","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{manufacturingOrder}/cost-allocation-breakdown","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":manufacturingOrder","cost-allocation-breakdown"],"variable":[{"key":"manufacturingOrder","value":"1","description":"The manufacturing order ID."}]},"description":"Get the cumulative cost-allocation breakdown across all posted production for this manufacturing order: the allocation method in use, each output's basis value, share percent, allocated cost, and unit cost, plus byproduct valuations. Always returns a usable shape - has_production is false when no production has been recorded yet.\nAuth: requires Bearer token.\nPath param: manufacturingOrder = manufacturing order id.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{manufacturingOrder}/cost-allocation-breakdown","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":manufacturingOrder","cost-allocation-breakdown"],"variable":[{"key":"manufacturingOrder","value":"1","description":"The manufacturing order ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"method\": \"by_weight\",\n    \"method_label\": \"By Weight\",\n    \"method_formula\": \"Share = (quantity produced × product weight) ÷ total across outputs\",\n    \"basis_label\": \"Weight\",\n    \"basis_hint\": \"Weight on each product\",\n    \"has_production\": true,\n    \"total_input_cost\": 1250,\n    \"byproduct_value\": 50,\n    \"remaining_pool\": 1200,\n    \"allocatable_outputs\": [\n      {\n        \"output_line_id\": 88,\n        \"product_id\": 1,\n        \"product_sku\": \"KIT-A\",\n        \"product_name\": \"Widget Kit A\",\n        \"role\": \"primary\",\n        \"produced_quantity\": 100,\n        \"basis_value\": 0.5,\n        \"weight\": 50,\n        \"allocated_cost\": 1200,\n        \"unit_cost\": 12,\n        \"missing_basis\": false,\n        \"share_percent\": 100\n      }\n    ],\n    \"byproducts\": [\n      {\n        \"output_line_id\": 89,\n        \"product_id\": 9,\n        \"product_sku\": \"SCRAP-01\",\n        \"product_name\": \"Offcut Material\",\n        \"role\": \"byproduct\",\n        \"produced_quantity\": 5,\n        \"valuation_method\": \"fixed_cost\",\n        \"valuation_amount\": 50,\n        \"allocated_cost\": 50,\n        \"unit_cost\": 10\n      }\n    ]\n  }\n}"}]},{"name":"Download Work Order PDF","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{manufacturingOrder}/print","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":manufacturingOrder","print"],"variable":[{"key":"manufacturingOrder","value":"1","description":"The manufacturing order ID."}]},"description":"Download a printable work order PDF for the manufacturing order (returned inline with Content-Type application/pdf). For Draft orders that have not been confirmed yet, component and output rows fall back to the BOM definition.\nAuth: requires Bearer token.\nPath param: manufacturingOrder = manufacturing order id.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api)."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{manufacturingOrder}/print","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":manufacturingOrder","print"],"variable":[{"key":"manufacturingOrder","value":"1","description":"The manufacturing order ID."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":""}]},{"name":"Preview Reverse Production Iteration Impact","request":{"method":"GET","header":[],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/production-iterations/{id2}/reverse-impact","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","production-iterations",":id2","reverse-impact"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]},"description":"Read-only preview of the allocation-demotion impact of reversing this production iteration. Because reversing it removes the stock it added, this behaves like a manual negative adjustment and returns one row per affected product: how much of the removed quantity is unallocated vs soft-allocated vs hard-allocated, plus every sales-order allocation and fulfillment (printed shipping label) the removal would disturb. Use it to decide which fulfillments to authorize voiding via `void_confirmations` on the reversing call. Nothing is mutated. A removal that only re-adds stock returns an empty `data` array.\n\n**Required scope: `manufacturing:read`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAuthentication: Requires Bearer token."},"response":[{"name":"OK","originalRequest":{"method":"GET","url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/production-iterations/{id2}/reverse-impact","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","production-iterations",":id2","reverse-impact"],"variable":[{"key":"id","value":"","description":"The id identifier"},{"key":"id2","value":"","description":"The id2 identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 5567,\n      \"product_sku\": \"WIDGET-BLUE\",\n      \"product_name\": \"Blue Widget\",\n      \"shortfall_quantity\": 0,\n      \"unallocated_quantity\": 10,\n      \"soft_allocated_quantity\": 6,\n      \"hard_allocated_quantity\": 4,\n      \"affected_allocations\": [\n        {\n          \"allocation_id\": 88201,\n          \"allocation_type\": \"soft\",\n          \"sales_order_id\": 3120,\n          \"order_number\": \"SO-3120\",\n          \"customer_name\": \"Acme Co.\",\n          \"fulfillment_id\": null,\n          \"carrier\": null,\n          \"carrier_void_supported\": null,\n          \"voidable_status\": null,\n          \"voidable_detail\": null,\n          \"quantity\": 6\n        },\n        {\n          \"allocation_id\": 88205,\n          \"allocation_type\": \"hard\",\n          \"sales_order_id\": 3125,\n          \"order_number\": \"SO-3125\",\n          \"customer_name\": \"Beta LLC\",\n          \"fulfillment_id\": 90142,\n          \"carrier\": \"ups\",\n          \"carrier_void_supported\": true,\n          \"voidable_status\": \"voidable\",\n          \"voidable_detail\": null,\n          \"quantity\": 4,\n          \"allocation_ids\": [\n            88205\n          ]\n        }\n      ]\n    }\n  ]\n}"}]},{"name":"Preview Record Production Release Impact","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/record-production/preview-release-impact","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","record-production","preview-release-impact"],"variable":[{"key":"id","value":"","description":"The id identifier"}]},"description":"Read-only preview of which back-ordered sales orders the proposed produced quantities would release finished-good stock to, returned one row per output product. Nothing is mutated — post the outputs you intend to record and get the release picture before committing.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Request body:**\n- `outputs` (array, optional) — the produced outputs to evaluate.\n- `outputs[].mo_output_line_id` (integer, required) — the output line being produced.\n- `outputs[].actual_produced_quantity` (number, required, min 0) — the quantity that would be produced.\n\n**Response (`data[]`, one row per output product):**\n- `releasable_quantity` — units of the newly produced stock that can be released to back-ordered demand.\n- `orders[]` — the back-ordered sales orders that would be filled (`sales_order_id`, `order_number`, `customer_name`, `quantity`).\n- `held_back[]` — demand that stays back-ordered, each with `reason_kept` explaining why (e.g. \"Insufficient supply\").\n\nAuthentication: Requires Bearer token.","body":{"mode":"raw","raw":"{\n  \"outputs\": [\n    {\n      \"mo_output_line_id\": 1,\n      \"actual_produced_quantity\": 100\n    }\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{id}/record-production/preview-release-impact","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":id","record-production","preview-release-impact"],"variable":[{"key":"id","value":"","description":"The id identifier"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": [\n    {\n      \"product_id\": 123,\n      \"product_sku\": \"WIDGET-001\",\n      \"product_name\": \"Blue Widget 500ml\",\n      \"releasable_quantity\": 8,\n      \"orders\": [\n        {\n          \"sales_order_id\": 4602,\n          \"order_number\": \"SO-10501\",\n          \"customer_name\": \"Acme Retail\",\n          \"quantity\": 5\n        },\n        {\n          \"sales_order_id\": 4610,\n          \"order_number\": \"SO-10509\",\n          \"customer_name\": \"Gamma Foods\",\n          \"quantity\": 3\n        }\n      ],\n      \"held_back\": [\n        {\n          \"allocation_id\": 90311,\n          \"product_id\": 123,\n          \"sales_order_id\": 4620,\n          \"sales_order_line_id\": 55123,\n          \"sales_order_number\": \"SO-10518\",\n          \"customer_name\": \"Delta Mart\",\n          \"quantity\": 4,\n          \"from_status\": \"planned\",\n          \"to_status\": \"allocated\",\n          \"reason_kept\": \"Insufficient supply\"\n        }\n      ]\n    }\n  ]\n}"}]}]},{"name":"MO Operations","item":[{"name":"Create Manual MO Operation","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{manufacturingOrder}/operations","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":manufacturingOrder","operations"],"variable":[{"key":"manufacturingOrder","value":"1","description":"Manufacturing order ID"}]},"description":"Create a manual operation against a specific MO. The new row is stamped source='manual' with bom_operation_id=null.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\n**Body fields:**\n- `sequence` (required, integer, min 0) — ordering on the MO; can interleave with from_bom rows\n- `name` (required, string, max 255)\n- `type` (required, enum) — one of: process, setup, per_unit, fixed_cost. Stored on the row as operation_type.\n- `work_center_id` (optional, integer, exists:work_centers,id)\n- `default_operator_id` (optional, integer, exists:users,id) — planned operator (distinct from operator_id which is set when the timer starts)\n- `expected_duration_minutes` (optional, integer, min 0)\n- `hourly_rate` (optional, numeric, min 0)\n- `fixed_cost` (optional, numeric, min 0) — flat per-run cost added on top of labour for any operation type (or the entire cost of a fixed_cost operation). Capitalized onto finished goods like labour, but never marked up by work-center overhead.\n- `notes` (optional, string, max 1000)\n\n**Refused** with 422 {code:'mo_locked'} when MO status is completed | closed | cancelled.\n\n`cost_classification` (optional, `labor`|`overhead`, default `labor`) sets the expense classification independently of the rate basis: `labor` posts the operation's variable cost to the Labor bucket (marked up by the work center's overhead %); `overhead` posts machine/tooling-style cost to the Overhead bucket with no additional markup.","body":{"mode":"raw","raw":"{\n  \"sequence\": 40,\n  \"name\": \"Final QA Check\",\n  \"type\": \"process\",\n  \"work_center_id\": 3,\n  \"default_operator_id\": 7,\n  \"expected_duration_minutes\": 15,\n  \"hourly_rate\": 35,\n  \"fixed_cost\": 0,\n  \"cost_classification\": \"labor\",\n  \"notes\": \"Added for this MO only — not on the BOM.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","url":{"raw":"{{baseUrl}}/api/manufacturing/manufacturing-orders/{manufacturingOrder}/operations","host":["{{baseUrl}}"],"path":["api","manufacturing","manufacturing-orders",":manufacturingOrder","operations"],"variable":[{"key":"manufacturingOrder","value":"1","description":"Manufacturing order ID"}]}},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 412,\n    \"manufacturing_order_id\": 14,\n    \"source\": \"manual\",\n    \"bom_operation_id\": null,\n    \"sequence\": 40,\n    \"name\": \"Final QA Check\",\n    \"type\": \"process\",\n    \"operation_type\": \"process\",\n    \"expected_duration_minutes\": 15,\n    \"hourly_rate\": 35,\n    \"status\": \"pending\",\n    \"scheduled_start\": null,\n    \"started_at\": null,\n    \"completed_at\": null,\n    \"actual_time_minutes\": null,\n    \"operator_id\": null,\n    \"default_operator_id\": 7,\n    \"default_operator\": {\n      \"id\": 7,\n      \"name\": \"Sam Cooper\"\n    },\n    \"work_center_id\": 3,\n    \"work_center\": {\n      \"id\": 3,\n      \"name\": \"QA Bench\"\n    },\n    \"fixed_cost\": 0,\n    \"cost_classification\": \"labor\",\n    \"actual_cost\": null,\n    \"cost_source\": \"none\",\n    \"notes\": \"Added for this MO only — not on the BOM.\",\n    \"bom_operation\": null,\n    \"is_modified_from_bom\": false\n  }\n}"}]},{"name":"Update MO Operation","request":{"method":"PUT","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{baseUrl}}/api/manufacturing/mo-operations/{moOperation}","host":["{{baseUrl}}"],"path":["api","manufacturing","mo-operations",":moOperation"],"variable":[{"key":"moOperation","value":"1","description":"MO operation ID"}]},"description":"Update an MO operation. Allowed on both source='manual' AND source='from_bom' rows — edits to from_bom rows do NOT propagate back to the BOM master.\n\n**Required scope: `manufacturing:write`**\nGrant this scope to your token under [Settings → Developer → Personal Access Tokens](https://app.sku.io/settings/api).\n\nAll body fields are 'sometimes' (omit to leave unchanged):\n- `sequence` (integer, min 0)\n- `name` (string, max 255)\n- `type` (enum: process, setup, per_unit, fixed_cost) — maps to operation_type column\n- `work_center_id` (integer, exists:work_centers,id, nullable)\n- `default_operator_id` (integer, exists:users,id, nullable)\n- `expected_duration_minutes` (integer, min 0, nullable)\n- `hourly_rate` (numeric, min 0, nullable)\n- `fixed_cost` (numeric, min 0, nullable) — flat per-run cost added additively to labour. Changing it re-derives actual_cost as (time × rate) + fixed_cost.\n- `notes` (string, max 1000, nullable)\n\nWhen the row is source='from_bom' and any of {sequence, name, operation_type, work_center_id, default_operator_id, expected_duration_minutes, hourly_rate, fixed_cost} now differs from the linked BOM operation, the response field `is_modified_from_bom` flips to true.\n\n**Refused** with 422 {code:'mo_locked'} when MO status is completed | closed | cancelled.\n\nThe response also carries a `cost_source` field: 'none' (no actual_cost yet), 'actual' (cost backed by logged time, or a fixed_cost operation), 'estimated' (cost fell back to planned duration because no timer was logged), or 'manual' (an explicitly supplied `actual_cost`). Sending `actual_cost` in the body stamps cost_source='manual' and locks the cost against automatic re-derivation — rate, fixed-cost, or type edits no longer recompute it; only another explicit `actual_cost` (null clears both cost and source), or an explicitly logged positive time, replaces it.\n\n`cost_classification` (optional, `labor`|`overhead`, default `labor`) sets the expense classification independently of the rate basis: `labor` posts the operation's variable cost to the Labor bucket (marked up by the work center's overhead %); `overhead` posts machine/tooling-style cost to the Overhead bucket with no additional markup.","body":{"mode":"raw","raw":"{\n  \"sequence\": 25,\n  \"name\": \"Mix (revised)\",\n  \"work_center_id\": 2,\n  \"expected_duration_minutes\": 45,\n  \"hourly_rate\": 55,\n  \"fixed_cost\": 5,\n  \"cost_classification\": \"labor\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","url":{"raw":"{{baseUrl}}/api/manufacturing/mo-operations/{moOperation}","host":["{{baseUrl}}"],"path":["api","manufacturing","mo-operations",":moOperation"],"variable":[{"key":"moOperation","value":"1","description":"MO operation ID"}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n  \"data\": {\n    \"id\": 1,\n    \"manufacturing_order_id\": 14,\n    \"source\": \"from_bom\",\n    \"bom_operation_id\": 12,\n    \"sequence\": 25,\n    \"name\": \"Mix (revised)\",\n    \"type\": \"setup\",\n    \"operation_type\": \"setup\",\n    \"expected_duration_minutes\": 45,\n    \"hourly_rate\": 55,\n    \"status\": \"pending\",\n    \"scheduled_start\": null,\n    \"started_at\": null,\n    \"completed_at\": null,\n    \"actual_time_minutes\": null,\n    \"operator_id\"