{"componentChunkName":"component---src-templates-simple-markdown-js","path":"/docs/direct-carrier-connections/tracking-webhooks/","matchPath":"","result":{"data":{"markdownRemark":{"html":"<h1 style=\"position:relative;\"><a href=\"#tracking-webhooks\" aria-label=\"tracking webhooks permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"tracking-webhooks\"></div>Tracking Webhooks</h1>\n<p>Tracking webhooks push tracking changes to your own system as they happen, so you don't have to poll. Register your URL once. From then on, whenever the tracking of freight you booked through <a href=\"/docs/direct-carrier-connections/dispatch/\"><code class=\"language-text\">POST /dispatching/jobs</code></a> changes, Shipwell sends the shipment's full, current tracking to that URL, signed so you can prove it came from Shipwell.</p>\n<p>Each delivery carries the same tracking that <a href=\"/docs/direct-carrier-connections/tracking/\"><code class=\"language-text\">GET /tracking/timelines/{resource_id}</code></a> returns under <code class=\"language-text\">data</code>, with the whole timeline in one document instead of paged. Everything on the <a href=\"/docs/direct-carrier-connections/tracking/\">Tracking</a> page about reading it applies here too.</p>\n<h2 style=\"position:relative;\"><a href=\"#how-it-works\" aria-label=\"how it works permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"how-it-works\"></div>How it works</h2>\n<div class=\"mermaid\" data-processed=\"true\"><svg id=\"mermaid-1790596929486\" width=\"100%\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" style=\"max-width: 1098px;\" viewBox=\"-50 -10 1098 957\" role=\"graphics-document document\" aria-roledescription=\"sequence\"><rect x=\"40\" y=\"671\" fill=\"rgba(245, 158, 11, 0.12)\" width=\"533\" height=\"180\" class=\"rect\"></rect><rect x=\"40\" y=\"427\" fill=\"rgba(16, 185, 129, 0.10)\" width=\"918\" height=\"234\" class=\"rect\"></rect><rect x=\"40\" y=\"75\" fill=\"rgba(59, 130, 246, 0.10)\" width=\"533\" height=\"342\" class=\"rect\"></rect><g><rect x=\"848\" y=\"871\" fill=\"#eaeaea\" stroke=\"#666\" width=\"150\" height=\"65\" name=\"Carrier\" rx=\"3\" ry=\"3\" class=\"actor actor-bottom\"></rect><text x=\"923\" y=\"903.5\" dominant-baseline=\"central\" alignment-baseline=\"central\" class=\"actor actor-box\" style=\"text-anchor: middle; font-size: 16px; font-weight: 400;\"><tspan x=\"923\" dy=\"0\">Carrier</tspan></text></g><g><rect x=\"463\" y=\"871\" fill=\"#eaeaea\" stroke=\"#666\" width=\"150\" height=\"65\" name=\"Shipwell\" rx=\"3\" ry=\"3\" class=\"actor actor-bottom\"></rect><text x=\"538\" y=\"903.5\" dominant-baseline=\"central\" alignment-baseline=\"central\" class=\"actor actor-box\" style=\"text-anchor: middle; font-size: 16px; font-weight: 400;\"><tspan x=\"538\" dy=\"0\">Shipwell</tspan></text></g><g><rect x=\"0\" y=\"871\" fill=\"#eaeaea\" stroke=\"#666\" width=\"150\" height=\"65\" name=\"You\" rx=\"3\" ry=\"3\" class=\"actor actor-bottom\"></rect><text x=\"75\" y=\"903.5\" dominant-baseline=\"central\" alignment-baseline=\"central\" class=\"actor actor-box\" style=\"text-anchor: middle; font-size: 16px; font-weight: 400;\"><tspan x=\"75\" dy=\"0\">Your system</tspan></text></g><g><line id=\"actor2\" x1=\"923\" y1=\"65\" x2=\"923\" y2=\"871\" class=\"actor-line 200\" stroke-width=\"0.5px\" stroke=\"#999\" name=\"Carrier\"></line><g id=\"root-2\"><rect x=\"848\" y=\"0\" fill=\"#eaeaea\" stroke=\"#666\" width=\"150\" height=\"65\" name=\"Carrier\" rx=\"3\" ry=\"3\" class=\"actor actor-top\"></rect><text x=\"923\" y=\"32.5\" dominant-baseline=\"central\" alignment-baseline=\"central\" class=\"actor actor-box\" style=\"text-anchor: middle; font-size: 16px; font-weight: 400;\"><tspan x=\"923\" dy=\"0\">Carrier</tspan></text></g></g><g><line id=\"actor1\" x1=\"538\" y1=\"65\" x2=\"538\" y2=\"871\" class=\"actor-line 200\" stroke-width=\"0.5px\" stroke=\"#999\" name=\"Shipwell\"></line><g id=\"root-1\"><rect x=\"463\" y=\"0\" fill=\"#eaeaea\" stroke=\"#666\" width=\"150\" height=\"65\" name=\"Shipwell\" rx=\"3\" ry=\"3\" class=\"actor actor-top\"></rect><text x=\"538\" y=\"32.5\" dominant-baseline=\"central\" alignment-baseline=\"central\" class=\"actor actor-box\" style=\"text-anchor: middle; font-size: 16px; font-weight: 400;\"><tspan x=\"538\" dy=\"0\">Shipwell</tspan></text></g></g><g><line id=\"actor0\" x1=\"75\" y1=\"65\" x2=\"75\" y2=\"871\" class=\"actor-line 200\" stroke-width=\"0.5px\" stroke=\"#999\" name=\"You\"></line><g id=\"root-0\"><rect x=\"0\" y=\"0\" fill=\"#eaeaea\" stroke=\"#666\" width=\"150\" height=\"65\" name=\"You\" rx=\"3\" ry=\"3\" class=\"actor actor-top\"></rect><text x=\"75\" y=\"32.5\" dominant-baseline=\"central\" alignment-baseline=\"central\" class=\"actor actor-box\" style=\"text-anchor: middle; font-size: 16px; font-weight: 400;\"><tspan x=\"75\" dy=\"0\">Your system</tspan></text></g></g><style>#mermaid-1790596929486{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-1790596929486 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-1790596929486 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-1790596929486 .error-icon{fill:#552222;}#mermaid-1790596929486 .error-text{fill:#552222;stroke:#552222;}#mermaid-1790596929486 .edge-thickness-normal{stroke-width:1px;}#mermaid-1790596929486 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-1790596929486 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-1790596929486 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-1790596929486 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-1790596929486 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-1790596929486 .marker{fill:#333333;stroke:#333333;}#mermaid-1790596929486 .marker.cross{stroke:#333333;}#mermaid-1790596929486 svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-1790596929486 p{margin:0;}#mermaid-1790596929486 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-1790596929486 text.actor&gt;tspan{fill:black;stroke:none;}#mermaid-1790596929486 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-1790596929486 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-1790596929486 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-1790596929486 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-1790596929486 #arrowhead path{fill:#333;stroke:#333;}#mermaid-1790596929486 .sequenceNumber{fill:white;}#mermaid-1790596929486 #sequencenumber{fill:#333;}#mermaid-1790596929486 #crosshead path{fill:#333;stroke:#333;}#mermaid-1790596929486 .messageText{fill:#333;stroke:none;}#mermaid-1790596929486 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-1790596929486 .labelText,#mermaid-1790596929486 .labelText&gt;tspan{fill:black;stroke:none;}#mermaid-1790596929486 .loopText,#mermaid-1790596929486 .loopText&gt;tspan{fill:black;stroke:none;}#mermaid-1790596929486 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-1790596929486 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-1790596929486 .noteText,#mermaid-1790596929486 .noteText&gt;tspan{fill:black;stroke:none;}#mermaid-1790596929486 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-1790596929486 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-1790596929486 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-1790596929486 .actorPopupMenu{position:absolute;}#mermaid-1790596929486 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-1790596929486 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-1790596929486 .actor-man circle,#mermaid-1790596929486 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-1790596929486 :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}</style><g></g><defs><symbol id=\"computer\" width=\"24\" height=\"24\"><path transform=\"scale(.5)\" d=\"M2 2v13h20v-13h-20zm18 11h-16v-9h16v9zm-10.228 6l.466-1h3.524l.467 1h-4.457zm14.228 3h-24l2-6h2.104l-1.33 4h18.45l-1.297-4h2.073l2 6zm-5-10h-14v-7h14v7z\"></path></symbol></defs><defs><symbol id=\"database\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"><path transform=\"scale(.5)\" d=\"M12.258.001l.256.004.255.005.253.008.251.01.249.012.247.015.246.016.242.019.241.02.239.023.236.024.233.027.231.028.229.031.225.032.223.034.22.036.217.038.214.04.211.041.208.043.205.045.201.046.198.048.194.05.191.051.187.053.183.054.18.056.175.057.172.059.168.06.163.061.16.063.155.064.15.066.074.033.073.033.071.034.07.034.069.035.068.035.067.035.066.035.064.036.064.036.062.036.06.036.06.037.058.037.058.037.055.038.055.038.053.038.052.038.051.039.05.039.048.039.047.039.045.04.044.04.043.04.041.04.04.041.039.041.037.041.036.041.034.041.033.042.032.042.03.042.029.042.027.042.026.043.024.043.023.043.021.043.02.043.018.044.017.043.015.044.013.044.012.044.011.045.009.044.007.045.006.045.004.045.002.045.001.045v17l-.001.045-.002.045-.004.045-.006.045-.007.045-.009.044-.011.045-.012.044-.013.044-.015.044-.017.043-.018.044-.02.043-.021.043-.023.043-.024.043-.026.043-.027.042-.029.042-.03.042-.032.042-.033.042-.034.041-.036.041-.037.041-.039.041-.04.041-.041.04-.043.04-.044.04-.045.04-.047.039-.048.039-.05.039-.051.039-.052.038-.053.038-.055.038-.055.038-.058.037-.058.037-.06.037-.06.036-.062.036-.064.036-.064.036-.066.035-.067.035-.068.035-.069.035-.07.034-.071.034-.073.033-.074.033-.15.066-.155.064-.16.063-.163.061-.168.06-.172.059-.175.057-.18.056-.183.054-.187.053-.191.051-.194.05-.198.048-.201.046-.205.045-.208.043-.211.041-.214.04-.217.038-.22.036-.223.034-.225.032-.229.031-.231.028-.233.027-.236.024-.239.023-.241.02-.242.019-.246.016-.247.015-.249.012-.251.01-.253.008-.255.005-.256.004-.258.001-.258-.001-.256-.004-.255-.005-.253-.008-.251-.01-.249-.012-.247-.015-.245-.016-.243-.019-.241-.02-.238-.023-.236-.024-.234-.027-.231-.028-.228-.031-.226-.032-.223-.034-.22-.036-.217-.038-.214-.04-.211-.041-.208-.043-.204-.045-.201-.046-.198-.048-.195-.05-.19-.051-.187-.053-.184-.054-.179-.056-.176-.057-.172-.059-.167-.06-.164-.061-.159-.063-.155-.064-.151-.066-.074-.033-.072-.033-.072-.034-.07-.034-.069-.035-.068-.035-.067-.035-.066-.035-.064-.036-.063-.036-.062-.036-.061-.036-.06-.037-.058-.037-.057-.037-.056-.038-.055-.038-.053-.038-.052-.038-.051-.039-.049-.039-.049-.039-.046-.039-.046-.04-.044-.04-.043-.04-.041-.04-.04-.041-.039-.041-.037-.041-.036-.041-.034-.041-.033-.042-.032-.042-.03-.042-.029-.042-.027-.042-.026-.043-.024-.043-.023-.043-.021-.043-.02-.043-.018-.044-.017-.043-.015-.044-.013-.044-.012-.044-.011-.045-.009-.044-.007-.045-.006-.045-.004-.045-.002-.045-.001-.045v-17l.001-.045.002-.045.004-.045.006-.045.007-.045.009-.044.011-.045.012-.044.013-.044.015-.044.017-.043.018-.044.02-.043.021-.043.023-.043.024-.043.026-.043.027-.042.029-.042.03-.042.032-.042.033-.042.034-.041.036-.041.037-.041.039-.041.04-.041.041-.04.043-.04.044-.04.046-.04.046-.039.049-.039.049-.039.051-.039.052-.038.053-.038.055-.038.056-.038.057-.037.058-.037.06-.037.061-.036.062-.036.063-.036.064-.036.066-.035.067-.035.068-.035.069-.035.07-.034.072-.034.072-.033.074-.033.151-.066.155-.064.159-.063.164-.061.167-.06.172-.059.176-.057.179-.056.184-.054.187-.053.19-.051.195-.05.198-.048.201-.046.204-.045.208-.043.211-.041.214-.04.217-.038.22-.036.223-.034.226-.032.228-.031.231-.028.234-.027.236-.024.238-.023.241-.02.243-.019.245-.016.247-.015.249-.012.251-.01.253-.008.255-.005.256-.004.258-.001.258.001zm-9.258 20.499v.01l.001.021.003.021.004.022.005.021.006.022.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.023.018.024.019.024.021.024.022.025.023.024.024.025.052.049.056.05.061.051.066.051.07.051.075.051.079.052.084.052.088.052.092.052.097.052.102.051.105.052.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.048.144.049.147.047.152.047.155.047.16.045.163.045.167.043.171.043.176.041.178.041.183.039.187.039.19.037.194.035.197.035.202.033.204.031.209.03.212.029.216.027.219.025.222.024.226.021.23.02.233.018.236.016.24.015.243.012.246.01.249.008.253.005.256.004.259.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.021.224-.024.22-.026.216-.027.212-.028.21-.031.205-.031.202-.034.198-.034.194-.036.191-.037.187-.039.183-.04.179-.04.175-.042.172-.043.168-.044.163-.045.16-.046.155-.046.152-.047.148-.048.143-.049.139-.049.136-.05.131-.05.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.053.083-.051.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.05.023-.024.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.023.01-.022.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.127l-.077.055-.08.053-.083.054-.085.053-.087.052-.09.052-.093.051-.095.05-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.045-.118.044-.12.043-.122.042-.124.042-.126.041-.128.04-.13.04-.132.038-.134.038-.135.037-.138.037-.139.035-.142.035-.143.034-.144.033-.147.032-.148.031-.15.03-.151.03-.153.029-.154.027-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.01-.179.008-.179.008-.181.006-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.006-.179-.008-.179-.008-.178-.01-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.027-.153-.029-.151-.03-.15-.03-.148-.031-.146-.032-.145-.033-.143-.034-.141-.035-.14-.035-.137-.037-.136-.037-.134-.038-.132-.038-.13-.04-.128-.04-.126-.041-.124-.042-.122-.042-.12-.044-.117-.043-.116-.045-.113-.045-.112-.046-.109-.047-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.05-.093-.052-.09-.051-.087-.052-.085-.053-.083-.054-.08-.054-.077-.054v4.127zm0-5.654v.011l.001.021.003.021.004.021.005.022.006.022.007.022.009.022.01.022.011.023.012.023.013.023.015.024.016.023.017.024.018.024.019.024.021.024.022.024.023.025.024.024.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.052.11.051.114.051.119.052.123.05.127.051.131.05.135.049.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.044.171.042.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.022.23.02.233.018.236.016.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.012.241-.015.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.048.139-.05.136-.049.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.051.051-.049.023-.025.023-.024.021-.025.02-.024.019-.024.018-.024.017-.024.015-.023.014-.023.013-.024.012-.022.01-.023.01-.023.008-.022.006-.022.006-.022.004-.021.004-.022.001-.021.001-.021v-4.139l-.077.054-.08.054-.083.054-.085.052-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.044-.118.044-.12.044-.122.042-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.035-.143.033-.144.033-.147.033-.148.031-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.009-.179.009-.179.007-.181.007-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.007-.179-.007-.179-.009-.178-.009-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.031-.146-.033-.145-.033-.143-.033-.141-.035-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.04-.126-.041-.124-.042-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.051-.093-.051-.09-.051-.087-.053-.085-.052-.083-.054-.08-.054-.077-.054v4.139zm0-5.666v.011l.001.02.003.022.004.021.005.022.006.021.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.024.018.023.019.024.021.025.022.024.023.024.024.025.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.051.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.043.171.043.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.021.23.02.233.018.236.017.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.013.241-.014.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.049.139-.049.136-.049.131-.051.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.049.023-.025.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.022.01-.023.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.153l-.077.054-.08.054-.083.053-.085.053-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.048-.105.048-.106.048-.109.046-.111.046-.114.046-.115.044-.118.044-.12.043-.122.043-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.034-.143.034-.144.033-.147.032-.148.032-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.024-.161.024-.162.023-.163.023-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.01-.178.01-.179.009-.179.007-.181.006-.182.006-.182.004-.184.003-.184.001-.185.001-.185-.001-.184-.001-.184-.003-.182-.004-.182-.006-.181-.006-.179-.007-.179-.009-.178-.01-.176-.01-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.023-.162-.023-.161-.024-.159-.024-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.032-.146-.032-.145-.033-.143-.034-.141-.034-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.041-.126-.041-.124-.041-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.048-.105-.048-.102-.048-.1-.05-.097-.049-.095-.051-.093-.051-.09-.052-.087-.052-.085-.053-.083-.053-.08-.054-.077-.054v4.153zm8.74-8.179l-.257.004-.254.005-.25.008-.247.011-.244.012-.241.014-.237.016-.233.018-.231.021-.226.022-.224.023-.22.026-.216.027-.212.028-.21.031-.205.032-.202.033-.198.034-.194.036-.191.038-.187.038-.183.04-.179.041-.175.042-.172.043-.168.043-.163.045-.16.046-.155.046-.152.048-.148.048-.143.048-.139.049-.136.05-.131.05-.126.051-.123.051-.118.051-.114.052-.11.052-.106.052-.101.052-.096.052-.092.052-.088.052-.083.052-.079.052-.074.051-.07.052-.065.051-.06.05-.056.05-.051.05-.023.025-.023.024-.021.024-.02.025-.019.024-.018.024-.017.023-.015.024-.014.023-.013.023-.012.023-.01.023-.01.022-.008.022-.006.023-.006.021-.004.022-.004.021-.001.021-.001.021.001.021.001.021.004.021.004.022.006.021.006.023.008.022.01.022.01.023.012.023.013.023.014.023.015.024.017.023.018.024.019.024.02.025.021.024.023.024.023.025.051.05.056.05.06.05.065.051.07.052.074.051.079.052.083.052.088.052.092.052.096.052.101.052.106.052.11.052.114.052.118.051.123.051.126.051.131.05.136.05.139.049.143.048.148.048.152.048.155.046.16.046.163.045.168.043.172.043.175.042.179.041.183.04.187.038.191.038.194.036.198.034.202.033.205.032.21.031.212.028.216.027.22.026.224.023.226.022.231.021.233.018.237.016.241.014.244.012.247.011.25.008.254.005.257.004.26.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.022.224-.023.22-.026.216-.027.212-.028.21-.031.205-.032.202-.033.198-.034.194-.036.191-.038.187-.038.183-.04.179-.041.175-.042.172-.043.168-.043.163-.045.16-.046.155-.046.152-.048.148-.048.143-.048.139-.049.136-.05.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.05.051-.05.023-.025.023-.024.021-.024.02-.025.019-.024.018-.024.017-.023.015-.024.014-.023.013-.023.012-.023.01-.023.01-.022.008-.022.006-.023.006-.021.004-.022.004-.021.001-.021.001-.021-.001-.021-.001-.021-.004-.021-.004-.022-.006-.021-.006-.023-.008-.022-.01-.022-.01-.023-.012-.023-.013-.023-.014-.023-.015-.024-.017-.023-.018-.024-.019-.024-.02-.025-.021-.024-.023-.024-.023-.025-.051-.05-.056-.05-.06-.05-.065-.051-.07-.052-.074-.051-.079-.052-.083-.052-.088-.052-.092-.052-.096-.052-.101-.052-.106-.052-.11-.052-.114-.052-.118-.051-.123-.051-.126-.051-.131-.05-.136-.05-.139-.049-.143-.048-.148-.048-.152-.048-.155-.046-.16-.046-.163-.045-.168-.043-.172-.043-.175-.042-.179-.041-.183-.04-.187-.038-.191-.038-.194-.036-.198-.034-.202-.033-.205-.032-.21-.031-.212-.028-.216-.027-.22-.026-.224-.023-.226-.022-.231-.021-.233-.018-.237-.016-.241-.014-.244-.012-.247-.011-.25-.008-.254-.005-.257-.004-.26-.001-.26.001z\"></path></symbol></defs><defs><symbol id=\"clock\" width=\"24\" height=\"24\"><path transform=\"scale(.5)\" d=\"M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5.848 12.459c.202.038.202.333.001.372-1.907.361-6.045 1.111-6.547 1.111-.719 0-1.301-.582-1.301-1.301 0-.512.77-5.447 1.125-7.445.034-.192.312-.181.343.014l.985 6.238 5.394 1.011z\"></path></symbol></defs><defs><marker id=\"arrowhead\" refX=\"7.9\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"12\" markerHeight=\"12\" orient=\"auto-start-reverse\"><path d=\"M -1 0 L 10 5 L 0 10 z\"></path></marker></defs><defs><marker id=\"crosshead\" markerWidth=\"15\" markerHeight=\"8\" orient=\"auto\" refX=\"4\" refY=\"4.5\"><path fill=\"none\" stroke=\"#000000\" stroke-width=\"1pt\" d=\"M 1,2 L 6,7 M 6,2 L 1,7\" style=\"stroke-dasharray: 0, 0;\"></path></marker></defs><defs><marker id=\"filled-head\" refX=\"15.5\" refY=\"7\" markerWidth=\"20\" markerHeight=\"28\" orient=\"auto\"><path d=\"M 18,7 L9,13 L14,7 L9,1 Z\"></path></marker></defs><defs><marker id=\"sequencenumber\" refX=\"15\" refY=\"15\" markerWidth=\"60\" markerHeight=\"40\" orient=\"auto\"><circle cx=\"15\" cy=\"15\" r=\"6\"></circle></marker></defs><g><rect x=\"50\" y=\"95\" fill=\"#EDF2AE\" stroke=\"#666\" width=\"513\" height=\"42\" class=\"note\"></rect><text x=\"307\" y=\"100\" text-anchor=\"middle\" dominant-baseline=\"middle\" alignment-baseline=\"middle\" class=\"noteText\" dy=\"1em\" style=\"font-size: 16px; font-weight: 400;\"><tspan x=\"307\">Once, at setup</tspan></text></g><g><rect x=\"50\" y=\"447\" fill=\"#EDF2AE\" stroke=\"#666\" width=\"898\" height=\"42\" class=\"note\"></rect><text x=\"499\" y=\"452\" text-anchor=\"middle\" dominant-baseline=\"middle\" alignment-baseline=\"middle\" class=\"noteText\" dy=\"1em\" style=\"font-size: 16px; font-weight: 400;\"><tspan x=\"499\">Every time tracking changes, with no calls from you</tspan></text></g><g><rect x=\"50\" y=\"691\" fill=\"#EDF2AE\" stroke=\"#666\" width=\"513\" height=\"42\" class=\"note\"></rect><text x=\"307\" y=\"696\" text-anchor=\"middle\" dominant-baseline=\"middle\" alignment-baseline=\"middle\" class=\"noteText\" dy=\"1em\" style=\"font-size: 16px; font-weight: 400;\"><tspan x=\"307\">Only when you need it</tspan></text></g><text x=\"305\" y=\"152\" text-anchor=\"middle\" dominant-baseline=\"middle\" alignment-baseline=\"middle\" class=\"messageText\" dy=\"1em\" style=\"font-size: 16px; font-weight: 400;\">POST /tracking/webhooks { url, error_contact_email }</text><line x1=\"76\" y1=\"191\" x2=\"534\" y2=\"191\" class=\"messageLine0\" stroke-width=\"2\" stroke=\"none\" marker-end=\"url(#arrowhead)\" style=\"fill: none;\"></line><line x1=\"76\" y1=\"191\" x2=\"76\" y2=\"191\" stroke-width=\"0\" marker-start=\"url(#sequencenumber)\"></line><text x=\"76\" y=\"195\" font-family=\"sans-serif\" font-size=\"12px\" text-anchor=\"middle\" class=\"sequenceNumber\">1</text><text x=\"308\" y=\"206\" text-anchor=\"middle\" dominant-baseline=\"middle\" alignment-baseline=\"middle\" class=\"messageText\" dy=\"1em\" style=\"font-size: 16px; font-weight: 400;\">201 webhook id + signing secret (shown once)</text><line x1=\"537\" y1=\"245\" x2=\"79\" y2=\"245\" class=\"messageLine1\" stroke-width=\"2\" stroke=\"none\" marker-end=\"url(#arrowhead)\" style=\"stroke-dasharray: 3, 3; fill: none;\"></line><line x1=\"537\" y1=\"245\" x2=\"537\" y2=\"245\" stroke-width=\"0\" marker-start=\"url(#sequencenumber)\"></line><text x=\"537\" y=\"249\" font-family=\"sans-serif\" font-size=\"12px\" text-anchor=\"middle\" class=\"sequenceNumber\">2</text><text x=\"305\" y=\"260\" text-anchor=\"middle\" dominant-baseline=\"middle\" alignment-baseline=\"middle\" class=\"messageText\" dy=\"1em\" style=\"font-size: 16px; font-weight: 400;\">POST /tracking/webhooks/{webhook_id}/test</text><line x1=\"76\" y1=\"299\" x2=\"534\" y2=\"299\" class=\"messageLine0\" stroke-width=\"2\" stroke=\"none\" marker-end=\"url(#arrowhead)\" style=\"fill: none;\"></line><line x1=\"76\" y1=\"299\" x2=\"76\" y2=\"299\" stroke-width=\"0\" marker-start=\"url(#sequencenumber)\"></line><text x=\"76\" y=\"303\" font-family=\"sans-serif\" font-size=\"12px\" text-anchor=\"middle\" class=\"sequenceNumber\">3</text><text x=\"308\" y=\"314\" text-anchor=\"middle\" dominant-baseline=\"middle\" alignment-baseline=\"middle\" class=\"messageText\" dy=\"1em\" style=\"font-size: 16px; font-weight: 400;\">tracking.webhook.test (signed)</text><line x1=\"537\" y1=\"353\" x2=\"79\" y2=\"353\" class=\"messageLine0\" stroke-width=\"2\" stroke=\"none\" marker-end=\"url(#filled-head)\" style=\"fill: none;\"></line><line x1=\"537\" y1=\"353\" x2=\"537\" y2=\"353\" stroke-width=\"0\" marker-start=\"url(#sequencenumber)\"></line><text x=\"537\" y=\"357\" font-family=\"sans-serif\" font-size=\"12px\" text-anchor=\"middle\" class=\"sequenceNumber\">4</text><text x=\"308\" y=\"368\" text-anchor=\"middle\" dominant-baseline=\"middle\" alignment-baseline=\"middle\" class=\"messageText\" dy=\"1em\" style=\"font-size: 16px; font-weight: 400;\">200 { delivered, response_code, latency_ms }</text><line x1=\"537\" y1=\"407\" x2=\"79\" y2=\"407\" class=\"messageLine1\" stroke-width=\"2\" stroke=\"none\" marker-end=\"url(#arrowhead)\" style=\"stroke-dasharray: 3, 3; fill: none;\"></line><line x1=\"537\" y1=\"407\" x2=\"537\" y2=\"407\" stroke-width=\"0\" marker-start=\"url(#sequencenumber)\"></line><text x=\"537\" y=\"411\" font-family=\"sans-serif\" font-size=\"12px\" text-anchor=\"middle\" class=\"sequenceNumber\">5</text><text x=\"732\" y=\"504\" text-anchor=\"middle\" dominant-baseline=\"middle\" alignment-baseline=\"middle\" class=\"messageText\" dy=\"1em\" style=\"font-size: 16px; font-weight: 400;\">New movement (Shipwell polls the carrier)</text><line x1=\"922\" y1=\"543\" x2=\"542\" y2=\"543\" class=\"messageLine1\" stroke-width=\"2\" stroke=\"none\" marker-end=\"url(#arrowhead)\" style=\"stroke-dasharray: 3, 3; fill: none;\"></line><line x1=\"922\" y1=\"543\" x2=\"922\" y2=\"543\" stroke-width=\"0\" marker-start=\"url(#sequencenumber)\"></line><text x=\"922\" y=\"547\" font-family=\"sans-serif\" font-size=\"12px\" text-anchor=\"middle\" class=\"sequenceNumber\">6</text><text x=\"308\" y=\"558\" text-anchor=\"middle\" dominant-baseline=\"middle\" alignment-baseline=\"middle\" class=\"messageText\" dy=\"1em\" style=\"font-size: 16px; font-weight: 400;\">tracking.updated, full snapshot (signed)</text><line x1=\"537\" y1=\"597\" x2=\"79\" y2=\"597\" class=\"messageLine0\" stroke-width=\"2\" stroke=\"none\" marker-end=\"url(#filled-head)\" style=\"fill: none;\"></line><line x1=\"537\" y1=\"597\" x2=\"537\" y2=\"597\" stroke-width=\"0\" marker-start=\"url(#sequencenumber)\"></line><text x=\"537\" y=\"601\" font-family=\"sans-serif\" font-size=\"12px\" text-anchor=\"middle\" class=\"sequenceNumber\">7</text><text x=\"305\" y=\"612\" text-anchor=\"middle\" dominant-baseline=\"middle\" alignment-baseline=\"middle\" class=\"messageText\" dy=\"1em\" style=\"font-size: 16px; font-weight: 400;\">2xx within 10 seconds</text><line x1=\"76\" y1=\"651\" x2=\"534\" y2=\"651\" class=\"messageLine1\" stroke-width=\"2\" stroke=\"none\" marker-end=\"url(#arrowhead)\" style=\"stroke-dasharray: 3, 3; fill: none;\"></line><line x1=\"76\" y1=\"651\" x2=\"76\" y2=\"651\" stroke-width=\"0\" marker-start=\"url(#sequencenumber)\"></line><text x=\"76\" y=\"655\" font-family=\"sans-serif\" font-size=\"12px\" text-anchor=\"middle\" class=\"sequenceNumber\">8</text><text x=\"305\" y=\"748\" text-anchor=\"middle\" dominant-baseline=\"middle\" alignment-baseline=\"middle\" class=\"messageText\" dy=\"1em\" style=\"font-size: 16px; font-weight: 400;\">GET /tracking/timelines/{resource_id}</text><line x1=\"76\" y1=\"787\" x2=\"534\" y2=\"787\" class=\"messageLine0\" stroke-width=\"2\" stroke=\"none\" marker-end=\"url(#arrowhead)\" style=\"fill: none;\"></line><line x1=\"76\" y1=\"787\" x2=\"76\" y2=\"787\" stroke-width=\"0\" marker-start=\"url(#sequencenumber)\"></line><text x=\"76\" y=\"791\" font-family=\"sans-serif\" font-size=\"12px\" text-anchor=\"middle\" class=\"sequenceNumber\">9</text><text x=\"308\" y=\"802\" text-anchor=\"middle\" dominant-baseline=\"middle\" alignment-baseline=\"middle\" class=\"messageText\" dy=\"1em\" style=\"font-size: 16px; font-weight: 400;\">The same snapshot, on demand</text><line x1=\"537\" y1=\"841\" x2=\"79\" y2=\"841\" class=\"messageLine1\" stroke-width=\"2\" stroke=\"none\" marker-end=\"url(#arrowhead)\" style=\"stroke-dasharray: 3, 3; fill: none;\"></line><line x1=\"537\" y1=\"841\" x2=\"537\" y2=\"841\" stroke-width=\"0\" marker-start=\"url(#sequencenumber)\"></line><text x=\"537\" y=\"845\" font-family=\"sans-serif\" font-size=\"12px\" text-anchor=\"middle\" class=\"sequenceNumber\">10</text></svg></div>\n<ol>\n<li>\n<strong>Register your URL, once.</strong>\n Shipwell returns a signing \n<code class=\"language-text\">secret</code>\n. Store it: it is shown only this once.\n</li>\n<li>\n<strong>Send a test, once.</strong>\n Shipwell sends a signed test event to your URL right away and tells you what your endpoint answered. A \n<code class=\"language-text\">2xx</code>\n proves Shipwell reached your endpoint through your firewall and TLS. Make your handler reject a bad signature, so a \n<code class=\"language-text\">2xx</code>\n also means your signature check passed.\n</li>\n<li>\n<strong>Receive updates.</strong>\n Book freight as usual. Each time its tracking changes, a signed \n<code class=\"language-text\">tracking.updated</code>\n event arrives at your URL, usually within a minute or two. You make no calls.\n</li>\n<li>\n<strong>Pull when you need to.</strong>\n After downtime, or for a periodic reconcile, read the same tracking with \n<code class=\"language-text\">GET /tracking/timelines/{resource_id}</code>\n. See \n<a href=\"#mixing-pull-and-push\">Mixing pull and push</a>\n.\n</li>\n</ol>\n<h2 style=\"position:relative;\"><a href=\"#before-you-start\" aria-label=\"before you start permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"before-you-start\"></div>Before you start</h2>\n<ul>\n<li>\n<strong>Tracking webhooks must be enabled for your company.</strong>\n Until they are, both endpoints answer \n<code class=\"language-text\">403</code>\n with \n<code class=\"language-text\">webhooks_not_enabled</code>\n. Contact your Shipwell representative to turn them on.\n</li>\n<li>\n<strong>Your endpoint must be public HTTPS.</strong>\n Shipwell delivers only to \n<code class=\"language-text\">https://</code>\n URLs that resolve to a public internet address. Private, loopback and cloud metadata addresses are refused, when you register and again before every delivery.\n</li>\n<li>\n<strong>Answer fast.</strong>\n Return any \n<code class=\"language-text\">2xx</code>\n within 10 seconds. If processing takes longer, store the delivery durably, acknowledge, and process from a queue.\n</li>\n<li>\n<strong>One webhook per company.</strong>\n To send updates somewhere else, change the webhook's URL rather than adding a second one.\n</li>\n</ul>\n<h2 style=\"position:relative;\"><a href=\"#two-ways-to-set-it-up\" aria-label=\"two ways to set it up permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"two-ways-to-set-it-up\"></div>Two ways to set it up</h2>\n<p>You can manage your tracking webhook in the Shipwell app, with no code, or from your own system through the REST API. Use whichever suits your team; both work on the same webhook.</p>\n<table>\n<thead>\n<tr>\n<th></th>\n<th>In the Shipwell app</th>\n<th>Through the REST API</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Register your URL</td>\n<td>Yes</td>\n<td>Yes, <code class=\"language-text\">POST /tracking/webhooks</code></td>\n</tr>\n<tr>\n<td>Get the signing secret, once</td>\n<td>Yes</td>\n<td>Yes, in the create response</td>\n</tr>\n<tr>\n<td>Send a test delivery</td>\n<td>Yes</td>\n<td>Yes, <code class=\"language-text\">POST /tracking/webhooks/{webhook_id}/test</code></td>\n</tr>\n<tr>\n<td>Edit, pause, resume or delete</td>\n<td>Yes</td>\n<td>Not available yet; use the app</td>\n</tr>\n<tr>\n<td>Delivery history and retry</td>\n<td>Yes</td>\n<td>Not available yet; use the app</td>\n</tr>\n</tbody>\n</table>\n<p>Start with <a href=\"#set-it-up-in-the-shipwell-app\">Set it up in the Shipwell app</a> or <a href=\"#step-1-register-your-url\">Step 1: Register your URL</a> for the API. Receiving and verifying deliveries is the same either way: see <a href=\"#step-3-receive-trackingupdated\">Step 3</a> and <a href=\"#verify-every-delivery\">Verify every delivery</a>.</p>\n<h2 style=\"position:relative;\"><a href=\"#set-it-up-in-the-shipwell-app\" aria-label=\"set it up in the shipwell app permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"set-it-up-in-the-shipwell-app\"></div>Set it up in the Shipwell app</h2>\n<p>Open <strong>Company → Webhooks</strong>. If you sign in with the API integration user, it sits next to <strong>API Keys</strong>. Company administrators find it under <strong>Developer Tools → Carrier Connection Webhooks</strong>.</p>\n<ol>\n<li>\nIn the \n<strong>Tracking</strong>\n section, choose \n<strong>Set up tracking webhook</strong>\n, enter your HTTPS URL and an error contact email, choose the \n<strong>Events</strong>\n to receive (\n<strong>Tracking updates</strong>\n, and \n<strong>Document updates</strong>\n if \n<a href=\"/docs/direct-carrier-connections/documents-webhooks/\">documents webhooks</a>\n are enabled for your company), optionally add custom data, then choose \n<strong>Create webhook</strong>\n.\n</li>\n<li>\nCopy the signing secret. It is shown only this once, and \n<strong>Done</strong>\n stays disabled until you confirm you've stored it.\n<p><img src=\"/35e16e8ebd0d6230f49e00854f42f284/tracking-webhooks-secret.png\" alt=\"The signing secret dialog: the secret with a Copy button, a warning that it is shown only once, and a Done button that stays disabled until you confirm you&#x27;ve stored it\"></p>\n</li>\n<li>\nChoose \n<strong>Send test delivery</strong>\n. Shipwell sends a signed test event to your URL and shows whether it was delivered, your endpoint's response code and how long it took.\n<p><img src=\"/b77be10d1a4435dcf55546dcf9527931/tracking-webhooks-section.png\" alt=\"The Tracking section of the Webhooks page: the webhook&#x27;s URL, events, error contact and custom data, with Edit, Pause, Delivery history and Delete, and a successful test delivery\"></p>\n</li>\n<li>\nFrom the same section you can \n<strong>Edit</strong>\n the URL, events, error contact or custom data, \n<strong>Pause</strong>\n and resume deliveries, or \n<strong>Delete</strong>\n the webhook.\n</li>\n<li>\n<strong>Delivery history</strong>\n lists the last 30 days of deliveries, newest first, with each one's event, status, attempts and your endpoint's last answer. Filter to \n<strong>Failed</strong>\n and choose \n<strong>Retry</strong>\n on a delivery, or \n<strong>Retry all failed</strong>\n after an outage on your side. A retried delivery goes out within about a minute with its original event \n<code class=\"language-text\">id</code>\n, so your duplicate check still works.\n<p><img src=\"/1779347fa255735d8399c9e52f1e24a9/tracking-webhooks-delivery-history.png\" alt=\"Delivery history: deliveries with their event, shipment, sequence, status, attempts, last answer and last attempt, filter buttons, Retry on failed rows and Retry all failed\"></p>\n</li>\n</ol>\n<h2 style=\"position:relative;\"><a href=\"#endpoints\" aria-label=\"endpoints permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"endpoints\"></div>Endpoints</h2>\n<table>\n<thead>\n<tr>\n<th>Endpoint</th>\n<th>What it does</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"https://docs.shipwell.com/openapi_pages/carrier-connections/operation/create_tracking_webhook/\"><code class=\"language-text\">POST /tracking/webhooks</code></a></td>\n<td>Register a URL. Returns the webhook <code class=\"language-text\">id</code> and its signing <code class=\"language-text\">secret</code>.</td>\n</tr>\n<tr>\n<td><a href=\"https://docs.shipwell.com/openapi_pages/carrier-connections/operation/test_tracking_webhook/\"><code class=\"language-text\">POST /tracking/webhooks/{webhook_id}/test</code></a></td>\n<td>Send one signed test event now and see what your endpoint answered.</td>\n</tr>\n</tbody>\n</table>\n<p>The events Shipwell sends, <code class=\"language-text\">tracking.updated</code>, <code class=\"language-text\">documents.updated</code> and <code class=\"language-text\">tracking.webhook.test</code>, are described under <strong>Webhooks</strong> in the <a href=\"https://docs.shipwell.com/openapi_pages/carrier-connections/tag/Tracking-Webhooks/\">API reference</a>. This page covers tracking; <a href=\"/docs/direct-carrier-connections/documents-webhooks/\">Documents Webhooks</a> covers documents on the same webhook.</p>\n<h2 style=\"position:relative;\"><a href=\"#step-1-register-your-url\" aria-label=\"step 1 register your url permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"step-1-register-your-url\"></div>Step 1: Register your URL</h2>\n<p>The steps from here use the REST API. To do the same in the app, see <a href=\"#set-it-up-in-the-shipwell-app\">Set it up in the Shipwell app</a>.</p>\n<p>A company has one tracking webhook. Registering a second answers <code class=\"language-text\">409</code> with <code class=\"language-text\">webhook_already_exists</code>.</p>\n<table>\n<thead>\n<tr>\n<th>Field</th>\n<th>Required</th>\n<th>Notes</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code class=\"language-text\">url</code></td>\n<td><strong>Yes</strong></td>\n<td>HTTPS only, resolving to a public address. Up to 2,048 characters, with no username or password in it.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">error_contact_email</code></td>\n<td><strong>Yes</strong></td>\n<td>Who Shipwell contacts if deliveries to your URL keep failing.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">custom_data</code></td>\n<td>No</td>\n<td>Any JSON object up to 4 KB. It comes back as <code class=\"language-text\">custom_data</code> in every delivery, which is handy for routing, such as your system's environment or tenant.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">event_names</code></td>\n<td>No</td>\n<td>Defaults to <code class=\"language-text\">[\"tracking.updated\"]</code>. Add <code class=\"language-text\">\"documents.updated\"</code> to receive document changes on the same URL too; see <a href=\"/docs/direct-carrier-connections/documents-webhooks/\">Documents Webhooks</a>.</td>\n</tr>\n</tbody>\n</table>\n<details open>\n  <summary>Example request</summary>\n<div class=\"code-wrapper\">\n        <div class=\"gatsby-code-button-container\"\n             data-toaster-id=\"99192935601693750000\"\n             data-toaster-duration=\"1500\"\n             onClick=\"copyCodeToClipboard(`curl --request POST 'https://sandbox-api.shipwell.com/tracking/webhooks' \\\\\n--header 'Authorization: Token <user-token>' \\\\\n--header 'Content-Type: application/json' \\\\\n--data-raw '{\n  &quot;url&quot;: &quot;https://tms.example.com/hooks/shipwell-tracking&quot;,\n  &quot;error_contact_email&quot;: &quot;integrations@example.com&quot;,\n  &quot;custom_data&quot;: { &quot;tms&quot;: &quot;acme-prod&quot; }\n}'`, `99192935601693750000`)\"\n        >\n          <div class=\"gatsby-code-button\" title=\"Copy the code snippet\">Copy</div>\n          <div class=\"done-indicator done-indicator-99192935601693750000\">Copied</div>\n        </div>\n        <div class=\"gatsby-highlight\" data-language=\"curl\"><pre class=\"language-curl\"><code class=\"language-curl\">curl --request POST &#39;https://sandbox-api.shipwell.com/tracking/webhooks&#39; \\\n--header &#39;Authorization: Token &lt;user-token&gt;&#39; \\\n--header &#39;Content-Type: application/json&#39; \\\n--data-raw &#39;{\n  &quot;url&quot;: &quot;https://tms.example.com/hooks/shipwell-tracking&quot;,\n  &quot;error_contact_email&quot;: &quot;integrations@example.com&quot;,\n  &quot;custom_data&quot;: { &quot;tms&quot;: &quot;acme-prod&quot; }\n}&#39;</code></pre></div>\n      </div>\n</details>\n<details open>\n  <summary>Example response (201)</summary>\n<div class=\"code-wrapper\">\n        <div class=\"gatsby-code-button-container\"\n             data-toaster-id=\"68903307465806400000\"\n             data-toaster-duration=\"1500\"\n             onClick=\"copyCodeToClipboard(`{\n  &quot;data&quot;: {\n    &quot;id&quot;: &quot;01KC2W7HQ3M8R5T6V9X0YZABCD&quot;,\n    &quot;resource_type&quot;: &quot;tracking_webhook&quot;,\n    &quot;self_link&quot;: &quot;https://sandbox-api.shipwell.com/tracking/webhooks/01KC2W7HQ3M8R5T6V9X0YZABCD&quot;,\n    &quot;url&quot;: &quot;https://tms.example.com/hooks/shipwell-tracking&quot;,\n    &quot;event_names&quot;: [&quot;tracking.updated&quot;],\n    &quot;status&quot;: &quot;ENABLED&quot;,\n    &quot;error_contact_email&quot;: &quot;integrations@example.com&quot;,\n    &quot;custom_data&quot;: { &quot;tms&quot;: &quot;acme-prod&quot; },\n    &quot;created_at&quot;: &quot;2026-08-19T15:12:08.102181Z&quot;,\n    &quot;updated_at&quot;: &quot;2026-08-19T15:12:08.102181Z&quot;,\n    &quot;secret&quot;: &quot;whsec_EXAMPLE_DO_NOT_USE&quot;\n  }\n}`, `68903307465806400000`)\"\n        >\n          <div class=\"gatsby-code-button\" title=\"Copy the code snippet\">Copy</div>\n          <div class=\"done-indicator done-indicator-68903307465806400000\">Copied</div>\n        </div>\n        <div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"data\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"01KC2W7HQ3M8R5T6V9X0YZABCD\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"resource_type\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"tracking_webhook\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"self_link\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"https://sandbox-api.shipwell.com/tracking/webhooks/01KC2W7HQ3M8R5T6V9X0YZABCD\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"url\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"https://tms.example.com/hooks/shipwell-tracking\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"event_names\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"tracking.updated\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"status\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"ENABLED\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"error_contact_email\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"integrations@example.com\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"custom_data\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token property\">\"tms\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"acme-prod\"</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"created_at\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"2026-08-19T15:12:08.102181Z\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"updated_at\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"2026-08-19T15:12:08.102181Z\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"secret\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"whsec_EXAMPLE_DO_NOT_USE\"</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n      </div>\n</details>\n<p><strong>Store the <code class=\"language-text\">secret</code> now.</strong> It is returned only in this response, and you need it to verify every delivery. Keep it where you keep other credentials, never in client-side code or logs.</p>\n<h2 style=\"position:relative;\"><a href=\"#step-2-send-a-test\" aria-label=\"step 2 send a test permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"step-2-send-a-test\"></div>Step 2: Send a test</h2>\n<p>Call this right after registering. Shipwell sends one signed <code class=\"language-text\">tracking.webhook.test</code> event to your URL and answers with what your endpoint did.</p>\n<details open>\n  <summary>Example request</summary>\n<div class=\"code-wrapper\">\n        <div class=\"gatsby-code-button-container\"\n             data-toaster-id=\"7362823156660147000\"\n             data-toaster-duration=\"1500\"\n             onClick=\"copyCodeToClipboard(`curl --request POST 'https://sandbox-api.shipwell.com/tracking/webhooks/01KC2W7HQ3M8R5T6V9X0YZABCD/test' \\\\\n--header 'Authorization: Token <user-token>'`, `7362823156660147000`)\"\n        >\n          <div class=\"gatsby-code-button\" title=\"Copy the code snippet\">Copy</div>\n          <div class=\"done-indicator done-indicator-7362823156660147000\">Copied</div>\n        </div>\n        <div class=\"gatsby-highlight\" data-language=\"curl\"><pre class=\"language-curl\"><code class=\"language-curl\">curl --request POST &#39;https://sandbox-api.shipwell.com/tracking/webhooks/01KC2W7HQ3M8R5T6V9X0YZABCD/test&#39; \\\n--header &#39;Authorization: Token &lt;user-token&gt;&#39;</code></pre></div>\n      </div>\n</details>\n<details open>\n  <summary>Example response (200)</summary>\n<div class=\"code-wrapper\">\n        <div class=\"gatsby-code-button-container\"\n             data-toaster-id=\"5158957072418957000\"\n             data-toaster-duration=\"1500\"\n             onClick=\"copyCodeToClipboard(`{\n  &quot;data&quot;: {\n    &quot;delivered&quot;: true,\n    &quot;response_code&quot;: 200,\n    &quot;error&quot;: null,\n    &quot;latency_ms&quot;: 412.6\n  }\n}`, `5158957072418957000`)\"\n        >\n          <div class=\"gatsby-code-button\" title=\"Copy the code snippet\">Copy</div>\n          <div class=\"done-indicator done-indicator-5158957072418957000\">Copied</div>\n        </div>\n        <div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"data\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"delivered\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"response_code\"</span><span class=\"token operator\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"error\"</span><span class=\"token operator\">:</span> <span class=\"token null keyword\">null</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"latency_ms\"</span><span class=\"token operator\">:</span> <span class=\"token number\">412.6</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n      </div>\n</details>\n<p>The response is <code class=\"language-text\">200</code> whether or not your endpoint accepted the test, so read <code class=\"language-text\">delivered</code>:</p>\n<table>\n<thead>\n<tr>\n<th><code class=\"language-text\">delivered</code></th>\n<th><code class=\"language-text\">response_code</code></th>\n<th>What to check</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code class=\"language-text\">true</code></td>\n<td><code class=\"language-text\">2xx</code></td>\n<td>Nothing. You're ready.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">false</code></td>\n<td><code class=\"language-text\">4xx</code> or <code class=\"language-text\">5xx</code></td>\n<td>Your endpoint answered but refused. Often a signature check failing, or a route or method mismatch: the test is a <code class=\"language-text\">POST</code>.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">false</code></td>\n<td><code class=\"language-text\">null</code></td>\n<td>Shipwell couldn't reach your endpoint. <code class=\"language-text\">error</code> says why: a timeout, a TLS problem, a name that doesn't resolve, or an address that isn't public.</td>\n</tr>\n</tbody>\n</table>\n<p>Shipwell can't see whether your endpoint verified the signature, only what it answered. Make your handler reject a bad signature with a <code class=\"language-text\">4xx</code>, so <code class=\"language-text\">delivered: true</code> also means your check passed.</p>\n<p>A test is sent once and never retried or stored. Its body looks like any delivery, with <code class=\"language-text\">event_name: \"tracking.webhook.test\"</code>, <code class=\"language-text\">sequence: 0</code> and a short message in <code class=\"language-text\">details</code>.</p>\n<h2 style=\"position:relative;\"><a href=\"#step-3-receive-trackingupdated\" aria-label=\"step 3 receive trackingupdated permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"step-3-receive-trackingupdated\"></div>Step 3: Receive <code class=\"language-text\">tracking.updated</code></h2>\n<p>Book freight with <a href=\"/docs/direct-carrier-connections/dispatch/\"><code class=\"language-text\">POST /dispatching/jobs</code></a> as usual. Shipwell sends <code class=\"language-text\">tracking.updated</code> whenever that shipment's tracking changes:</p>\n<ul>\n<li>\nwhen tracking starts after the booking,\n</li>\n<li>\nwhen the carrier reports a new movement,\n</li>\n<li>\nwhen tracking reaches its end, for example \n<code class=\"language-text\">DELIVERED</code>\n, cancelled or expired.\n</li>\n</ul>\n<p>If a check with the carrier changes nothing, nothing is sent. Several movements reported at once arrive as one event carrying all of them.</p>\n<h3 style=\"position:relative;\"><a href=\"#headers\" aria-label=\"headers permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"headers\"></div>Headers</h3>\n<table>\n<thead>\n<tr>\n<th>Header</th>\n<th>Value</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code class=\"language-text\">X-Shipwell-Signature</code></td>\n<td>Hex HMAC-SHA256 of <code class=\"language-text\">{X-Shipwell-Timestamp};{raw request body}</code>, keyed with your webhook's <code class=\"language-text\">secret</code>.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">X-Shipwell-Timestamp</code></td>\n<td>When this attempt was signed, as ISO 8601, for example <code class=\"language-text\">2026-08-23T17:03:00.064978+00:00</code>. A new value on every retry.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">Content-Type</code></td>\n<td><code class=\"language-text\">application/json; charset=utf-8</code></td>\n</tr>\n<tr>\n<td><code class=\"language-text\">User-Agent</code></td>\n<td><code class=\"language-text\">shipwell-webhooks</code></td>\n</tr>\n</tbody>\n</table>\n<details open>\n  <summary>Example tracking.updated (abridged to two movements)</summary>\n<div class=\"code-wrapper\">\n        <div class=\"gatsby-code-button-container\"\n             data-toaster-id=\"97947286317322730000\"\n             data-toaster-duration=\"1500\"\n             onClick=\"copyCodeToClipboard(`{\n  &quot;id&quot;: &quot;01KC2WA3F5G6H7J8K9M0N1P2Q3&quot;,\n  &quot;event_name&quot;: &quot;tracking.updated&quot;,\n  &quot;occurred_at&quot;: &quot;2026-08-23T17:03:00.053997+00:00&quot;,\n  &quot;webhook_id&quot;: &quot;01KC2W7HQ3M8R5T6V9X0YZABCD&quot;,\n  &quot;source&quot;: {\n    &quot;publishing_system&quot;: &quot;genesis&quot;,\n    &quot;company_id&quot;: &quot;3b1f6c2e-8a4d-4f7b-9c21-5e7d0a9b4c11&quot;,\n    &quot;environment&quot;: &quot;sandbox&quot;\n  },\n  &quot;custom_data&quot;: { &quot;tms&quot;: &quot;acme-prod&quot; },\n  &quot;sequence&quot;: 2,\n  &quot;details&quot;: {\n    &quot;resource_id&quot;: &quot;9f2c1b84-6d3a-4c17-9b0e-2f5a7c48d31e&quot;,\n    &quot;tracking&quot;: {\n      &quot;resource_id&quot;: &quot;9f2c1b84-6d3a-4c17-9b0e-2f5a7c48d31e&quot;,\n      &quot;resource_type&quot;: &quot;tracking&quot;,\n      &quot;self_link&quot;: &quot;https://sandbox-api.shipwell.com/tracking/timelines/9f2c1b84-6d3a-4c17-9b0e-2f5a7c48d31e&quot;,\n      &quot;tracking_status&quot;: &quot;COMPLETED&quot;,\n      &quot;is_terminal&quot;: true,\n      &quot;current_state&quot;: &quot;DELIVERED&quot;,\n      &quot;current_state_at&quot;: &quot;2026-08-23T16:40:00Z&quot;,\n      &quot;provider_code&quot;: &quot;RLC&quot;,\n      &quot;carrier_name&quot;: &quot;R+L Carriers&quot;,\n      &quot;carrier_scac&quot;: &quot;RLCA&quot;,\n      &quot;tracking_reference_kind&quot;: &quot;PRO_NUMBER&quot;,\n      &quot;tracking_reference&quot;: &quot;123456789&quot;,\n      &quot;events&quot;: [\n        {\n          &quot;id&quot;: &quot;01KBZD4Q6W0000000000000001&quot;,\n          &quot;occurred_at&quot;: &quot;2026-08-20T14:05:00Z&quot;,\n          &quot;recorded_at&quot;: &quot;2026-08-20T14:31:02.118Z&quot;,\n          &quot;description&quot;: &quot;Shipment picked up&quot;,\n          &quot;carrier_status_code&quot;: &quot;PKP&quot;,\n          &quot;shipment_state&quot;: &quot;AT_PICKUP&quot;,\n          &quot;provider_code&quot;: &quot;RLC&quot;,\n          &quot;location&quot;: { &quot;city&quot;: &quot;CHARLOTTE&quot;, &quot;state&quot;: &quot;NC&quot;, &quot;postal_code&quot;: &quot;28202&quot;, &quot;country&quot;: &quot;US&quot; },\n          &quot;latitude&quot;: 35.22709,\n          &quot;longitude&quot;: -80.84313\n        },\n        {\n          &quot;id&quot;: &quot;01KBZD4Q6W0000000000000009&quot;,\n          &quot;occurred_at&quot;: &quot;2026-08-23T16:40:00Z&quot;,\n          &quot;recorded_at&quot;: &quot;2026-08-23T17:02:41.550Z&quot;,\n          &quot;description&quot;: &quot;Delivered&quot;,\n          &quot;carrier_status_code&quot;: &quot;DEL&quot;,\n          &quot;shipment_state&quot;: &quot;DELIVERED&quot;,\n          &quot;provider_code&quot;: &quot;RLC&quot;,\n          &quot;location&quot;: { &quot;city&quot;: &quot;RALEIGH&quot;, &quot;state&quot;: &quot;NC&quot;, &quot;postal_code&quot;: &quot;27601&quot;, &quot;country&quot;: &quot;US&quot; },\n          &quot;latitude&quot;: 35.77959,\n          &quot;longitude&quot;: -78.63818\n        }\n      ],\n      &quot;next_cursor&quot;: null\n    }\n  }\n}`, `97947286317322730000`)\"\n        >\n          <div class=\"gatsby-code-button\" title=\"Copy the code snippet\">Copy</div>\n          <div class=\"done-indicator done-indicator-97947286317322730000\">Copied</div>\n        </div>\n        <div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"01KC2WA3F5G6H7J8K9M0N1P2Q3\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"event_name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"tracking.updated\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"occurred_at\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"2026-08-23T17:03:00.053997+00:00\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"webhook_id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"01KC2W7HQ3M8R5T6V9X0YZABCD\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"source\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"publishing_system\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"genesis\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"company_id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"3b1f6c2e-8a4d-4f7b-9c21-5e7d0a9b4c11\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"environment\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"sandbox\"</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"custom_data\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token property\">\"tms\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"acme-prod\"</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"sequence\"</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"details\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"resource_id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"9f2c1b84-6d3a-4c17-9b0e-2f5a7c48d31e\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"tracking\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">\"resource_id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"9f2c1b84-6d3a-4c17-9b0e-2f5a7c48d31e\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">\"resource_type\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"tracking\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">\"self_link\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"https://sandbox-api.shipwell.com/tracking/timelines/9f2c1b84-6d3a-4c17-9b0e-2f5a7c48d31e\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">\"tracking_status\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"COMPLETED\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">\"is_terminal\"</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">\"current_state\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"DELIVERED\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">\"current_state_at\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"2026-08-23T16:40:00Z\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">\"provider_code\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"RLC\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">\"carrier_name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"R+L Carriers\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">\"carrier_scac\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"RLCA\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">\"tracking_reference_kind\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"PRO_NUMBER\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">\"tracking_reference\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"123456789\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">\"events\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n        <span class=\"token punctuation\">{</span>\n          <span class=\"token property\">\"id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"01KBZD4Q6W0000000000000001\"</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"occurred_at\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"2026-08-20T14:05:00Z\"</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"recorded_at\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"2026-08-20T14:31:02.118Z\"</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Shipment picked up\"</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"carrier_status_code\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"PKP\"</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"shipment_state\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"AT_PICKUP\"</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"provider_code\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"RLC\"</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"location\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token property\">\"city\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"CHARLOTTE\"</span><span class=\"token punctuation\">,</span> <span class=\"token property\">\"state\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"NC\"</span><span class=\"token punctuation\">,</span> <span class=\"token property\">\"postal_code\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"28202\"</span><span class=\"token punctuation\">,</span> <span class=\"token property\">\"country\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"US\"</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"latitude\"</span><span class=\"token operator\">:</span> <span class=\"token number\">35.22709</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"longitude\"</span><span class=\"token operator\">:</span> <span class=\"token number\">-80.84313</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">{</span>\n          <span class=\"token property\">\"id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"01KBZD4Q6W0000000000000009\"</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"occurred_at\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"2026-08-23T16:40:00Z\"</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"recorded_at\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"2026-08-23T17:02:41.550Z\"</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Delivered\"</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"carrier_status_code\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"DEL\"</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"shipment_state\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"DELIVERED\"</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"provider_code\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"RLC\"</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"location\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token property\">\"city\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"RALEIGH\"</span><span class=\"token punctuation\">,</span> <span class=\"token property\">\"state\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"NC\"</span><span class=\"token punctuation\">,</span> <span class=\"token property\">\"postal_code\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"27601\"</span><span class=\"token punctuation\">,</span> <span class=\"token property\">\"country\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"US\"</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"latitude\"</span><span class=\"token operator\">:</span> <span class=\"token number\">35.77959</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">\"longitude\"</span><span class=\"token operator\">:</span> <span class=\"token number\">-78.63818</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">\"next_cursor\"</span><span class=\"token operator\">:</span> <span class=\"token null keyword\">null</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n      </div>\n</details>\n<table>\n<thead>\n<tr>\n<th>Field</th>\n<th>Meaning</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code class=\"language-text\">id</code></td>\n<td>This event's id. The same on every retry of it, so use it to spot duplicates.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">event_name</code></td>\n<td><code class=\"language-text\">tracking.updated</code>, or <code class=\"language-text\">tracking.webhook.test</code> for a test.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">occurred_at</code></td>\n<td>When Shipwell created the event. Carrier times are on each movement.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">webhook_id</code></td>\n<td>Which of your webhooks this delivery is for.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">source</code></td>\n<td>Who sent it. <code class=\"language-text\">company_id</code> is your company, and <code class=\"language-text\">environment</code> tells sandbox from production.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">custom_data</code></td>\n<td>Exactly what you registered, echoed back.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">sequence</code></td>\n<td>Increases for each <code class=\"language-text\">resource_id</code>. It never goes backwards, and it can skip numbers.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">details.resource_id</code></td>\n<td>The shipment, as returned by the dispatch.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">details.tracking</code></td>\n<td>The full tracking, the same <code class=\"language-text\">TrackingResult</code> that <code class=\"language-text\">GET /tracking/timelines/{resource_id}</code> returns under <code class=\"language-text\">data</code>. Every movement so far is included in this one document, not paged and not only the new ones, so <code class=\"language-text\">next_cursor</code> is always <code class=\"language-text\">null</code>.</td>\n</tr>\n</tbody>\n</table>\n<h2 style=\"position:relative;\"><a href=\"#verify-every-delivery\" aria-label=\"verify every delivery permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"verify-every-delivery\"></div>Verify every delivery</h2>\n<p>Before trusting a delivery, check its signature:</p>\n<ol>\n<li>\nTake the \n<strong>raw request body</strong>\n, exactly as received. Don't parse and re-serialize it first: a single changed space breaks the signature.\n</li>\n<li>\nBuild the string \n<code class=\"language-text\">{X-Shipwell-Timestamp};{raw body}</code>\n.\n</li>\n<li>\nCompute HMAC-SHA256 of it, keyed with your webhook's \n<code class=\"language-text\">secret</code>\n, as lowercase hex.\n</li>\n<li>\nCompare it with \n<code class=\"language-text\">X-Shipwell-Signature</code>\n in constant time. If they differ, answer \n<code class=\"language-text\">401</code>\n and ignore the delivery.\n</li>\n</ol>\n<p>To guard against replays, also reject a delivery whose <code class=\"language-text\">X-Shipwell-Timestamp</code> is more than a few minutes old. Retries are signed afresh, so a genuine retry always passes this check.</p>\n<details open>\n  <summary>Node.js (Express)</summary>\n<div class=\"code-wrapper\">\n        <div class=\"gatsby-code-button-container\"\n             data-toaster-id=\"82318355385735580000\"\n             data-toaster-duration=\"1500\"\n             onClick=\"copyCodeToClipboard(`const crypto = require(&quot;crypto&quot;);\nconst express = require(&quot;express&quot;);\n\nconst app = express();\nconst SECRET = process.env.SHIPWELL_WEBHOOK_SECRET; // the whsec_... value from registration\n\n// express.raw keeps the body as the exact bytes Shipwell signed.\napp.post(&quot;/hooks/shipwell-tracking&quot;, express.raw({ type: &quot;application/json&quot; }), async (req, res) => {\n  const timestamp = req.get(&quot;X-Shipwell-Timestamp&quot;) || &quot;&quot;;\n  const received = req.get(&quot;X-Shipwell-Signature&quot;) || &quot;&quot;;\n  const expected = crypto\n    .createHmac(&quot;sha256&quot;, SECRET)\n    .update(\\\\`redocly_dollar_sign{timestamp};redocly_dollar_sign{req.body.toString(&quot;utf8&quot;)}\\\\`)\n    .digest(&quot;hex&quot;);\n\n  const valid =\n    received.length === expected.length &&\n    crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));\n  const fresh = Math.abs(Date.now() - Date.parse(timestamp)) < 5 * 60 * 1000;\n  if (!valid || !fresh) return res.sendStatus(401);\n\n  const event = JSON.parse(req.body);\n  try {\n    await storeDurably(event); // your queue or database: resolve only once the event is persisted\n  } catch (err) {\n    return res.sendStatus(503); // not stored, so let Shipwell retry\n  }\n  res.sendStatus(204); // acknowledge after storing, process afterwards\n});`, `82318355385735580000`)\"\n        >\n          <div class=\"gatsby-code-button\" title=\"Copy the code snippet\">Copy</div>\n          <div class=\"done-indicator done-indicator-82318355385735580000\">Copied</div>\n        </div>\n        <div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> crypto <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"crypto\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> express <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"express\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> app <span class=\"token operator\">=</span> <span class=\"token function\">express</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> <span class=\"token constant\">SECRET</span> <span class=\"token operator\">=</span> process<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span><span class=\"token constant\">SHIPWELL_WEBHOOK_SECRET</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// the whsec_... value from registration</span>\n\n<span class=\"token comment\">// express.raw keeps the body as the exact bytes Shipwell signed.</span>\napp<span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/hooks/shipwell-tracking\"</span><span class=\"token punctuation\">,</span> express<span class=\"token punctuation\">.</span><span class=\"token function\">raw</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">\"application/json\"</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">req<span class=\"token punctuation\">,</span> res</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> timestamp <span class=\"token operator\">=</span> req<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"X-Shipwell-Timestamp\"</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> received <span class=\"token operator\">=</span> req<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"X-Shipwell-Signature\"</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> expected <span class=\"token operator\">=</span> crypto\n    <span class=\"token punctuation\">.</span><span class=\"token function\">createHmac</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"sha256\"</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">SECRET</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">update</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>timestamp<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">;</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>req<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"utf8\"</span><span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">digest</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"hex\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> valid <span class=\"token operator\">=</span>\n    received<span class=\"token punctuation\">.</span>length <span class=\"token operator\">===</span> expected<span class=\"token punctuation\">.</span>length <span class=\"token operator\">&amp;&amp;</span>\n    crypto<span class=\"token punctuation\">.</span><span class=\"token function\">timingSafeEqual</span><span class=\"token punctuation\">(</span>Buffer<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span>received<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> Buffer<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span>expected<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> fresh <span class=\"token operator\">=</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">abs</span><span class=\"token punctuation\">(</span>Date<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> Date<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>timestamp<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;</span> <span class=\"token number\">5</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>valid <span class=\"token operator\">||</span> <span class=\"token operator\">!</span>fresh<span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token function\">sendStatus</span><span class=\"token punctuation\">(</span><span class=\"token number\">401</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> event <span class=\"token operator\">=</span> <span class=\"token constant\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>req<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">await</span> <span class=\"token function\">storeDurably</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// your queue or database: resolve only once the event is persisted</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>err<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token function\">sendStatus</span><span class=\"token punctuation\">(</span><span class=\"token number\">503</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// not stored, so let Shipwell retry</span>\n  <span class=\"token punctuation\">}</span>\n  res<span class=\"token punctuation\">.</span><span class=\"token function\">sendStatus</span><span class=\"token punctuation\">(</span><span class=\"token number\">204</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// acknowledge after storing, process afterwards</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n      </div>\n</details>\n<details>\n  <summary>Python (Flask)</summary>\n<div class=\"code-wrapper\">\n        <div class=\"gatsby-code-button-container\"\n             data-toaster-id=\"68598550912833446000\"\n             data-toaster-duration=\"1500\"\n             onClick=\"copyCodeToClipboard(`import hashlib\nimport hmac\nimport os\nfrom datetime import datetime, timedelta, timezone\n\nfrom flask import Flask, abort, request\n\napp = Flask(__name__)\nSECRET = os.environ[&quot;SHIPWELL_WEBHOOK_SECRET&quot;]  # the whsec_... value from registration\n\n\n@app.post(&quot;/hooks/shipwell-tracking&quot;)\ndef shipwell_tracking():\n    timestamp = request.headers.get(&quot;X-Shipwell-Timestamp&quot;, &quot;&quot;)\n    received = request.headers.get(&quot;X-Shipwell-Signature&quot;, &quot;&quot;)\n    body = request.get_data(as_text=True)  # the raw body, exactly as sent\n    expected = hmac.new(SECRET.encode(), f&quot;{timestamp};{body}&quot;.encode(), hashlib.sha256).hexdigest()\n\n    if not hmac.compare_digest(received, expected):\n        abort(401)\n    try:\n        sent_at = datetime.fromisoformat(timestamp)\n    except ValueError:\n        abort(401)\n    if abs(datetime.now(timezone.utc) - sent_at) > timedelta(minutes=5):\n        abort(401)\n\n    try:\n        store_durably(request.get_json())  # your queue or database: return only once persisted\n    except Exception:\n        abort(503)  # not stored, so let Shipwell retry\n    return &quot;&quot;, 204  # acknowledge after storing, process afterwards`, `68598550912833446000`)\"\n        >\n          <div class=\"gatsby-code-button\" title=\"Copy the code snippet\">Copy</div>\n          <div class=\"done-indicator done-indicator-68598550912833446000\">Copied</div>\n        </div>\n        <div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">import</span> hashlib\n<span class=\"token keyword\">import</span> hmac\n<span class=\"token keyword\">import</span> os\n<span class=\"token keyword\">from</span> datetime <span class=\"token keyword\">import</span> datetime<span class=\"token punctuation\">,</span> timedelta<span class=\"token punctuation\">,</span> timezone\n\n<span class=\"token keyword\">from</span> flask <span class=\"token keyword\">import</span> Flask<span class=\"token punctuation\">,</span> abort<span class=\"token punctuation\">,</span> request\n\napp <span class=\"token operator\">=</span> Flask<span class=\"token punctuation\">(</span>__name__<span class=\"token punctuation\">)</span>\nSECRET <span class=\"token operator\">=</span> os<span class=\"token punctuation\">.</span>environ<span class=\"token punctuation\">[</span><span class=\"token string\">\"SHIPWELL_WEBHOOK_SECRET\"</span><span class=\"token punctuation\">]</span>  <span class=\"token comment\"># the whsec_... value from registration</span>\n\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>post</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/hooks/shipwell-tracking\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">def</span> <span class=\"token function\">shipwell_tracking</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    timestamp <span class=\"token operator\">=</span> request<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span>get<span class=\"token punctuation\">(</span><span class=\"token string\">\"X-Shipwell-Timestamp\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span>\n    received <span class=\"token operator\">=</span> request<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span>get<span class=\"token punctuation\">(</span><span class=\"token string\">\"X-Shipwell-Signature\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">)</span>\n    body <span class=\"token operator\">=</span> request<span class=\"token punctuation\">.</span>get_data<span class=\"token punctuation\">(</span>as_text<span class=\"token operator\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\">)</span>  <span class=\"token comment\"># the raw body, exactly as sent</span>\n    expected <span class=\"token operator\">=</span> hmac<span class=\"token punctuation\">.</span>new<span class=\"token punctuation\">(</span>SECRET<span class=\"token punctuation\">.</span>encode<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string-interpolation\"><span class=\"token string\">f\"</span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>timestamp<span class=\"token punctuation\">}</span></span><span class=\"token string\">;</span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>body<span class=\"token punctuation\">}</span></span><span class=\"token string\">\"</span></span><span class=\"token punctuation\">.</span>encode<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> hashlib<span class=\"token punctuation\">.</span>sha256<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>hexdigest<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token keyword\">if</span> <span class=\"token keyword\">not</span> hmac<span class=\"token punctuation\">.</span>compare_digest<span class=\"token punctuation\">(</span>received<span class=\"token punctuation\">,</span> expected<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        abort<span class=\"token punctuation\">(</span><span class=\"token number\">401</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">try</span><span class=\"token punctuation\">:</span>\n        sent_at <span class=\"token operator\">=</span> datetime<span class=\"token punctuation\">.</span>fromisoformat<span class=\"token punctuation\">(</span>timestamp<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">except</span> ValueError<span class=\"token punctuation\">:</span>\n        abort<span class=\"token punctuation\">(</span><span class=\"token number\">401</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">if</span> <span class=\"token builtin\">abs</span><span class=\"token punctuation\">(</span>datetime<span class=\"token punctuation\">.</span>now<span class=\"token punctuation\">(</span>timezone<span class=\"token punctuation\">.</span>utc<span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> sent_at<span class=\"token punctuation\">)</span> <span class=\"token operator\">></span> timedelta<span class=\"token punctuation\">(</span>minutes<span class=\"token operator\">=</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        abort<span class=\"token punctuation\">(</span><span class=\"token number\">401</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token keyword\">try</span><span class=\"token punctuation\">:</span>\n        store_durably<span class=\"token punctuation\">(</span>request<span class=\"token punctuation\">.</span>get_json<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>  <span class=\"token comment\"># your queue or database: return only once persisted</span>\n    <span class=\"token keyword\">except</span> Exception<span class=\"token punctuation\">:</span>\n        abort<span class=\"token punctuation\">(</span><span class=\"token number\">503</span><span class=\"token punctuation\">)</span>  <span class=\"token comment\"># not stored, so let Shipwell retry</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">,</span> <span class=\"token number\">204</span>  <span class=\"token comment\"># acknowledge after storing, process afterwards</span></code></pre></div>\n      </div>\n</details>\n<p>This is the same signing scheme as Shipwell's platform <a href=\"/docs/webhook/use-webhook/\">webhooks</a>, so code you already use to verify those works here unchanged.</p>\n<h2 style=\"position:relative;\"><a href=\"#handle-each-delivery\" aria-label=\"handle each delivery permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"handle-each-delivery\"></div>Handle each delivery</h2>\n<p>Deliveries are <strong>at least once</strong>, and they aren't guaranteed to arrive in order. Four rules make that safe:</p>\n<ol>\n<li>\n<strong>Ignore an <code class=\"language-text\">id</code> you have already processed.</strong>\n A retry after a timeout, or a response Shipwell never saw, sends the same event again.\n</li>\n<li>\n<strong>Keep the highest <code class=\"language-text\">sequence</code> per <code class=\"language-text\">resource_id</code>, and ignore anything lower.</strong>\n An older snapshot arriving late must not overwrite a newer one. If your webhook also receives \n<code class=\"language-text\">documents.updated</code>\n, keep it per \n<code class=\"language-text\">resource_id</code>\n and \n<code class=\"language-text\">event_name</code>\n: each event counts its own sequence.\n</li>\n<li>\n<strong>Replace, don't merge.</strong>\n \n<code class=\"language-text\">details.tracking</code>\n is the whole current state, so overwrite your copy of the shipment's tracking with it. There's nothing to reconstruct from differences.\n</li>\n<li>\n<strong>Store, acknowledge, then process.</strong>\n Answer \n<code class=\"language-text\">2xx</code>\n only once the delivery is verified and stored durably, then do the rest of the work. If you can't store it, answer \n<code class=\"language-text\">503</code>\n and Shipwell retries. A \n<code class=\"language-text\">2xx</code>\n for an event you then lose can't be recovered by retry.\n</li>\n</ol>\n<p><code class=\"language-text\">sequence</code> can skip numbers, so never wait for a missing one. A gap simply means a snapshot you had no webhook to receive.</p>\n<h3 style=\"position:relative;\"><a href=\"#mixing-pull-and-push\" aria-label=\"mixing pull and push permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"mixing-pull-and-push\"></div>Mixing pull and push</h3>\n<p><code class=\"language-text\">GET /tracking/timelines/{resource_id}</code> returns the current tracking but carries no <code class=\"language-text\">sequence</code>, so it can't be ordered against deliveries. A delivery that was being retried while you pulled may hold an older snapshot than the one you pulled, and no later change is guaranteed to correct it. So:</p>\n<ul>\n<li>\nKeep applying deliveries by \n<code class=\"language-text\">sequence</code>\n as usual.\n</li>\n<li>\nAfter applying any delivery that arrives after a pull, pull that shipment again. A retry can arrive hours later.\n</li>\n<li>\nNever let a snapshot with \n<code class=\"language-text\">is_terminal: false</code>\n replace one with \n<code class=\"language-text\">is_terminal: true</code>\n. A finished shipment sends no further changes.\n</li>\n</ul>\n<h2 style=\"position:relative;\"><a href=\"#retries\" aria-label=\"retries permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"retries\"></div>Retries</h2>\n<table>\n<thead>\n<tr>\n<th>Your endpoint answers</th>\n<th>What Shipwell does</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Any <code class=\"language-text\">2xx</code></td>\n<td>Done.</td>\n</tr>\n<tr>\n<td>A timeout (over 10 seconds), a network or TLS error, <code class=\"language-text\">408</code>, <code class=\"language-text\">429</code> or <code class=\"language-text\">5xx</code></td>\n<td>Retries after 30 seconds, then 2 minutes, 10 minutes, 30 minutes, 1 hour, 3, 6, 12 and 24 hours: 10 attempts over about 47 hours. A <code class=\"language-text\">Retry-After</code> header on a <code class=\"language-text\">429</code> or <code class=\"language-text\">503</code> is honoured.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">410 Gone</code></td>\n<td>Stops, and disables the webhook. Use it when you've retired the URL.</td>\n</tr>\n<tr>\n<td>Any other <code class=\"language-text\">4xx</code>, or a <code class=\"language-text\">3xx</code></td>\n<td>Stops, without retrying: a refusal won't change on its own. Redirects are not followed.</td>\n</tr>\n</tbody>\n</table>\n<p>Every retry of an event carries the same <code class=\"language-text\">id</code> and body, with a new <code class=\"language-text\">X-Shipwell-Timestamp</code> and signature. The one exception is <code class=\"language-text\">documents.updated</code>, whose file download links are made fresh for each attempt; see <a href=\"/docs/direct-carrier-connections/documents-webhooks/#retries-pausing-and-delivery-history\">Documents Webhooks</a>. To send a failed delivery again yourself, use <strong>Retry</strong> in <a href=\"#set-it-up-in-the-shipwell-app\">Delivery history</a>. If an event exhausts its retries, the next change to that shipment still sends the full, current snapshot, and <a href=\"/docs/direct-carrier-connections/tracking/\"><code class=\"language-text\">GET /tracking/timelines/{resource_id}</code></a> always has it.</p>\n<h2 style=\"position:relative;\"><a href=\"#pause-change-or-remove-a-webhook\" aria-label=\"pause change or remove a webhook permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"pause-change-or-remove-a-webhook\"></div>Pause, change or remove a webhook</h2>\n<p>Use <strong>Edit</strong>, <strong>Pause</strong> and <strong>Delete</strong> in the Tracking section of <strong>Company → Webhooks</strong> in the Shipwell app. Pausing stops deliveries until you resume; a delivery can't be retried while its webhook is paused. To stop deliveries from your side at once, answer <code class=\"language-text\">410 Gone</code>: Shipwell disables the webhook on the first one, and you can resume it in the app.</p>\n<h2 style=\"position:relative;\"><a href=\"#errors\" aria-label=\"errors permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"errors\"></div>Errors</h2>\n<table>\n<thead>\n<tr>\n<th>Status</th>\n<th><code class=\"language-text\">code</code></th>\n<th>Meaning</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code class=\"language-text\">400</code></td>\n<td></td>\n<td>The request body is invalid, for example a URL that isn't HTTPS, points at a private address, or contains whitespace. <code class=\"language-text\">errors[].source.pointer</code> names the field.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">401</code></td>\n<td></td>\n<td>The <code class=\"language-text\">Authorization</code> header is missing or invalid. Send <code class=\"language-text\">Authorization: Token &lt;user-token></code>.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">403</code></td>\n<td><code class=\"language-text\">webhooks_not_enabled</code></td>\n<td>Tracking webhooks aren't enabled for your company yet. Contact your Shipwell representative.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">403</code></td>\n<td><code class=\"language-text\">documents_webhooks_not_enabled</code></td>\n<td><code class=\"language-text\">event_names</code> includes <code class=\"language-text\">documents.updated</code>, which isn't enabled for your company yet. See <a href=\"/docs/direct-carrier-connections/documents-webhooks/\">Documents Webhooks</a>.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">409</code></td>\n<td><code class=\"language-text\">webhook_already_exists</code></td>\n<td>Your company already has a tracking webhook. Change its URL instead of registering another.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">404</code></td>\n<td><code class=\"language-text\">webhook_not_found</code></td>\n<td>No webhook with that id for your company. Another company's webhook answers the same as one that doesn't exist.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">429</code></td>\n<td></td>\n<td>Too many requests. Wait a moment and try again.</td>\n</tr>\n<tr>\n<td><code class=\"language-text\">503</code></td>\n<td><code class=\"language-text\">webhooks_unavailable</code></td>\n<td>Tracking webhooks aren't available in this environment yet.</td>\n</tr>\n</tbody>\n</table>\n<h2 style=\"position:relative;\"><a href=\"#troubleshooting\" aria-label=\"troubleshooting permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"troubleshooting\"></div>Troubleshooting</h2>\n<table>\n<thead>\n<tr>\n<th>Symptom</th>\n<th>Check</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>The test says <code class=\"language-text\">delivered: false</code> with <code class=\"language-text\">response_code: null</code></td>\n<td>That your URL is reachable from the internet over HTTPS with a valid certificate, and resolves to a public address.</td>\n</tr>\n<tr>\n<td>The test reaches you, but your signature check fails</td>\n<td>That you hash the <strong>raw</strong> body, not a parsed and re-serialized copy, and use the <code class=\"language-text\">secret</code> from this webhook's registration.</td>\n</tr>\n<tr>\n<td>The test works, but no <code class=\"language-text\">tracking.updated</code> arrives</td>\n<td>That the freight was booked through <code class=\"language-text\">POST /dispatching/jobs</code> by the same company, and that its tracking has changed since you registered. Changes before registration aren't sent. <code class=\"language-text\">GET /tracking/timelines/{resource_id}</code> shows the current state.</td>\n</tr>\n<tr>\n<td>The same event arrives twice</td>\n<td>Expected with at-least-once delivery. Skip an <code class=\"language-text\">id</code> you've already processed.</td>\n</tr>\n<tr>\n<td>Deliveries stopped</td>\n<td><strong>Delivery history</strong> in the app: failed deliveries show your endpoint's last answer. Check the webhook isn't paused; a <code class=\"language-text\">410 Gone</code> answer pauses it. Fix your endpoint, then <strong>Retry all failed</strong>.</td>\n</tr>\n</tbody>\n</table>\n<h2 style=\"position:relative;\"><a href=\"#api-reference\" aria-label=\"api reference permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"api-reference\"></div>API reference</h2>\n<p><a href=\"https://docs.shipwell.com/openapi_pages/carrier-connections/tag/Tracking-Webhooks/\">Tracking Webhooks API reference</a>: both endpoints and both event payloads, with every field.</p>\n<h2 style=\"position:relative;\"><a href=\"#next-steps\" aria-label=\"next steps permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><div class=\"hidden-anchor\" id=\"next-steps\"></div>Next steps</h2>\n<p>Read <a href=\"/docs/direct-carrier-connections/tracking/\">Tracking</a> for what the tracking document's fields and states mean, and have the shipment's documents <a href=\"/docs/direct-carrier-connections/documents-webhooks/\">pushed to the same webhook</a> or <a href=\"/docs/direct-carrier-connections/documents/\">pull them</a> as it nears delivery.</p>","headings":[{"value":"Tracking Webhooks","depth":1},{"value":"How it works","depth":2},{"value":"Before you start","depth":2},{"value":"Two ways to set it up","depth":2},{"value":"Set it up in the Shipwell app","depth":2},{"value":"Endpoints","depth":2},{"value":"Step 1: Register your URL","depth":2},{"value":"Step 2: Send a test","depth":2},{"value":"Step 3: Receive <code class=\"language-text\">tracking.updated</code>","depth":2},{"value":"Headers","depth":3},{"value":"Verify every delivery","depth":2},{"value":"Handle each delivery","depth":2},{"value":"Mixing pull and push","depth":3},{"value":"Retries","depth":2},{"value":"Pause, change or remove a webhook","depth":2},{"value":"Errors","depth":2},{"value":"Troubleshooting","depth":2},{"value":"API reference","depth":2},{"value":"Next steps","depth":2}]},"contentItem":{"data":{"lastModified":"2026-10-03T23:48:23.000Z","enableToc":null,"disableLastModified":null,"tocMaxDepth":null,"requestLogin":false}},"siteConfig":{"enableToc":true,"disableLastModified":false,"tocMaxDepth":4}},"pageContext":{"matchPath":"","id":"e2362791-d2ac-5d1c-bfc4-fa5634802c8d__redocly content/docs/direct-carrier-connections/tracking-webhooks/","seo":{"title":"Tracking Webhooks","description":null,"image":"","keywords":null,"jsonLd":null,"lang":null,"siteUrl":null},"pageId":"docs/direct-carrier-connections/tracking-webhooks.md","pageBaseUrl":"/docs/direct-carrier-connections/tracking-webhooks","type":"markdown","toc":{"enable":true,"maxDepth":4,"headings":[{"depth":1,"value":"Tracking Webhooks","id":"tracking-webhooks"},{"depth":2,"value":"How it works","id":"how-it-works"},{"depth":2,"value":"Before you start","id":"before-you-start"},{"depth":2,"value":"Two ways to set it up","id":"two-ways-to-set-it-up"},{"depth":2,"value":"Set it up in the Shipwell app","id":"set-it-up-in-the-shipwell-app"},{"depth":2,"value":"Endpoints","id":"endpoints"},{"depth":2,"value":"Step 1: Register your URL","id":"step-1-register-your-url"},{"depth":2,"value":"Step 2: Send a test","id":"step-2-send-a-test"},{"depth":2,"value":"Step 3: Receive <code class=\"language-text\">tracking.updated</code>","id":"step-3-receive-trackingupdated"},{"depth":3,"value":"Headers","id":"headers"},{"depth":2,"value":"Verify every delivery","id":"verify-every-delivery"},{"depth":2,"value":"Handle each delivery","id":"handle-each-delivery"},{"depth":3,"value":"Mixing pull and push","id":"mixing-pull-and-push"},{"depth":2,"value":"Retries","id":"retries"},{"depth":2,"value":"Pause, change or remove a webhook","id":"pause-change-or-remove-a-webhook"},{"depth":2,"value":"Errors","id":"errors"},{"depth":2,"value":"Troubleshooting","id":"troubleshooting"},{"depth":2,"value":"API reference","id":"api-reference"},{"depth":2,"value":"Next steps","id":"next-steps"}]},"data":{"title":"Tracking Webhooks"},"catalogInfo":null,"link":"/docs/direct-carrier-connections/tracking-webhooks/","sidebarName":"developerPortal","isLanding":false,"showPrevButton":null,"showNextButton":null,"apiVersions":null,"apiVersionId":null,"isDefaultApiVersion":null}},"staticQueryHashes":["1123603147","1302185487","1344209882","1398840060","1520077861","1975142765","2667623876","2950305614","3240152602","3743992808","561138138"]}