A shipment was created that this provider can fulfill.
ship-by-loop.purchase.requested
Services have been selected and are ready for purchase.
ship-by-loop.cancellation.requested
The shipment was cancelled.
ship-by-loop.services.requested: submit the services this provider can
offer for the shipment’s packages with
POST /ship-by-loop/shipments/{shipmentId}/services. The shopper-facing
portal polls for a response roughly every 5 seconds, up to 12 times
(roughly 30-60 seconds total). If nothing arrives in that window, the
shopper falls back to whichever standard (non-carrier-choice) return
methods were already available — no error is shown. The API still
accepts a POST .../services call after that window, but the
portal has stopped polling for it by then, so the shopper who already
fell back will not see it.
ship-by-loop.purchase.requested: record the resulting purchase, including
tracking and label details, with
POST /ship-by-loop/shipments/{shipmentId}/orders.
ship-by-loop.cancellation.requested: stop any work in progress for this
shipment and void/refund any carrier label already purchased. Loop does not
expect a response beyond the 200 acknowledgement.
Acknowledge every delivery with a 200 response.
Package field state by trigger
service, label_url, qr_code_url, and tracking_number on each
packages entry are populated over the lifetime of a shipment rather
than all at once:
Trigger
service
label_url
qr_code_url
tracking_number
services.requested
null
null
null
null
purchase.requested
set
null
null
null
cancellation.requested
see below
see below
see below
see below
service is null at services.requested (nothing has been chosen
yet) and always set by purchase.requested — Loop only asks a
provider to purchase once every package has a service selected.
label_url / qr_code_url / tracking_number stay null through
both of those triggers; they’re populated only once the provider
calls POST .../orders, which happens after purchase.requested is
received. For cancellation.requested, every field reflects whatever
had already been recorded before the cancellation — null for any of
them the provider hadn’t gotten to yet.
Return a 200 status to indicate that the data was received successfully.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Loop Returns Documentation","colors":{"primary":"#0672FF","light":"#FFFCF7","dark":"#000000"},"logo":{"light":"https://mintcdn.com/loopreturns/mWP1nZGNp75FiB-l/logo/loop-logo-lapis-charcoal.svg?fit=max\u0026auto=format\u0026n=mWP1nZGNp75FiB-l\u0026q=85\u0026s=5113cc3f8f2d70a65c2c774e471a13c4","dark":"https://mintcdn.com/loopreturns/mWP1nZGNp75FiB-l/logo/loop-logo-lapis-newsprint.svg?fit=max\u0026auto=format\u0026n=mWP1nZGNp75FiB-l\u0026q=85\u0026s=c2197689146b468c5df1b8581f091743"},"favicon":"https://mintcdn.com/loopreturns/mWP1nZGNp75FiB-l/favicon.svg?fit=max\u0026auto=format\u0026n=mWP1nZGNp75FiB-l\u0026q=85\u0026s=93b43e224efe823b43c6d1b9c45799f9","navbar":{"links":[{"href":"https://help.loopreturns.com","label":"Support"}],"primary":{"type":"button","label":"Request Demo","href":"https://www.loopreturns.com/book-a-demo-today/"}},"footer":{"socials":{"linkedin":"https://www.linkedin.com/company/loop-returns","instagram":"https://www.instagram.com/loopreturns"}},"fonts":{"heading":{"family":"ABC Ginto","weight":900,"source":"fonts/ABCGinto-Black.woff2","format":"woff2"},"body":{"family":"Matter","weight":400,"source":"fonts/Matter-Regular.woff2","format":"woff2"}},"styling":{"latex":false},"navigation":{"global":{"anchors":[{"anchor":"System Status","icon":"radar","href":"https://status.loopreturns.com"}]},"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"loopreturns","hasAudioNarration":false,"actualSubdomain":"loopreturns","customDomains":["docs.loopreturns.com"],"gitSource":{"type":"gitlab","owner":"loopreturns/documentation","repo":"public-api-docs","deployBranch":"main","contentDirectory":"","isPrivate":true,"projectId":67355043,"mountPath":"","label":""},"chroma":{"liveDeploymentHistoryId":"6aeca26621aa22f79f8e3517","collectionId":"dc0f7af0-e5b9-46ae-900e-ed1f575f606c","schemaVersion":"v3","latestBuiltDeploymentHistoryId":"6aeca26621aa22f79f8e3517"},"entitlements":{"AI_CHAT":{"status":"ENABLED"},"WEB_MCP":{"status":"ENABLED"},"NARRATION":{"status":"ENABLED"}},"buildId":"6aee02e9b56b944fc2811a93","clientVersion":"0.0.4074","searchFilterCounts":[{"language":"default","tag":"API Reference","count":177},{"language":"default","tag":"APIs","count":169},{"language":"default","tag":"Commerce Data","count":69},{"language":"default","tag":"Return Create","count":52},{"language":"default","tag":"Guides","count":30},{"language":"default","tag":"Products","count":17},{"language":"default","tag":"Webhook Reference","count":14},{"language":"default","tag":"Collections","count":12},{"language":"default","tag":"Orders","count":11},{"language":"default","tag":"MCP Server","count":10}]},"assistantConfig":{},"basePath":"","hasChatPermissions":true,"hasWebMcpPermissions":true,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"comment-mode","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"webhook-reference/topics/ship-by-loop-shipment-webhook","slug":"/webhook-reference/topics/ship-by-loop-shipment-webhook","pageMetadata":{"title":"Ship by Loop shipment","openapi":"webhook ShipByLoopShipment","description":"| Trigger | What it means |\n| ------------------------------------- | ------------------------------------------------------- |\n| `ship-by-loop.services.requested` | A shipment was created that this provider can fulfill. |\n| `ship-by-loop.purchase.requested` | Services have been selected and are ready for purchase. |\n| `ship-by-loop.cancellation.requested` | The shipment was cancelled. |\n\n- **`ship-by-loop.services.requested`**: submit the services this provider can\n offer for the shipment's packages with\n `POST /ship-by-loop/shipments/{shipmentId}/services`. The shopper-facing\n portal polls for a response roughly every 5 seconds, up to 12 times\n (roughly 30-60 seconds total). If nothing arrives in that window, the\n shopper falls back to whichever standard (non-carrier-choice) return\n methods were already available — no error is shown. The API still\n accepts a `POST .../services` call after that window, but the\n portal has stopped polling for it by then, so the shopper who already\n fell back will not see it.\n- **`ship-by-loop.purchase.requested`**: record the resulting purchase, including\n tracking and label details, with\n `POST /ship-by-loop/shipments/{shipmentId}/orders`.\n- **`ship-by-loop.cancellation.requested`**: stop any work in progress for this\n shipment and void/refund any carrier label already purchased. Loop does not\n expect a response beyond the `200` acknowledgement.\n\nAcknowledge every delivery with a `200` response.\n\n### Package field state by trigger\n\n`service`, `label_url`, `qr_code_url`, and `tracking_number` on each\n`packages` entry are populated over the lifetime of a shipment rather\nthan all at once:\n\n| Trigger | `service` | `label_url` | `qr_code_url` | `tracking_number` |\n| --------------------------- | ---------- | ------------ | -------------- | ------------------ |\n| `services.requested` | `null` | `null` | `null` | `null` |\n| `purchase.requested` | set | `null` | `null` | `null` |\n| `cancellation.requested` | see below | see below | see below | see below |\n\n`service` is `null` at `services.requested` (nothing has been chosen\nyet) and always set by `purchase.requested` — Loop only asks a\nprovider to purchase once every package has a service selected.\n`label_url` / `qr_code_url` / `tracking_number` stay `null` through\nboth of those triggers; they're populated only once the provider\ncalls `POST .../orders`, which happens after `purchase.requested` is\nreceived. For `cancellation.requested`, every field reflects whatever\nhad already been recorded before the cancellation — `null` for any of\nthem the provider hadn't gotten to yet.\n","href":"/webhook-reference/topics/ship-by-loop-shipment-webhook"},"description":{"compiledSource":"\"use strict\";\nconst {Fragment: _Fragment, jsx: _jsx, jsxs: _jsxs} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n code: \"code\",\n h3: \"h3\",\n li: \"li\",\n p: \"p\",\n strong: \"strong\",\n table: \"table\",\n tbody: \"tbody\",\n td: \"td\",\n th: \"th\",\n thead: \"thead\",\n tr: \"tr\",\n ul: \"ul\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsxs(_Fragment, {\n children: [_jsxs(_components.table, {\n children: [_jsx(_components.thead, {\n children: _jsxs(_components.tr, {\n children: [_jsx(_components.th, {\n children: \"Trigger\"\n }), _jsx(_components.th, {\n children: \"What it means\"\n })]\n })\n }), _jsxs(_components.tbody, {\n children: [_jsxs(_components.tr, {\n children: [_jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"ship-by-loop.services.requested\"\n })\n }), _jsx(_components.td, {\n children: \"A shipment was created that this provider can fulfill.\"\n })]\n }), _jsxs(_components.tr, {\n children: [_jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"ship-by-loop.purchase.requested\"\n })\n }), _jsx(_components.td, {\n children: \"Services have been selected and are ready for purchase.\"\n })]\n }), _jsxs(_components.tr, {\n children: [_jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"ship-by-loop.cancellation.requested\"\n })\n }), _jsx(_components.td, {\n children: \"The shipment was cancelled.\"\n })]\n })]\n })]\n }), \"\\n\", _jsxs(_components.ul, {\n children: [\"\\n\", _jsxs(_components.li, {\n children: [_jsx(_components.strong, {\n children: _jsx(_components.code, {\n children: \"ship-by-loop.services.requested\"\n })\n }), \": submit the services this provider can\\noffer for the shipment’s packages with\\n\", _jsx(_components.code, {\n children: \"POST /ship-by-loop/shipments/{shipmentId}/services\"\n }), \". The shopper-facing\\nportal polls for a response roughly every 5 seconds, up to 12 times\\n(roughly 30-60 seconds total). If nothing arrives in that window, the\\nshopper falls back to whichever standard (non-carrier-choice) return\\nmethods were already available — no error is shown. The API still\\naccepts a \", _jsx(_components.code, {\n children: \"POST .../services\"\n }), \" call after that window, but the\\nportal has stopped polling for it by then, so the shopper who already\\nfell back will not see it.\"]\n }), \"\\n\", _jsxs(_components.li, {\n children: [_jsx(_components.strong, {\n children: _jsx(_components.code, {\n children: \"ship-by-loop.purchase.requested\"\n })\n }), \": record the resulting purchase, including\\ntracking and label details, with\\n\", _jsx(_components.code, {\n children: \"POST /ship-by-loop/shipments/{shipmentId}/orders\"\n }), \".\"]\n }), \"\\n\", _jsxs(_components.li, {\n children: [_jsx(_components.strong, {\n children: _jsx(_components.code, {\n children: \"ship-by-loop.cancellation.requested\"\n })\n }), \": stop any work in progress for this\\nshipment and void/refund any carrier label already purchased. Loop does not\\nexpect a response beyond the \", _jsx(_components.code, {\n children: \"200\"\n }), \" acknowledgement.\"]\n }), \"\\n\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"Acknowledge every delivery with a \", _jsx(_components.code, {\n children: \"200\"\n }), \" response.\"]\n }), \"\\n\", _jsx(_components.h3, {\n children: \"Package field state by trigger\"\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.code, {\n children: \"service\"\n }), \", \", _jsx(_components.code, {\n children: \"label_url\"\n }), \", \", _jsx(_components.code, {\n children: \"qr_code_url\"\n }), \", and \", _jsx(_components.code, {\n children: \"tracking_number\"\n }), \" on each\\n\", _jsx(_components.code, {\n children: \"packages\"\n }), \" entry are populated over the lifetime of a shipment rather\\nthan all at once:\"]\n }), \"\\n\", _jsxs(_components.table, {\n children: [_jsx(_components.thead, {\n children: _jsxs(_components.tr, {\n children: [_jsx(_components.th, {\n children: \"Trigger\"\n }), _jsx(_components.th, {\n children: _jsx(_components.code, {\n children: \"service\"\n })\n }), _jsx(_components.th, {\n children: _jsx(_components.code, {\n children: \"label_url\"\n })\n }), _jsx(_components.th, {\n children: _jsx(_components.code, {\n children: \"qr_code_url\"\n })\n }), _jsx(_components.th, {\n children: _jsx(_components.code, {\n children: \"tracking_number\"\n })\n })]\n })\n }), _jsxs(_components.tbody, {\n children: [_jsxs(_components.tr, {\n children: [_jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"services.requested\"\n })\n }), _jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"null\"\n })\n }), _jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"null\"\n })\n }), _jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"null\"\n })\n }), _jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"null\"\n })\n })]\n }), _jsxs(_components.tr, {\n children: [_jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"purchase.requested\"\n })\n }), _jsx(_components.td, {\n children: \"set\"\n }), _jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"null\"\n })\n }), _jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"null\"\n })\n }), _jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"null\"\n })\n })]\n }), _jsxs(_components.tr, {\n children: [_jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"cancellation.requested\"\n })\n }), _jsx(_components.td, {\n children: \"see below\"\n }), _jsx(_components.td, {\n children: \"see below\"\n }), _jsx(_components.td, {\n children: \"see below\"\n }), _jsx(_components.td, {\n children: \"see below\"\n })]\n })]\n })]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.code, {\n children: \"service\"\n }), \" is \", _jsx(_components.code, {\n children: \"null\"\n }), \" at \", _jsx(_components.code, {\n children: \"services.requested\"\n }), \" (nothing has been chosen\\nyet) and always set by \", _jsx(_components.code, {\n children: \"purchase.requested\"\n }), \" — Loop only asks a\\nprovider to purchase once every package has a service selected.\\n\", _jsx(_components.code, {\n children: \"label_url\"\n }), \" / \", _jsx(_components.code, {\n children: \"qr_code_url\"\n }), \" / \", _jsx(_components.code, {\n children: \"tracking_number\"\n }), \" stay \", _jsx(_components.code, {\n children: \"null\"\n }), \" through\\nboth of those triggers; they’re populated only once the provider\\ncalls \", _jsx(_components.code, {\n children: \"POST .../orders\"\n }), \", which happens after \", _jsx(_components.code, {\n children: \"purchase.requested\"\n }), \" is\\nreceived. For \", _jsx(_components.code, {\n children: \"cancellation.requested\"\n }), \", every field reflects whatever\\nhad already been recorded before the cancellation — \", _jsx(_components.code, {\n children: \"null\"\n }), \" for any of\\nthem the provider hadn’t gotten to yet.\"]\n })]\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){return _jsx(_Fragment,{})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Ship by Loop shipment","openapi":"webhook ShipByLoopShipment","description":"| Trigger | What it means |\n| ------------------------------------- | ------------------------------------------------------- |\n| `ship-by-loop.services.requested` | A shipment was created that this provider can fulfill. |\n| `ship-by-loop.purchase.requested` | Services have been selected and are ready for purchase. |\n| `ship-by-loop.cancellation.requested` | The shipment was cancelled. |\n\n- **`ship-by-loop.services.requested`**: submit the services this provider can\n offer for the shipment's packages with\n `POST /ship-by-loop/shipments/{shipmentId}/services`. The shopper-facing\n portal polls for a response roughly every 5 seconds, up to 12 times\n (roughly 30-60 seconds total). If nothing arrives in that window, the\n shopper falls back to whichever standard (non-carrier-choice) return\n methods were already available — no error is shown. The API still\n accepts a `POST .../services` call after that window, but the\n portal has stopped polling for it by then, so the shopper who already\n fell back will not see it.\n- **`ship-by-loop.purchase.requested`**: record the resulting purchase, including\n tracking and label details, with\n `POST /ship-by-loop/shipments/{shipmentId}/orders`.\n- **`ship-by-loop.cancellation.requested`**: stop any work in progress for this\n shipment and void/refund any carrier label already purchased. Loop does not\n expect a response beyond the `200` acknowledgement.\n\nAcknowledge every delivery with a `200` response.\n\n### Package field state by trigger\n\n`service`, `label_url`, `qr_code_url`, and `tracking_number` on each\n`packages` entry are populated over the lifetime of a shipment rather\nthan all at once:\n\n| Trigger | `service` | `label_url` | `qr_code_url` | `tracking_number` |\n| --------------------------- | ---------- | ------------ | -------------- | ------------------ |\n| `services.requested` | `null` | `null` | `null` | `null` |\n| `purchase.requested` | set | `null` | `null` | `null` |\n| `cancellation.requested` | see below | see below | see below | see below |\n\n`service` is `null` at `services.requested` (nothing has been chosen\nyet) and always set by `purchase.requested` — Loop only asks a\nprovider to purchase once every package has a service selected.\n`label_url` / `qr_code_url` / `tracking_number` stay `null` through\nboth of those triggers; they're populated only once the provider\ncalls `POST .../orders`, which happens after `purchase.requested` is\nreceived. For `cancellation.requested`, every field reflects whatever\nhad already been recorded before the cancellation — `null` for any of\nthem the provider hadn't gotten to yet.\n","href":"/webhook-reference/topics/ship-by-loop-shipment-webhook"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[],"codeExamples":{}},"jsLinks":["/_mintlify/custom/loopreturns/d7dc6883aeaefe8b/scripts/generate-unstable-nav.js","/_mintlify/custom/loopreturns/eb155f0b6d2849a4/scripts/update-openapi-servers.js"],"openApiReferenceData":{"operation":{"description":"| Trigger | What it means |\n| ------------------------------------- | ------------------------------------------------------- |\n| `ship-by-loop.services.requested` | A shipment was created that this provider can fulfill. |\n| `ship-by-loop.purchase.requested` | Services have been selected and are ready for purchase. |\n| `ship-by-loop.cancellation.requested` | The shipment was cancelled. |\n\n- **`ship-by-loop.services.requested`**: submit the services this provider can\n offer for the shipment's packages with\n `POST /ship-by-loop/shipments/{shipmentId}/services`. The shopper-facing\n portal polls for a response roughly every 5 seconds, up to 12 times\n (roughly 30-60 seconds total). If nothing arrives in that window, the\n shopper falls back to whichever standard (non-carrier-choice) return\n methods were already available — no error is shown. The API still\n accepts a `POST .../services` call after that window, but the\n portal has stopped polling for it by then, so the shopper who already\n fell back will not see it.\n- **`ship-by-loop.purchase.requested`**: record the resulting purchase, including\n tracking and label details, with\n `POST /ship-by-loop/shipments/{shipmentId}/orders`.\n- **`ship-by-loop.cancellation.requested`**: stop any work in progress for this\n shipment and void/refund any carrier label already purchased. Loop does not\n expect a response beyond the `200` acknowledgement.\n\nAcknowledge every delivery with a `200` response.\n\n### Package field state by trigger\n\n`service`, `label_url`, `qr_code_url`, and `tracking_number` on each\n`packages` entry are populated over the lifetime of a shipment rather\nthan all at once:\n\n| Trigger | `service` | `label_url` | `qr_code_url` | `tracking_number` |\n| --------------------------- | ---------- | ------------ | -------------- | ------------------ |\n| `services.requested` | `null` | `null` | `null` | `null` |\n| `purchase.requested` | set | `null` | `null` | `null` |\n| `cancellation.requested` | see below | see below | see below | see below |\n\n`service` is `null` at `services.requested` (nothing has been chosen\nyet) and always set by `purchase.requested` — Loop only asks a\nprovider to purchase once every package has a service selected.\n`label_url` / `qr_code_url` / `tracking_number` stay `null` through\nboth of those triggers; they're populated only once the provider\ncalls `POST .../orders`, which happens after `purchase.requested` is\nreceived. For `cancellation.requested`, every field reflects whatever\nhad already been recorded before the cancellation — `null` for any of\nthem the provider hadn't gotten to yet.\n","requestBody":"74d902a6-c094-4aeb-a760-f1965bcb5ff2","responses":{"200":"9e50b22d-9206-4b93-9e45-28c9a73c872a"},"method":"post","type":"webhook","servers":[],"security":[],"path":"ShipByLoopShipment","baseUrlOptions":[],"title":"","codeSamples":[],"requiredOnlyExamples":false,"requestExamples":[{"language":"json","filename":"servicesRequested","code":"{\n \"topic\": \"ship-by-loop.shipment\",\n \"trigger\": \"ship-by-loop.services.requested\",\n \"shop_id\": 4321,\n \"shipment\": {\n \"id\": \"ff8a4470-41e0-4cc3-9bfe-44564f4286b7\",\n \"type\": \"return\",\n \"status\": \"draft\",\n \"origin\": {\n \"name\": \"Jane Shopper\",\n \"address1\": \"123 Main Street\",\n \"address2\": null,\n \"city\": \"Columbus\",\n \"state\": \"OH\",\n \"zip\": \"43215\",\n \"country\": \"US\",\n \"email\": \"jane@example.com\",\n \"phone\": \"5555555555\"\n },\n \"destination\": {\n \"name\": \"Returns Processing\",\n \"address1\": \"500 Industrial Pkwy\",\n \"address2\": null,\n \"city\": \"Reno\",\n \"state\": \"NV\",\n \"zip\": \"89501\",\n \"country\": \"US\",\n \"email\": \"returns@example.com\",\n \"phone\": \"5555550199\"\n },\n \"outbound_order\": {\n \"order_number\": \"#1001\",\n \"carrier\": \"USPS\",\n \"tracking_number\": \"1Z888\",\n \"total_value\": {\n \"value\": 4565,\n \"currency\": \"USD\"\n },\n \"shipped_date\": \"2026-08-01T14:32:00+00:00\",\n \"commercial_invoice_number\": null\n },\n \"packages\": [\n {\n \"id\": \"900a8671-35ad-4743-9b6c-2540d3bb1faf\",\n \"dimensions\": {\n \"length\": 12,\n \"width\": 9,\n \"height\": 4,\n \"unit\": \"in\"\n },\n \"weight\": {\n \"value\": 24,\n \"unit\": \"oz\"\n },\n \"items\": [\n {\n \"id\": \"3c6e9d0a-9b1a-4e9f-8c3e-5a2b1d4f6e7a\",\n \"name\": \"Classic Tee\",\n \"sku\": \"SKU1234567890\",\n \"country_of_origin\": \"US\",\n \"hs_code\": \"6109.10.00\",\n \"weight\": {\n \"value\": 8,\n \"unit\": \"oz\"\n },\n \"price\": {\n \"value\": 2800,\n \"currency\": \"USD\"\n }\n }\n ],\n \"service\": null,\n \"label_url\": null,\n \"qr_code_url\": null,\n \"tracking_number\": null\n }\n ]\n }\n}"},{"language":"json","filename":"purchaseRequested","code":"{\n \"topic\": \"ship-by-loop.shipment\",\n \"trigger\": \"ship-by-loop.purchase.requested\",\n \"shop_id\": 4321,\n \"shipment\": {\n \"id\": \"ff8a4470-41e0-4cc3-9bfe-44564f4286b7\",\n \"type\": \"return\",\n \"status\": \"new\",\n \"origin\": {\n \"name\": \"Jane Shopper\",\n \"address1\": \"123 Main Street\",\n \"address2\": null,\n \"city\": \"Columbus\",\n \"state\": \"OH\",\n \"zip\": \"43215\",\n \"country\": \"US\",\n \"email\": \"jane@example.com\",\n \"phone\": \"5555555555\"\n },\n \"destination\": {\n \"name\": \"Returns Processing\",\n \"address1\": \"500 Industrial Pkwy\",\n \"address2\": null,\n \"city\": \"Reno\",\n \"state\": \"NV\",\n \"zip\": \"89501\",\n \"country\": \"US\",\n \"email\": \"returns@example.com\",\n \"phone\": \"5555550199\"\n },\n \"outbound_order\": {\n \"order_number\": \"#1001\",\n \"carrier\": \"USPS\",\n \"tracking_number\": \"1Z888\",\n \"total_value\": {\n \"value\": 4565,\n \"currency\": \"USD\"\n },\n \"shipped_date\": \"2026-08-01T14:32:00+00:00\",\n \"commercial_invoice_number\": null\n },\n \"packages\": [\n {\n \"id\": \"900a8671-35ad-4743-9b6c-2540d3bb1faf\",\n \"dimensions\": {\n \"length\": 12,\n \"width\": 9,\n \"height\": 4,\n \"unit\": \"in\"\n },\n \"weight\": {\n \"value\": 24,\n \"unit\": \"oz\"\n },\n \"items\": [\n {\n \"id\": \"3c6e9d0a-9b1a-4e9f-8c3e-5a2b1d4f6e7a\",\n \"name\": \"Classic Tee\",\n \"sku\": \"SKU1234567890\",\n \"country_of_origin\": \"US\",\n \"hs_code\": \"6109.10.00\",\n \"weight\": {\n \"value\": 8,\n \"unit\": \"oz\"\n },\n \"price\": {\n \"value\": 2800,\n \"currency\": \"USD\"\n }\n }\n ],\n \"service\": {\n \"id\": \"svc_8f2a1c3d\",\n \"external_id\": \"svc_ground\",\n \"name\": \"Ground Advantage\",\n \"carrier\": \"USPS\",\n \"rate\": null,\n \"packages\": [\n \"900a8671-35ad-4743-9b6c-2540d3bb1faf\"\n ]\n },\n \"label_url\": null,\n \"qr_code_url\": null,\n \"tracking_number\": null\n }\n ]\n }\n}"},{"language":"json","filename":"cancellationRequested","code":"{\n \"topic\": \"ship-by-loop.shipment\",\n \"trigger\": \"ship-by-loop.cancellation.requested\",\n \"shop_id\": 4321,\n \"shipment\": {\n \"id\": \"ff8a4470-41e0-4cc3-9bfe-44564f4286b7\",\n \"type\": \"return\",\n \"status\": \"cancelled\",\n \"origin\": {\n \"name\": \"Jane Shopper\",\n \"address1\": \"123 Main Street\",\n \"address2\": null,\n \"city\": \"Columbus\",\n \"state\": \"OH\",\n \"zip\": \"43215\",\n \"country\": \"US\",\n \"email\": \"jane@example.com\",\n \"phone\": \"5555555555\"\n },\n \"destination\": {\n \"name\": \"Returns Processing\",\n \"address1\": \"500 Industrial Pkwy\",\n \"address2\": null,\n \"city\": \"Reno\",\n \"state\": \"NV\",\n \"zip\": \"89501\",\n \"country\": \"US\",\n \"email\": \"returns@example.com\",\n \"phone\": \"5555550199\"\n },\n \"outbound_order\": {\n \"order_number\": \"#1001\",\n \"carrier\": \"USPS\",\n \"tracking_number\": \"1Z888\",\n \"total_value\": {\n \"value\": 4565,\n \"currency\": \"USD\"\n },\n \"shipped_date\": \"2026-08-01T14:32:00+00:00\",\n \"commercial_invoice_number\": null\n },\n \"packages\": [\n {\n \"id\": \"900a8671-35ad-4743-9b6c-2540d3bb1faf\",\n \"dimensions\": {\n \"length\": 12,\n \"width\": 9,\n \"height\": 4,\n \"unit\": \"in\"\n },\n \"weight\": {\n \"value\": 24,\n \"unit\": \"oz\"\n },\n \"items\": [\n {\n \"id\": \"3c6e9d0a-9b1a-4e9f-8c3e-5a2b1d4f6e7a\",\n \"name\": \"Classic Tee\",\n \"sku\": \"SKU1234567890\",\n \"country_of_origin\": \"US\",\n \"hs_code\": \"6109.10.00\",\n \"weight\": {\n \"value\": 8,\n \"unit\": \"oz\"\n },\n \"price\": {\n \"value\": 2800,\n \"currency\": \"USD\"\n }\n }\n ],\n \"service\": {\n \"id\": \"svc_8f2a1c3d\",\n \"external_id\": \"svc_ground\",\n \"name\": \"Ground Advantage\",\n \"carrier\": \"USPS\",\n \"rate\": {\n \"value\": 0,\n \"currency\": \"USD\"\n },\n \"packages\": [\n \"900a8671-35ad-4743-9b6c-2540d3bb1faf\"\n ]\n },\n \"label_url\": \"https://partner.example/labels/1.pdf\",\n \"qr_code_url\": \"https://partner.example/qr/1.png\",\n \"tracking_number\": {\n \"value\": \"1Z999\",\n \"url\": \"https://partner.example/track/1Z999\"\n }\n }\n ]\n }\n}"}],"requestExampleType":"webhook","responseExampleType":"generated"},"dependencies":{"parameters":{},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"The envelope delivered on every Ship by Loop webhook","required":["topic","trigger","shop_id","shipment"],"properties":{"topic":{"type":"string","const":"ship-by-loop.shipment","isRequired":true,"uniqueKey":"topic","typeLabel":"string"},"trigger":{"type":"string","description":"The event that produced this delivery","enum":["ship-by-loop.services.requested","ship-by-loop.purchase.requested","ship-by-loop.cancellation.requested"],"examples":["ship-by-loop.services.requested"],"isRequired":true,"uniqueKey":"trigger","typeLabel":"enum\u003cstring>"},"shop_id":{"type":"integer","description":"The Loop shop the shipment belongs to","examples":[4321],"isRequired":true,"uniqueKey":"shop_id","typeLabel":"integer"},"shipment":{"type":"object","required":["id","type","status","origin","destination","outbound_order","packages"],"properties":{"id":{"type":"string","description":"Use this as `shipmentId` when responding","examples":["ff8a4470-41e0-4cc3-9bfe-44564f4286b7"],"isRequired":true,"uniqueKey":"shipment.id","typeLabel":"string"},"type":{"type":"string","enum":["return","outbound"],"examples":["return"],"isRequired":true,"uniqueKey":"shipment.type","typeLabel":"enum\u003cstring>"},"status":{"type":"string","description":"The shipment's state. `services.requested` delivers `draft`, `purchase.requested` delivers `new`, and `cancellation.requested` delivers `cancelled`.","enum":["draft","new","cancelled"],"examples":["draft"],"isRequired":true,"uniqueKey":"shipment.status","typeLabel":"enum\u003cstring>"},"origin":{"type":"object","required":["name","address1","address2","city","state","zip","country","email","phone"],"properties":{"name":{"type":"string","examples":["Some Name"],"isRequired":true,"uniqueKey":"shipment.origin.name","typeLabel":"string"},"address1":{"type":"string","examples":["123 Main Street"],"isRequired":true,"uniqueKey":"shipment.origin.address1","typeLabel":"string"},"address2":{"oneOf":[{"type":"string","examples":[null],"isRequired":true,"uniqueKey":"shipment.origin.address2.oneOf.0","typeLabel":"string"},{"type":"null","examples":[null],"isRequired":true,"uniqueKey":"shipment.origin.address2.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.origin.address2","typeLabel":"string | null"},"city":{"type":"string","examples":["Columbus"],"isRequired":true,"uniqueKey":"shipment.origin.city","typeLabel":"string"},"state":{"oneOf":[{"type":"string","description":"The ISO 3166-2 alpha-2 region code of the address's state or province.","examples":["OH"],"isRequired":true,"uniqueKey":"shipment.origin.state.oneOf.0","typeLabel":"string"},{"type":"null","description":"The ISO 3166-2 alpha-2 region code of the address's state or province.","examples":["OH"],"isRequired":true,"uniqueKey":"shipment.origin.state.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.origin.state","typeLabel":"string | null"},"zip":{"type":"string","examples":["43215"],"isRequired":true,"uniqueKey":"shipment.origin.zip","typeLabel":"string"},"country":{"type":"string","description":"The ISO 3166-1 alpha-2 country code of the address.","examples":["US"],"isRequired":true,"uniqueKey":"shipment.origin.country","typeLabel":"string"},"email":{"oneOf":[{"type":"string","format":"email","examples":["test@loopreturns.com"],"isRequired":true,"uniqueKey":"shipment.origin.email.oneOf.0","typeLabel":"string\u003cemail>"},{"type":"null","format":"email","examples":["test@loopreturns.com"],"isRequired":true,"uniqueKey":"shipment.origin.email.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.origin.email","typeLabel":"string\u003cemail> | null"},"phone":{"oneOf":[{"type":"string","examples":["5555555555"],"isRequired":true,"uniqueKey":"shipment.origin.phone.oneOf.0","typeLabel":"string"},{"type":"null","examples":["5555555555"],"isRequired":true,"uniqueKey":"shipment.origin.phone.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.origin.phone","typeLabel":"string | null"}},"isRequired":true,"uniqueKey":"shipment.origin","typeLabel":"object"},"destination":{"type":"object","required":["name","address1","address2","city","state","zip","country","email","phone"],"properties":{"name":{"type":"string","examples":["Some Name"],"isRequired":true,"uniqueKey":"shipment.destination.name","typeLabel":"string"},"address1":{"type":"string","examples":["123 Main Street"],"isRequired":true,"uniqueKey":"shipment.destination.address1","typeLabel":"string"},"address2":{"oneOf":[{"type":"string","examples":[null],"isRequired":true,"uniqueKey":"shipment.destination.address2.oneOf.0","typeLabel":"string"},{"type":"null","examples":[null],"isRequired":true,"uniqueKey":"shipment.destination.address2.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.destination.address2","typeLabel":"string | null"},"city":{"type":"string","examples":["Columbus"],"isRequired":true,"uniqueKey":"shipment.destination.city","typeLabel":"string"},"state":{"oneOf":[{"type":"string","description":"The ISO 3166-2 alpha-2 region code of the address's state or province.","examples":["OH"],"isRequired":true,"uniqueKey":"shipment.destination.state.oneOf.0","typeLabel":"string"},{"type":"null","description":"The ISO 3166-2 alpha-2 region code of the address's state or province.","examples":["OH"],"isRequired":true,"uniqueKey":"shipment.destination.state.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.destination.state","typeLabel":"string | null"},"zip":{"type":"string","examples":["43215"],"isRequired":true,"uniqueKey":"shipment.destination.zip","typeLabel":"string"},"country":{"type":"string","description":"The ISO 3166-1 alpha-2 country code of the address.","examples":["US"],"isRequired":true,"uniqueKey":"shipment.destination.country","typeLabel":"string"},"email":{"oneOf":[{"type":"string","format":"email","examples":["test@loopreturns.com"],"isRequired":true,"uniqueKey":"shipment.destination.email.oneOf.0","typeLabel":"string\u003cemail>"},{"type":"null","format":"email","examples":["test@loopreturns.com"],"isRequired":true,"uniqueKey":"shipment.destination.email.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.destination.email","typeLabel":"string\u003cemail> | null"},"phone":{"oneOf":[{"type":"string","examples":["5555555555"],"isRequired":true,"uniqueKey":"shipment.destination.phone.oneOf.0","typeLabel":"string"},{"type":"null","examples":["5555555555"],"isRequired":true,"uniqueKey":"shipment.destination.phone.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.destination.phone","typeLabel":"string | null"}},"isRequired":true,"uniqueKey":"shipment.destination","typeLabel":"object"},"outbound_order":{"oneOf":[{"type":"object","description":"The original outbound order, when the shipment is a return","required":["order_number","carrier","tracking_number","total_value","shipped_date","commercial_invoice_number"],"properties":{"order_number":{"type":"string","examples":["#1001"],"isRequired":true,"uniqueKey":"shipment.outbound_order.oneOf.0.order_number","typeLabel":"string"},"carrier":{"type":"string","examples":["USPS"],"isRequired":true,"uniqueKey":"shipment.outbound_order.oneOf.0.carrier","typeLabel":"string"},"tracking_number":{"type":"string","examples":["1Z888"],"isRequired":true,"uniqueKey":"shipment.outbound_order.oneOf.0.tracking_number","typeLabel":"string"},"total_value":{"type":"object","required":["value","currency"],"properties":{"value":{"type":"integer","description":"Minor units","examples":[4565],"isRequired":true,"uniqueKey":"shipment.outbound_order.oneOf.0.total_value.value","typeLabel":"integer"},"currency":{"type":"string","examples":["USD"],"isRequired":true,"uniqueKey":"shipment.outbound_order.oneOf.0.total_value.currency","typeLabel":"string"}},"isRequired":true,"uniqueKey":"shipment.outbound_order.oneOf.0.total_value","typeLabel":"object"},"shipped_date":{"type":"string","format":"date-time","description":"ISO 8601","examples":["2026-08-01T14:32:00+00:00"],"isRequired":true,"uniqueKey":"shipment.outbound_order.oneOf.0.shipped_date","typeLabel":"string\u003cdate-time>"},"commercial_invoice_number":{"oneOf":[{"type":"string","examples":["CI-10293"],"isRequired":true,"uniqueKey":"shipment.outbound_order.oneOf.0.commercial_invoice_number.oneOf.0","typeLabel":"string"},{"type":"null","examples":["CI-10293"],"isRequired":true,"uniqueKey":"shipment.outbound_order.oneOf.0.commercial_invoice_number.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.outbound_order.oneOf.0.commercial_invoice_number","typeLabel":"string | null"}},"isRequired":true,"uniqueKey":"shipment.outbound_order.oneOf.0","typeLabel":"object"},{"type":"null","description":"The original outbound order, when the shipment is a return","isRequired":true,"uniqueKey":"shipment.outbound_order.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.outbound_order","typeLabel":"object | null"},"packages":{"type":"array","items":{"type":"object","required":["id","dimensions","weight","items","service","label_url","qr_code_url","tracking_number"],"properties":{"id":{"type":"string","description":"Use this as `package_id` when responding","examples":["900a8671-35ad-4743-9b6c-2540d3bb1faf"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].id","typeLabel":"string"},"dimensions":{"type":"object","required":["length","width","height","unit"],"properties":{"length":{"type":"number","examples":[2],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].dimensions.length","typeLabel":"number"},"width":{"type":"number","examples":[2],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].dimensions.width","typeLabel":"number"},"height":{"type":"number","examples":[2],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].dimensions.height","typeLabel":"number"},"unit":{"type":"string","examples":["in"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].dimensions.unit","typeLabel":"string"}},"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].dimensions","typeLabel":"object"},"weight":{"type":"object","required":["value","unit"],"properties":{"value":{"type":"number","examples":[8],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].weight.value","typeLabel":"number"},"unit":{"type":"string","examples":["oz"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].weight.unit","typeLabel":"string"}},"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].weight","typeLabel":"object"},"items":{"type":"array","items":{"type":"object","required":["id","name","sku","country_of_origin","hs_code","weight","price"],"properties":{"id":{"type":"string","examples":["3c6e9d0a-9b1a-4e9f-8c3e-5a2b1d4f6e7a"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].id","typeLabel":"string"},"name":{"type":"string","examples":["Test Item"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].name","typeLabel":"string"},"sku":{"oneOf":[{"type":"string","examples":["SKU1234567890"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].sku.oneOf.0","typeLabel":"string"},{"type":"null","examples":["SKU1234567890"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].sku.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].sku","typeLabel":"string | null"},"country_of_origin":{"oneOf":[{"type":"string","examples":["US"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].country_of_origin.oneOf.0","typeLabel":"string"},{"type":"null","examples":["US"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].country_of_origin.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].country_of_origin","typeLabel":"string | null"},"hs_code":{"oneOf":[{"type":"string","examples":["7117.19.00"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].hs_code.oneOf.0","typeLabel":"string"},{"type":"null","examples":["7117.19.00"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].hs_code.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].hs_code","typeLabel":"string | null"},"weight":{"oneOf":[{"type":"object","required":["value","unit"],"properties":{"value":{"type":"number","examples":[8],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].weight.oneOf.0.value","typeLabel":"number"},"unit":{"type":"string","examples":["oz"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].weight.oneOf.0.unit","typeLabel":"string"}},"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].weight.oneOf.0","typeLabel":"object"},{"type":"null","isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].weight.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].weight","typeLabel":"object | null"},"price":{"type":"object","required":["value","currency"],"properties":{"value":{"type":"integer","description":"Minor units","examples":[4565],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].price.value","typeLabel":"integer"},"currency":{"type":"string","examples":["USD"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].price.currency","typeLabel":"string"}},"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX].price","typeLabel":"object"}},"uniqueKey":"shipment.packages.items.[INDEX].\\items.items.[INDEX]","typeLabel":"object"},"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].\\items","typeLabel":"object[]"},"service":{"oneOf":[{"type":"object","description":"The selected service. Null until one has been chosen","required":["id","external_id","name","carrier","rate","packages"],"properties":{"id":{"type":"string","description":"Loop's identifier for the service","examples":["svc_8f2a1c3d"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].service.oneOf.0.id","typeLabel":"string"},"external_id":{"type":"string","description":"The identifier the provider supplied","examples":["svc_ground"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].service.oneOf.0.external_id","typeLabel":"string"},"name":{"type":"string","examples":["Ground Advantage"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].service.oneOf.0.name","typeLabel":"string"},"carrier":{"type":"string","examples":["USPS"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].service.oneOf.0.carrier","typeLabel":"string"},"rate":{"oneOf":[{"type":"object","required":["value","currency"],"properties":{"value":{"type":"integer","description":"Minor units","examples":[4565],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].service.oneOf.0.rate.oneOf.0.value","typeLabel":"integer"},"currency":{"type":"string","examples":["USD"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].service.oneOf.0.rate.oneOf.0.currency","typeLabel":"string"}},"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].service.oneOf.0.rate.oneOf.0","typeLabel":"object"},{"type":"null","isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].service.oneOf.0.rate.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].service.oneOf.0.rate","typeLabel":"object | null"},"packages":{"type":"array","items":{"type":"string","examples":["900a8671-35ad-4743-9b6c-2540d3bb1faf"],"uniqueKey":"shipment.packages.items.[INDEX].service.oneOf.0.packages.items.[INDEX]","typeLabel":"string"},"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].service.oneOf.0.packages","typeLabel":"string[]"}},"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].service.oneOf.0","typeLabel":"object"},{"type":"null","description":"The selected service. Null until one has been chosen","isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].service.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].service","typeLabel":"object | null"},"label_url":{"oneOf":[{"type":"string","format":"uri","description":"Set once a label has been recorded against the package","examples":["https://partner.example/labels/1.pdf"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].label_url.oneOf.0","typeLabel":"string\u003curi>"},{"type":"null","format":"uri","description":"Set once a label has been recorded against the package","examples":["https://partner.example/labels/1.pdf"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].label_url.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].label_url","typeLabel":"string\u003curi> | null"},"qr_code_url":{"oneOf":[{"type":"string","format":"uri","examples":["https://partner.example/qr/1.png"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].qr_code_url.oneOf.0","typeLabel":"string\u003curi>"},{"type":"null","format":"uri","examples":["https://partner.example/qr/1.png"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].qr_code_url.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].qr_code_url","typeLabel":"string\u003curi> | null"},"tracking_number":{"oneOf":[{"type":"object","required":["value","url"],"properties":{"value":{"type":"string","examples":["1Z999"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].tracking_number.oneOf.0.value","typeLabel":"string"},"url":{"oneOf":[{"type":"string","format":"uri","examples":["https://partner.example/track/1Z999"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].tracking_number.oneOf.0.url.oneOf.0","typeLabel":"string\u003curi>"},{"type":"null","format":"uri","examples":["https://partner.example/track/1Z999"],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].tracking_number.oneOf.0.url.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].tracking_number.oneOf.0.url","typeLabel":"string\u003curi> | null"}},"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].tracking_number.oneOf.0","typeLabel":"object"},{"type":"null","isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].tracking_number.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"shipment.packages.items.[INDEX].tracking_number","typeLabel":"object | null"}},"uniqueKey":"shipment.packages.items.[INDEX]","typeLabel":"object"},"isRequired":true,"uniqueKey":"shipment.packages","typeLabel":"object[]"}},"isRequired":true,"uniqueKey":"shipment","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"examples":{"servicesRequested":{"title":"servicesRequested","value":{"topic":"ship-by-loop.shipment","trigger":"ship-by-loop.services.requested","shop_id":4321,"shipment":{"id":"ff8a4470-41e0-4cc3-9bfe-44564f4286b7","type":"return","status":"draft","origin":{"name":"Jane Shopper","address1":"123 Main Street","address2":null,"city":"Columbus","state":"OH","zip":"43215","country":"US","email":"jane@example.com","phone":"5555555555"},"destination":{"name":"Returns Processing","address1":"500 Industrial Pkwy","address2":null,"city":"Reno","state":"NV","zip":"89501","country":"US","email":"returns@example.com","phone":"5555550199"},"outbound_order":{"order_number":"#1001","carrier":"USPS","tracking_number":"1Z888","total_value":{"value":4565,"currency":"USD"},"shipped_date":"2026-08-01T14:32:00+00:00","commercial_invoice_number":null},"packages":[{"id":"900a8671-35ad-4743-9b6c-2540d3bb1faf","dimensions":{"length":12,"width":9,"height":4,"unit":"in"},"weight":{"value":24,"unit":"oz"},"items":[{"id":"3c6e9d0a-9b1a-4e9f-8c3e-5a2b1d4f6e7a","name":"Classic Tee","sku":"SKU1234567890","country_of_origin":"US","hs_code":"6109.10.00","weight":{"value":8,"unit":"oz"},"price":{"value":2800,"currency":"USD"}}],"service":null,"label_url":null,"qr_code_url":null,"tracking_number":null}]}}},"purchaseRequested":{"title":"purchaseRequested","description":"`service` is now set (every package has one selected) — `label_url`, `qr_code_url` and `tracking_number` stay `null` until the provider calls `POST .../orders`.","value":{"topic":"ship-by-loop.shipment","trigger":"ship-by-loop.purchase.requested","shop_id":4321,"shipment":{"id":"ff8a4470-41e0-4cc3-9bfe-44564f4286b7","type":"return","status":"new","origin":{"name":"Jane Shopper","address1":"123 Main Street","address2":null,"city":"Columbus","state":"OH","zip":"43215","country":"US","email":"jane@example.com","phone":"5555555555"},"destination":{"name":"Returns Processing","address1":"500 Industrial Pkwy","address2":null,"city":"Reno","state":"NV","zip":"89501","country":"US","email":"returns@example.com","phone":"5555550199"},"outbound_order":{"order_number":"#1001","carrier":"USPS","tracking_number":"1Z888","total_value":{"value":4565,"currency":"USD"},"shipped_date":"2026-08-01T14:32:00+00:00","commercial_invoice_number":null},"packages":[{"id":"900a8671-35ad-4743-9b6c-2540d3bb1faf","dimensions":{"length":12,"width":9,"height":4,"unit":"in"},"weight":{"value":24,"unit":"oz"},"items":[{"id":"3c6e9d0a-9b1a-4e9f-8c3e-5a2b1d4f6e7a","name":"Classic Tee","sku":"SKU1234567890","country_of_origin":"US","hs_code":"6109.10.00","weight":{"value":8,"unit":"oz"},"price":{"value":2800,"currency":"USD"}}],"service":{"id":"svc_8f2a1c3d","external_id":"svc_ground","name":"Ground Advantage","carrier":"USPS","rate":null,"packages":["900a8671-35ad-4743-9b6c-2540d3bb1faf"]},"label_url":null,"qr_code_url":null,"tracking_number":null}]}}},"cancellationRequested":{"title":"cancellationRequested","description":"Every package field reflects whatever had already been recorded before the cancellation — this example shows a package where a label had already been purchased.","value":{"topic":"ship-by-loop.shipment","trigger":"ship-by-loop.cancellation.requested","shop_id":4321,"shipment":{"id":"ff8a4470-41e0-4cc3-9bfe-44564f4286b7","type":"return","status":"cancelled","origin":{"name":"Jane Shopper","address1":"123 Main Street","address2":null,"city":"Columbus","state":"OH","zip":"43215","country":"US","email":"jane@example.com","phone":"5555555555"},"destination":{"name":"Returns Processing","address1":"500 Industrial Pkwy","address2":null,"city":"Reno","state":"NV","zip":"89501","country":"US","email":"returns@example.com","phone":"5555550199"},"outbound_order":{"order_number":"#1001","carrier":"USPS","tracking_number":"1Z888","total_value":{"value":4565,"currency":"USD"},"shipped_date":"2026-08-01T14:32:00+00:00","commercial_invoice_number":null},"packages":[{"id":"900a8671-35ad-4743-9b6c-2540d3bb1faf","dimensions":{"length":12,"width":9,"height":4,"unit":"in"},"weight":{"value":24,"unit":"oz"},"items":[{"id":"3c6e9d0a-9b1a-4e9f-8c3e-5a2b1d4f6e7a","name":"Classic Tee","sku":"SKU1234567890","country_of_origin":"US","hs_code":"6109.10.00","weight":{"value":8,"unit":"oz"},"price":{"value":2800,"currency":"USD"}}],"service":{"id":"svc_8f2a1c3d","external_id":"svc_ground","name":"Ground Advantage","carrier":"USPS","rate":{"value":0,"currency":"USD"},"packages":["900a8671-35ad-4743-9b6c-2540d3bb1faf"]},"label_url":"https://partner.example/labels/1.pdf","qr_code_url":"https://partner.example/qr/1.png","tracking_number":{"value":"1Z999","url":"https://partner.example/track/1Z999"}}]}}}},"exampleType":"examples"}},"exampleType":"examples"},"responses":{"9e50b22d-9206-4b93-9e45-28c9a73c872a":{"description":"Return a 200 status to indicate that the data was received successfully.","content":{"_mintlify/placeholder":{"schema":{"type":"object","description":""}}}}},"security":{},"servers":{}},"originalFileLocation":"openapi/ship-by-loop-webhook.yaml"},"subdomain":"loopreturns","siteName":"Loop Returns Documentation","title":"Ship by Loop shipment - Loop Returns Documentation","navMaps":{"firstHrefInVersion":{"2026-07":{"href":"/webhook-reference/topics/return-webhook","matchLevel":2},"2026-10":{"href":"/webhook-reference/topics/ship-by-loop-shipment-webhook","matchLevel":0},"unstable":{"href":"/webhook-reference/topics/ship-by-loop-shipment-webhook","matchLevel":0}},"firstHrefInLanguage":{}},"lastModified":"2026-09-30T19:22:02.044Z","originalFileLocation":"webhook-reference/topics/ship-by-loop-shipment-webhook.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"| Trigger | What it means | | ------------------------------------- | ------------------------------------------------------- | | ship-by-loop.services.requested | A shipment was created that this provider can fulfill. | | ship-by-loop.purchase.requested | Services have been selected and are ready for purchase. | | ship-by-loop.cancellation.requested | The shipment was cancelled. | - ship-by-loop.services.requested: submit the services this provider can offer for the shipment\u0026#x27;s packages with POST /ship-by-loop/shipments/{shipmentId}/services. The shopper-facing portal polls for a response roughly every 5 seconds, up to 12 times (roughly 30-60 seconds total). If nothing arrives in that window, the shopper falls back to whichever standard (non-carrier-choice) return methods were already available — no error is shown. The API still accepts a POST .../services call after that window, but the portal has stopped polling for it by then, so the shopper who already fell back will not see it. - ship-by-loop.purchase.requested: record the resulting purchase, including tracking and label details, with POST /ship-by-loop/shipments/{shipmentId}/orders. - ship-by-loop.cancellation.requested: stop any work in progress for this shipment and void/refund any carrier label already purchased. Loop does not expect a response beyond the 200 acknowledgement. Acknowledge every delivery with a 200 response. ### Package field state by trigger service, label_url, qr_code_url, and tracking_number on each packages entry are populated over the lifetime of a shipment rather than all at once: | Trigger | service | label_url | qr_code_url | tracking_number | | --------------------------- | ---------- | ------------ | -------------- | ------------------ | | services.requested | null | null | null | null | | purchase.requested | set | null | null | null | | cancellation.requested | see below | see below | see below | see below | service is null at services.requested (nothing has been chosen yet) and always set by purchase.requested — Loop only asks a provider to purchase once every package has a service selected. label_url / qr_code_url / tracking_number stay null through both of those triggers; they\u0026#x27;re populated only once the provider calls POST .../orders, which happens after purchase.requested is received. For cancellation.requested, every field reflects whatever had already been recorded before the cancellation — null for any of them the provider hadn\u0026#x27;t gotten to yet.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Loop Returns Documentation\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/loopreturns/X0rUYkP1UIsEaMhI/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/loopreturns/X0rUYkP1UIsEaMhI/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/loopreturns/X0rUYkP1UIsEaMhI/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/loopreturns/X0rUYkP1UIsEaMhI/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/loopreturns/X0rUYkP1UIsEaMhI/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/loopreturns/X0rUYkP1UIsEaMhI/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/loopreturns/X0rUYkP1UIsEaMhI/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/loopreturns/X0rUYkP1UIsEaMhI/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/loopreturns/X0rUYkP1UIsEaMhI/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Ship by Loop shipment - Loop Returns Documentation\">\u003cmeta data-mint-head property=\"og:description\" content=\"| Trigger | What it means | | ------------------------------------- | ------------------------------------------------------- | | ship-by-loop.services.requested | A shipment was created that this provider can fulfill. | | ship-by-loop.purchase.requested | Services have been selected and are ready for purchase. | | ship-by-loop.cancellation.requested | The shipment was cancelled. | - ship-by-loop.services.requested: submit the services this provider can offer for the shipment\u0026#x27;s packages with POST /ship-by-loop/shipments/{shipmentId}/services. The shopper-facing portal polls for a response roughly every 5 seconds, up to 12 times (roughly 30-60 seconds total). If nothing arrives in that window, the shopper falls back to whichever standard (non-carrier-choice) return methods were already available — no error is shown. The API still accepts a POST .../services call after that window, but the portal has stopped polling for it by then, so the shopper who already fell back will not see it. - ship-by-loop.purchase.requested: record the resulting purchase, including tracking and label details, with POST /ship-by-loop/shipments/{shipmentId}/orders. - ship-by-loop.cancellation.requested: stop any work in progress for this shipment and void/refund any carrier label already purchased. Loop does not expect a response beyond the 200 acknowledgement. Acknowledge every delivery with a 200 response. ### Package field state by trigger service, label_url, qr_code_url, and tracking_number on each packages entry are populated over the lifetime of a shipment rather than all at once: | Trigger | service | label_url | qr_code_url | tracking_number | | --------------------------- | ---------- | ------------ | -------------- | ------------------ | | services.requested | null | null | null | null | | purchase.requested | set | null | null | null | | cancellation.requested | see below | see below | see below | see below | service is null at services.requested (nothing has been chosen yet) and always set by purchase.requested — Loop only asks a provider to purchase once every package has a service selected. label_url / qr_code_url / tracking_number stay null through both of those triggers; they\u0026#x27;re populated only once the provider calls POST .../orders, which happens after purchase.requested is received. For cancellation.requested, every field reflects whatever had already been recorded before the cancellation — null for any of them the provider hadn\u0026#x27;t gotten to yet.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Loop Returns Documentation\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.loopreturns.com/webhook-reference/topics/ship-by-loop-shipment-webhook\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://loopreturns.mintlify.site/_mintlify/api/og/v2?division=Webhook+Payloads\u0026amp;title=Ship+by+Loop+shipment\u0026amp;description=%7C+Trigger++++++++++++++++++++++++++++++%7C+What+it+means++++++++++++++++++++++++++++++++++++++++++%7C%0A%7C+-------------------------------------+%7C+--------------------\u0026amp;theme=5ccacdea69e246fe33e6ab29\u0026amp;signature=1bd415e0c3703b5f53f44581d58aa4879436ee770d0ff87eeb27b3b6e6a10de4\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Ship by Loop shipment - Loop Returns Documentation\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"| Trigger | What it means | | ------------------------------------- | ------------------------------------------------------- | | ship-by-loop.services.requested | A shipment was created that this provider can fulfill. | | ship-by-loop.purchase.requested | Services have been selected and are ready for purchase. | | ship-by-loop.cancellation.requested | The shipment was cancelled. | - ship-by-loop.services.requested: submit the services this provider can offer for the shipment\u0026#x27;s packages with POST /ship-by-loop/shipments/{shipmentId}/services. The shopper-facing portal polls for a response roughly every 5 seconds, up to 12 times (roughly 30-60 seconds total). If nothing arrives in that window, the shopper falls back to whichever standard (non-carrier-choice) return methods were already available — no error is shown. The API still accepts a POST .../services call after that window, but the portal has stopped polling for it by then, so the shopper who already fell back will not see it. - ship-by-loop.purchase.requested: record the resulting purchase, including tracking and label details, with POST /ship-by-loop/shipments/{shipmentId}/orders. - ship-by-loop.cancellation.requested: stop any work in progress for this shipment and void/refund any carrier label already purchased. Loop does not expect a response beyond the 200 acknowledgement. Acknowledge every delivery with a 200 response. ### Package field state by trigger service, label_url, qr_code_url, and tracking_number on each packages entry are populated over the lifetime of a shipment rather than all at once: | Trigger | service | label_url | qr_code_url | tracking_number | | --------------------------- | ---------- | ------------ | -------------- | ------------------ | | services.requested | null | null | null | null | | purchase.requested | set | null | null | null | | cancellation.requested | see below | see below | see below | see below | service is null at services.requested (nothing has been chosen yet) and always set by purchase.requested — Loop only asks a provider to purchase once every package has a service selected. label_url / qr_code_url / tracking_number stay null through both of those triggers; they\u0026#x27;re populated only once the provider calls POST .../orders, which happens after purchase.requested is received. For cancellation.requested, every field reflects whatever had already been recorded before the cancellation — null for any of them the provider hadn\u0026#x27;t gotten to yet.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://loopreturns.mintlify.site/_mintlify/api/og/v2?division=Webhook+Payloads\u0026amp;title=Ship+by+Loop+shipment\u0026amp;description=%7C+Trigger++++++++++++++++++++++++++++++%7C+What+it+means++++++++++++++++++++++++++++++++++++++++++%7C%0A%7C+-------------------------------------+%7C+--------------------\u0026amp;theme=5ccacdea69e246fe33e6ab29\u0026amp;signature=1bd415e0c3703b5f53f44581d58aa4879436ee770d0ff87eeb27b3b6e6a10de4\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/loopreturns/X0rUYkP1UIsEaMhI/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Loop Returns Documentation\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#0672FF\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.loopreturns.com/webhook-reference/topics/ship-by-loop-shipment-webhook\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/webhook-reference/topics/ship-by-loop-shipment-webhook.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.loopreturns.com/#organization\",\"name\":\"Loop Returns Documentation\",\"url\":\"https://docs.loopreturns.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/loopreturns/mWP1nZGNp75FiB-l/logo/loop-logo-lapis-charcoal.svg?fit=max\u0026auto=format\u0026n=mWP1nZGNp75FiB-l\u0026q=85\u0026s=5113cc3f8f2d70a65c2c774e471a13c4\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.loopreturns.com#website\",\"name\":\"Loop Returns Documentation\",\"url\":\"https://docs.loopreturns.com\",\"publisher\":{\"@id\":\"https://docs.loopreturns.com/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.loopreturns.com/webhook-reference/topics/ship-by-loop-shipment-webhook#webpage\",\"url\":\"https://docs.loopreturns.com/webhook-reference/topics/ship-by-loop-shipment-webhook\",\"name\":\"Ship by Loop shipment\",\"description\":\"| Trigger | What it means | | ------------------------------------- | ------------------------------------------------------- | | ship-by-loop.services.requested | A shipment was created that this provider can fulfill. | | ship-by-loop.purchase.requested | Services have been selected and are ready for purchase. | | ship-by-loop.cancellation.requested | The shipment was cancelled. | - ship-by-loop.services.requested: submit the services this provider can offer for the shipment's packages with POST /ship-by-loop/shipments/{shipmentId}/services. The shopper-facing portal polls for a response roughly every 5 seconds, up to 12 times (roughly 30-60 seconds total). If nothing arrives in that window, the shopper falls back to whichever standard (non-carrier-choice) return methods were already available — no error is shown. The API still accepts a POST .../services call after that window, but the portal has stopped polling for it by then, so the shopper who already fell back will not see it. - ship-by-loop.purchase.requested: record the resulting purchase, including tracking and label details, with POST /ship-by-loop/shipments/{shipmentId}/orders. - ship-by-loop.cancellation.requested: stop any work in progress for this shipment and void/refund any carrier label already purchased. Loop does not expect a response beyond the 200 acknowledgement. Acknowledge every delivery with a 200 response. ### Package field state by trigger service, label_url, qr_code_url, and tracking_number on each packages entry are populated over the lifetime of a shipment rather than all at once: | Trigger | service | label_url | qr_code_url | tracking_number | | --------------------------- | ---------- | ------------ | -------------- | ------------------ | | services.requested | null | null | null | null | | purchase.requested | set | null | null | null | | cancellation.requested | see below | see below | see below | see below | service is null at services.requested (nothing has been chosen yet) and always set by purchase.requested — Loop only asks a provider to purchase once every package has a service selected. label_url / qr_code_url / tracking_number stay null through both of those triggers; they're populated only once the provider calls POST .../orders, which happens after purchase.requested is received. For cancellation.requested, every field reflects whatever had already been recorded before the cancellation — null for any of them the provider hadn't gotten to yet.\",\"dateModified\":\"2026-09-30T19:22:02.044Z\",\"isPartOf\":{\"@id\":\"https://docs.loopreturns.com#website\"},\"breadcrumb\":{\"@id\":\"https://docs.loopreturns.com/webhook-reference/topics/ship-by-loop-shipment-webhook#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.loopreturns.com/webhook-reference/topics/ship-by-loop-shipment-webhook#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Webhook Payloads\",\"item\":\"https://docs.loopreturns.com/webhook-reference/topics/return-webhook\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Ship by Loop shipment\",\"item\":\"https://docs.loopreturns.com/webhook-reference/topics/ship-by-loop-shipment-webhook\"}]},{\"@type\":[\"TechArticle\",\"APIReference\"],\"@id\":\"https://docs.loopreturns.com/webhook-reference/topics/ship-by-loop-shipment-webhook#apireference\",\"headline\":\"Ship by Loop shipment\",\"name\":\"Ship by Loop shipment\",\"description\":\"| Trigger | What it means | | ------------------------------------- | ------------------------------------------------------- | | ship-by-loop.services.requested | A shipment was created that this provider can fulfill. | | ship-by-loop.purchase.requested | Services have been selected and are ready for purchase. | | ship-by-loop.cancellation.requested | The shipment was cancelled. | - ship-by-loop.services.requested: submit the services this provider can offer for the shipment's packages with POST /ship-by-loop/shipments/{shipmentId}/services. The shopper-facing portal polls for a response roughly every 5 seconds, up to 12 times (roughly 30-60 seconds total). If nothing arrives in that window, the shopper falls back to whichever standard (non-carrier-choice) return methods were already available — no error is shown. The API still accepts a POST .../services call after that window, but the portal has stopped polling for it by then, so the shopper who already fell back will not see it. - ship-by-loop.purchase.requested: record the resulting purchase, including tracking and label details, with POST /ship-by-loop/shipments/{shipmentId}/orders. - ship-by-loop.cancellation.requested: stop any work in progress for this shipment and void/refund any carrier label already purchased. Loop does not expect a response beyond the 200 acknowledgement. Acknowledge every delivery with a 200 response. ### Package field state by trigger service, label_url, qr_code_url, and tracking_number on each packages entry are populated over the lifetime of a shipment rather than all at once: | Trigger | service | label_url | qr_code_url | tracking_number | | --------------------------- | ---------- | ------------ | -------------- | ------------------ | | services.requested | null | null | null | null | | purchase.requested | set | null | null | null | | cancellation.requested | see below | see below | see below | see below | service is null at services.requested (nothing has been chosen yet) and always set by purchase.requested — Loop only asks a provider to purchase once every package has a service selected. label_url / qr_code_url / tracking_number stay null through both of those triggers; they're populated only once the provider calls POST .../orders, which happens after purchase.requested is received. For cancellation.requested, every field reflects whatever had already been recorded before the cancellation — null for any of them the provider hadn't gotten to yet.\",\"url\":\"https://docs.loopreturns.com/webhook-reference/topics/ship-by-loop-shipment-webhook\",\"mainEntityOfPage\":{\"@id\":\"https://docs.loopreturns.com/webhook-reference/topics/ship-by-loop-shipment-webhook#webpage\"},\"dateModified\":\"2026-09-30T19:22:02.044Z\",\"publisher\":{\"@id\":\"https://docs.loopreturns.com/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.loopreturns.com#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Loop Returns Documentation\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Loop Returns Documentation","nav":{"global":{"anchors":[{"anchor":"System Status","icon":"radar","href":"https://status.loopreturns.com"}]},"versions":[{"version":"2026-07","pages":[{"title":"2026-07","href":"/webhook-reference/topics/return-webhook"}]},{"version":"2026-10","default":true,"tabs":[{"tab":"Introduction","pages":[{"title":"Introduction","href":"/welcome"}]},{"tab":"Guides","pages":[{"title":"Guides","href":"/integration-guides/overview"}]},{"tab":"MCP Server","pages":[{"title":"MCP Server","href":"/mcp/overview"}]},{"tab":"API Reference","pages":[{"title":"API Reference","href":"/api-reference/getting-started"}]},{"tab":"Webhook Reference","groups":[{"group":"Webhook Reference","pages":[{"title":"Getting Started","href":"/webhook-reference/getting-started"},{"title":"Create a webhook subscription","href":"/webhook-reference/setting-up"},{"title":"Validate webhooks from Loop","href":"/webhook-reference/validation"}]},{"group":"Webhook Payloads","pages":[{"title":"Return","openapi":"webhook Return","href":"/webhook-reference/topics/return-webhook"},{"title":"Return processing","openapi":"webhook ReturnProcessing","href":"/webhook-reference/topics/return-processing-webhook"},{"title":"Label","openapi":"webhook Label","href":"/webhook-reference/topics/label-webhook"},{"title":"Label requests","openapi":"webhook LabelRequests","description":"| Trigger | What it means |\n| -------------------------- | -------------------------------------------------- |\n| `label.request.issued` | Loop needs a label for this return. |\n| `label.request.cancelled` | A previously issued label request was cancelled. |\n\n- **`label.request.issued`**: the `id` in the payload is the label request ID. Use\n it to create the label with `POST /labels`, or, if label generation fails on\n your end, to report the failure with `POST /label-requests/{id}/errors`.\n- **`label.request.cancelled`**: for example, a merchant regenerated the label, or\n the return itself was cancelled. Stop any work in progress for this `id` and\n void/refund the carrier label if one was already purchased. The `id` is never\n reused: a regenerated label arrives as a brand-new `label.request.issued` event\n with a new `id`.\n\nAcknowledge every delivery with a `200` response. A non-200 response is not how you\nreport a label-generation failure — failures are reported with\n`POST /label-requests/{id}/errors`. Reporting an error does not cause Loop to resend\nthe webhook or issue a new label request: the request remains `issued` and stays\nfulfillable via `POST /labels` with the same `id`, so retrying is your integration's\nown responsibility.\n","href":"/webhook-reference/topics/label-requests-webhook"},{"title":"Ship by Loop shipment","openapi":"webhook ShipByLoopShipment","href":"/webhook-reference/topics/ship-by-loop-shipment-webhook"},{"title":"Happy Returns Shipment","openapi":"webhook HappyReturnsShipmentProcessed","description":null,"href":"/webhook-reference/topics/happy-returns-shipment-webhook"},{"title":"Gift card","openapi":"webhook GiftCardRequested","href":"/webhook-reference/topics/gift-card-webhook"},{"title":"Restock","openapi":"webhook Restocking","href":"/webhook-reference/topics/restock-webhook"}]}]}]},{"version":"unstable","tabs":[{"tab":"Introduction","pages":[{"title":"Introduction","href":"/welcome"}]},{"tab":"Guides","pages":[{"title":"Guides","href":"/integration-guides/overview"}]},{"tab":"MCP Server","pages":[{"title":"MCP Server","href":"/mcp/overview"}]},{"tab":"API Reference","pages":[{"title":"API Reference","href":"/api-reference/unstable/changes"}]},{"tab":"Webhook Reference","groups":[{"group":"Webhook Reference","pages":[{"title":"Getting Started","href":"/webhook-reference/getting-started"},{"title":"Create a webhook subscription","href":"/webhook-reference/setting-up"},{"title":"Validate webhooks from Loop","href":"/webhook-reference/validation"}]},{"group":"Webhook Payloads","pages":[{"title":"Return","openapi":"webhook Return","href":"/webhook-reference/topics/return-webhook"},{"title":"Return processing","openapi":"webhook ReturnProcessing","href":"/webhook-reference/topics/return-processing-webhook"},{"title":"Label","openapi":"webhook Label","href":"/webhook-reference/topics/label-webhook"},{"title":"Label requests","openapi":"webhook LabelRequests","description":"| Trigger | What it means |\n| -------------------------- | -------------------------------------------------- |\n| `label.request.issued` | Loop needs a label for this return. |\n| `label.request.cancelled` | A previously issued label request was cancelled. |\n\n- **`label.request.issued`**: the `id` in the payload is the label request ID. Use\n it to create the label with `POST /labels`, or, if label generation fails on\n your end, to report the failure with `POST /label-requests/{id}/errors`.\n- **`label.request.cancelled`**: for example, a merchant regenerated the label, or\n the return itself was cancelled. Stop any work in progress for this `id` and\n void/refund the carrier label if one was already purchased. The `id` is never\n reused: a regenerated label arrives as a brand-new `label.request.issued` event\n with a new `id`.\n\nAcknowledge every delivery with a `200` response. A non-200 response is not how you\nreport a label-generation failure — failures are reported with\n`POST /label-requests/{id}/errors`. Reporting an error does not cause Loop to resend\nthe webhook or issue a new label request: the request remains `issued` and stays\nfulfillable via `POST /labels` with the same `id`, so retrying is your integration's\nown responsibility.\n","href":"/webhook-reference/topics/label-requests-webhook"},{"title":"Ship by Loop shipment","openapi":"webhook ShipByLoopShipment","href":"/webhook-reference/topics/ship-by-loop-shipment-webhook"},{"title":"Happy Returns Shipment","openapi":"webhook HappyReturnsShipmentProcessed","description":null,"href":"/webhook-reference/topics/happy-returns-shipment-webhook"},{"title":"Gift card","openapi":"webhook GiftCardRequested","href":"/webhook-reference/topics/gift-card-webhook"},{"title":"Restock","openapi":"webhook Restocking","href":"/webhook-reference/topics/restock-webhook"}]}]}]}]},"basePath":"","title":"Ship by Loop shipment - Loop Returns Documentation","buildId":"dpl_BLQvU86QFwq3AhPofSTw6q8dGWKm","deploymentId":"dpl_BLQvU86QFwq3AhPofSTw6q8dGWKm","dynamics":["c2aa737661","2b790eda92","b0f84f53ff","a9bb46feb3"],"chunks":["OpenApiReferenceProvider-Myd8dsju.js","utils-C2HkQgW0.js","cloneDeep-izs3Hzxb.js","_Uint8Array-BVIqJ90l.js","isObjectLike-C8vmzPEN.js","isArray-Dcu1dQ5t.js","utils-8rSppmTv.js","toString-BX-iyh8G.js","isSymbol-CSH9U2Gm.js","addUniqueKeysToSchema-B6MjiCFm.js","snippetPresets-DwrSAONL.js","style-to-object-BY3JEAdk.js","v4-DIgz8zy6.js","ApiExamples-1-a5UQKQ.js","isEqual-BfaiBbcl.js","usePlaygroundInputsStore-C541m7wZ.js","shallow-CWELCZ77.js","getApiPlaygroundDisplay-CTo5Zn03.js","CodeSnippet-D--dzvnl.js","CodeSnippets-DM3cXEDh.js","Page-CxAy5y67.js","getPastedJson-C-VECgcL.js","icon-BPq2-Mg3.js","MethodPill-D9J5cUHd.js","DuplicateIcon-BqHaxUnD.js","createIcon-CIbcZHp9.js","DynamicMarkdown-BKiHAm_d.js","SectionHeading-DstQXt3H.js","axios-vUo_bRtp.js","Markdown-CpxgzsKA.js","clsx-C5EfhVm0.js","lib-DuNMoZ05.js","lib-B8HbPgcC.js","html-void-elements-mCSPHezU.js","dist-CJ9Oly2E.js","katex-YMM-FMeD.js","marked.esm-CZWythHk.js","AssistantPageLink-Mk6P3qvh.js"]}