{"templateId":"markdown","versions":[{"version":"shipstation-v2","label":"ShipStation V2 API","link":"/apis/shipstation-v2/docs/tracking/webhooks","default":true,"active":false,"folderId":"58c9a61d"},{"version":"shipengine","label":"ShipStation API (formerly ShipEngine)","link":"/apis/shipengine/docs/tracking/webhooks","default":false,"active":true,"folderId":"58c9a61d"},{"version":"shipstation-v1","label":"ShipStation V1 API","link":"/apis/shipstation-v1/docs/tracking/webhooks","default":false,"active":false,"folderId":"58c9a61d"}],"sharedDataIds":{"sidebar":"sidebar-apis/@shipengine/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Creating a Webhook Listener","keywords":"shipping, labels, shipstation, documentation, api","siteUrl":"https://docs.shipstation.com","lang":"en-US","llmstxt":{"hide":false,"title":"ShipStation API LLM Docs","description":"Find links and references to all markdown documentation for use with LLMs","excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"creating-a-webhook-listener","__idx":0},"children":["Creating a Webhook Listener"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["As described in our ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"/apis/shipengine/docs/guides/webhooks"},"children":["Setting Up Webhooks"]}," guide, testing webhooks using ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://requestbin.com/"},"children":["RequestBin.com"]}," is a great way to get started with using and understanding webhooks."," ","It provides temporary URLs that you can register through the ShipStation API dashboard or by using the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["v1/environment/webhooks"]}," endpoint."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When using ShipStation API in a production environment, you will need to provide a more robust web service to host your own custom endpoints. This guide walks you through creating a simple web application that exposes an endpoint you can use to subscribe to ShipStation API ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["track"]}," webhooks."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["We will be developing a web application that listens for webhook requests, often called a webhook listener. Our application will run a web server and listen for HTTP POST requests on a specified endpoint This example is written in ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://developer.mozilla.org/en-US/docs/Web/JavaScript"},"children":["JavaScript"]},"/",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://nodejs.org/"},"children":["NodeJS"]}," and uses the ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://expressjs.com/"},"children":["Express"]}," web framework."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"requirements","__idx":1},"children":["Requirements"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To follow along with the steps in this guide, you will need to do the following:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Install ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://nodejs.org"},"children":["NodeJS"]}," per the site's instructions. Likewise, you can use other package managers, such as ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://brew.sh/"},"children":["Homebrew"]}," on macOS or Linux, and ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://chocolatey.org/"},"children":["Chocolatey"]}," for Windows."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Verify that ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://www.npmjs.com/"},"children":["npm"]}," was installed successfully along with NodeJS by running the following command from a terminal: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["npm -v"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Clone the ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://github.com/ShipEngine/code-samples"},"children":["code-samples repo"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Open an editor where you can write and run code. We will be using ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://code.visualstudio.com/"},"children":["Visual Studio Code"]}," in this guide, but you can use any editor you are comfortable with."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["In VS Code, select ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["File"]}," in the menu bar at the top and then select ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Open Folder"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Navigate to the directory where you cloned the repo and select ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["code-samples/node-webhook-listener-track"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Click the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Select"]}," button"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Now that you have the source code available, we will walk through and explain it line by line."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"install-dependencies","__idx":2},"children":["Install Dependencies"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["We will be using several external NPM packages in our web application, such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Express"]},". These are known as dependencies. We could install each package individually by running ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["npm install <package name>"]},". However, it is customary to provide a ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://github.com/ShipEngine/code-samples/blob/218fef76a2ac4bad4f29322a1a6ee64b6a93552b/node-webhook-listener-track/package.json#L1"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["package.json"]}]}," file in the root of your project that lists all the project's dependencies as well as other identifying information about the application. This facilitates installation since all dependencies or all files in the project are listed in a central location and can all be installed at once with a single command."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When you create a new NodeJS project, you can run ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["npm init"]}," and answer a series of questions to generate a default ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["package.json"]}," file. We have included a complete ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["package.json"]}," file in the repository."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Open a terminal in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["node-webhook-listener-track"]}," directory and run the following command: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["npm install"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This command installs the dependencies we listed in the",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["package.json"]}," file and creates a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["package.json.lock"]}," file to nail down the specific versions installed since the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["package.json"]}," file allows us to specify minimum versions."]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["TIP:"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"open-a-terminal","__idx":3},"children":["Open a Terminal"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can access a terminal directly from Visual Studio Code. Click ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Terminal"]}," on the menu bar and select ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["New"]}," and a terminal will open up at the bottom of your screen."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"import-dependencies","__idx":4},"children":["Import Dependencies"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["We've now installed all the dependencies required for this application, and we are ready to take a look at the code, which resides in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["index.js"]}," file."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["We start by importing the tools and frameworks mentioned above, as well as a few others, at the top of our file. The code below includes all the packages needed by this application. We will be using ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["express"]},", which is a web framework that provides the web server we are using. We will configure ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["express"]}," to use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["body-parser"]}," package so that we can access the data in the requests sent to the endpoint. The other packages are used in other endpoints in this application."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"const express = require('express');\nconst bodyParser = require('body-parser');\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"create-the-web-application","__idx":5},"children":["Create the Web Application"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["At this point, we have specified and installed dependencies, and we are ready to create our web application and define endpoints. When you configure webhooks in ShipStation API, you provide a URL to which ShipStation API will send an HTTP POST request with a JSON payload whenever a particular event occurs. We recommend that you create an individual endpoint for each type of webhook you wish to subscribe to and limit traffic on those endpoints to ShipStation API webhook traffic."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The code below creates an instance of an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["express"]}," web server and assigns it to the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["app"]}," variable. We then configure our ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["express"]}," application to use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["body-parser"]}," package. Since we called the variable ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["bodyParser"]}," in the import statement above, that's how we reference it in the code below. The last line of code starts the server, listening on port ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["3000"]}," for incoming requests. This line is customarily the last one in the script. We will be filling in our endpoint implementation in the space between."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"const app = express();\napp.use(bodyParser.urlencoded({ extended: false }));\napp.use(bodyParser.json());\n\n...\n\nlet server = app.listen(3000, function() {\n  console.log('Listening on port %d', server.address().port);\n});\n\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"create-the-track-endpoint","__idx":6},"children":["Create the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/track"]}," Endpoint"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["We now have a very simple web application called ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["app,"]}," but we have not defined any endpoints for our application. In this example, we are going to create a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/track"]}," endpoint to use for tracking event webhooks. One of the benefits of developing and hosting your own web application is that you can programmatically trigger other events to occur once you receive the webhook, which we'll demonstrate in our example endpoints."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Before we attempt to implement the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/track"]}," endpoint, let's take a look at the structure of the message we expect to receive. This is an example of the payload the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/track"]}," endpoint will receive whenever a tracking event webhook is fired."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"sample-track-webhook-payload","__idx":7},"children":["Sample Track Webhook Payload"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"resource_url\": \"https://api.shipengine.com/v1/tracking?carrier_code=usps&tracking_number=9400111298370264401222\",\n  \"resource_type\": \"API_TRACK\",\n  \"data\": {\n    \"label_url\": null,\n    \"tracking_number\": \"9400111298370264401222\",\n    \"status_code\": \"IT\",\n    \"carrier_detail_code\": null,\n    \"status_description\": \"In Transit\",\n    \"carrier_status_code\": \"NT\",\n    \"carrier_status_description\": \"Your package is moving within the USPS network and is on track to be delivered the expected delivery date. It is currently in transit to the next facility.\",\n    \"ship_date\": \"2020-06-30T16:09:00\",\n    \"estimated_delivery_date\": \"2020-07-06T00:00:00\",\n    \"actual_delivery_date\": null,\n    \"exception_description\": null,\n    \"events\": [\n      {\n        \"occurred_at\": \"2020-07-02T00:00:00Z\",\n        \"carrier_occurred_at\": \"2020-07-02T00:00:00\",\n        \"description\": \"In Transit, Arriving On Time\",\n        \"city_locality\": \"\",\n        \"state_province\": \"\",\n        \"postal_code\": \"\",\n        \"country_code\": \"\",\n        \"company_name\": \"\",\n        \"signer\": \"\",\n        \"event_code\": \"NT\",\n        \"event_description\": \"In Transit, Arriving On Time\",\n        \"carrier_detail_code\": null,\n        \"status_code\": null,\n        \"latitude\": null,\n        \"longitude\": null\n      },\n      {\n        \"occurred_at\": \"2020-06-30T20:09:00Z\",\n        \"carrier_occurred_at\": \"2020-06-30T16:09:00\",\n        \"description\": \"Shipment Received, Package Acceptance Pending\",\n        \"city_locality\": \"VERSAILLES\",\n        \"state_province\": \"KY\",\n        \"postal_code\": \"40383\",\n        \"country_code\": \"\",\n        \"company_name\": \"\",\n        \"signer\": \"\",\n        \"event_code\": \"TM\",\n        \"event_description\": \"Shipment Received, Package Acceptance Pending\",\n        \"carrier_detail_code\": null,\n        \"status_code\": null,\n        \"latitude\": 37.8614,\n        \"longitude\": -84.6646\n      }\n    ]\n  }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["INFO:"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"event-timestamps","__idx":8},"children":["Event Timestamps"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["carrier_occurred_at"]}," is the timestamp of the event received from the carrier, it is assumed to be the local time of where the event occurred."," ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["Note that this property is not yet fully supported across all carriers."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["occurred_at"]}," is the UTC based time of the event's occurrence."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can see that the example payload above contains a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status_code"]}," property, which you can use to find out where the package is in the delivery process.  You can use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status_description"]}," for an explanation of the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status_code"]},". For example, the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status_code"]}," 'DE' has a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status_description"]}," of 'Delivered'."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This payload also contains a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tracking_number"]},", which you should be able to use to locate the customer and their contact data from within your application - not your webhook listener application but the application you used to create the label you want to receive tracking events for."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The code below is an example implementation of an endpoint that listens for HTTP POST requests. We do this by making a call to the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["app.post"]}," method of our ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["express"]}," server instance. The first parameter we pass to the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["app.post"]}," method is the path we want to use for the endpoint, in this case ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/track"]},". The second parameter is a callback function."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you need more context on asynchronous programming and callback functions, check out ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://javascript.info/callbacks"},"children":["this reference"]},". The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["app.post"]}," method is asynchronous, meaning that the program does not wait for this call to return before moving to the next line of code in the file. Instead, we pass a callback function as the second parameter to tell the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["app.post"]}," method what to do when it completes. In this case, we define the callback function directly in the call to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["app.post"]},". This is known as an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["anonymous function"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Once the endpoint receives a request, it invokes the callback function. In this case, we extract useful data from the payload that is sent from the webhook. We can look at the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status_code"]}," to determine that the package has been delivered. We can use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tracking_number"]}," to look up the customer's contact information and send an email informing them that their package has been delivered, adding a personal touch to your service. We have access to this data on the request object because we configured the application to use the ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://www.npmjs.com/package/body-parser"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["body-parser"]}]}," package."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Finally, we send a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200"]}," HTTP status code to terminate the call and return program control to the main application. Whenever ShipStation API sends a tracking event webhook to your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/track"]}," endpoint, this code will be called."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"app.post('/track', (req, res) => {\n\n  let trackingNumber = req.body.data.tracking_number;\n  let statusCode = req.body.data.status_code;\n\n  if(statusCode === 'DE') { // Package was delivered\n\n    // Use the trackingNumber to get the contact\n    // info associated with this package from your backend system.\n    // Email the user that the package was delivered.\n  }\n  res.sendStatus(200);\n});\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"putting-it-all-together","__idx":9},"children":["Putting It All Together"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["We have now performed the following:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Cloned the repo."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Installed the packages using ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["npm install"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Created import statements for the packages we want to use in our application."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Created an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["express"]}," web application that listens for requests on port ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["3000"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Created an endpoint to use for tracking events."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The complete script for the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/track"]}," endpoint is included below. If you cloned our ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://github.com/ShipEngine/code-samples"},"children":["code-samples repository"]},", you will find this example application, in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["node-webhook-listener-track"]}," directory."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"const express = require('express');\nconst bodyParser = require('body-parser');\n\nconst app = express();\napp.use(bodyParser.urlencoded({ extended: false }));\napp.use(bodyParser.json());\n\nconst API_KEY = YOUR_API_KEY;\n\napp.post('/track', (req, res) => {\n\n  let trackingNumber = req.body.data.tracking_number;\n  let statusCode = req.body.data.status_code;\n\n  if(statusCode === 'DE') { // Package was delivered\n\n    // Use the trackingNumber to get the contact\n    // info associated with this package from your backend system.\n    // Email the user that the package was delivered.\n  }\n  res.sendStatus(200);\n});\n\nlet server = app.listen(3000, function() {\n  console.log('Listening on port %d', server.address().port);\n});\n\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"running-the-webhook-listener","__idx":10},"children":["Running the Webhook Listener"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["We have now written a web application that exposes a single endpoint. Let's run it locally to test it out."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you are developing in Visual Studio Code, follow these steps to run your application:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Make sure ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["index.js"]}," is open in Visual Studio Code."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Set the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["API_KEY"]}," variable to a valid API key for your account."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Click ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Run"]}," in the top menu."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Select ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Start Debugging"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Selected ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Node.js"]}," in the environment drop-down list that is displayed."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You should see a debug window at the bottom of the screen. Your VS Code should look similar to this."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"img","attributes":{"src":"/assets/vsc-debug.18bd7f31556775d975cb0efad16c80a8058b1946eeef13e576ff481e4f6a0ebb.afd9f624.png","alt":"VS Code Debug Example"},"children":[]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"testing-the-webhook-listener","__idx":11},"children":["Testing the Webhook Listener"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Your application is now running! But let's test it out before we try to use it to receive webhooks."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Follow these steps to test that your application is working:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Download and install ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://www.postman.com/"},"children":["Postman"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Open Postman"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Click the orange ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["New"]}," button"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Select ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Request"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Give your request a name and click ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Save"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Change the method type from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET"]}," to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST"]}," in the drop-down"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Enter ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["http://localhost:3000/track"]}," for the URL"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Select the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Body"]}," tab"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Change the type from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["none"]}," to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["raw"]}," in the drop-down box"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Change the type from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Text"]}," to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["JSON"]}," in the drop-down box"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Copy the ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"#sample-track-webhook-payload"},"children":["sample"]}," payload above and paste it into the request body"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Paste it into the body area"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Click the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Send"]}," button. You should get a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200"]}," HTTP status code and see ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["OK"]}," in the body. Your screen will look similar to this."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"img","attributes":{"src":"/assets/postman.289bf7304b5b8001d085134fe2b948a7748f649cf4f8db6f94f02ce880028bb1.afd9f624.png","alt":"Postman Example"},"children":[]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"validation","__idx":12},"children":["Validation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["We copied and pasted the sample payload to use in the test request to our web application because we wrote the endpoint to expect certain properties in the request and we didn't provide any error handling. Before using the application in production, you will need check that the message is what you expect and only attempt to access those properties if you receive the correct message. Furthermore, you can check that requests received on your endpoint are coming from ShipStation API by inspecting the headers from within your endpoint. All requests coming from ShipStation API will have the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["user-agent"]}," header set to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ShipEngine/v1"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"authentication","__idx":13},"children":["Authentication"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You may also wish to add security to your webhooks by using ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://en.wikipedia.org/wiki/Basic_access_authentication"},"children":["Basic Authentication"]},". This would require you to supply the username and password directly in the URL as specified in the example below. It would prevent any traffic from reaching your endpoint that did not include the valid username and password in the URL."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"http","header":{"controls":{"copy":{}}},"source":"POST /v1/environment/webhooks HTTP/1.1\nHost: api.shipengine.com\nAPI-Key: __YOUR_API_KEY_HERE__\nContent-Type: application/json\n\n{\n  \"url\": \"https://username:password@example.com\",\n  \"event\": \"batch\"\n}\n","lang":"http"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"getting-to-production","__idx":14},"children":["Getting to Production"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["At this point, we have created a simple web application that listens for webhook requests on the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/track"]}," endpoint. We tested it locally by sending a sample webhook payload using Postman. There are additional steps you will need to take before you can configure ShipStation API to use the endpoints exposed by this application. Namely, you will need to host your application and make it publicly accessible so that ShipStation API can reach it."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you have an existing web application that integrates with ShipStation API, then you should be familiar with the steps required to host your webhook listener application. If not, you'll probably need to start by registering a domain name for your application. You will then need to select a cloud provider, such as ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://aws.amazon.com/websites/"},"children":["AWS"]},", ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"http://microsoft.azure.com"},"children":["Azure"]},", or ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://cloud.google.com/solutions/web-hosting?utm_campaign=na-US-all-en-dr-bkws-all-all-trial-b-dr-1008076&utm_term=KW_%2Bgoogle+%2Bweb+%2Bhosting-ST_%2BGoogle+%2Bweb+%2Bhosting&gclid=EAIaIQobChMIxqzT8pSn6gIVSr7ACh0Tug8fEAAYASAAEgKfj_D_BwE&utm_content=text-ad-none-any-DEV_c-CRE_113112112207-ADGP_Hybrid+%7C+AW+SEM+%7C+BKWS+%7C+US+%7C+en+%7C+Multi+%7E+Google+Web+Hosting-KWID_43700009942847439-kwd-26940697878&utm_source=google&utm_medium=cpc"},"children":["GCP"]}," to host your application and provide DNS services for your application."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You might also use a web hosting service that handles domain registration, hosting, security, and"," ","other facets of web hosting for you."]}]},"headings":[{"value":"Creating a Webhook Listener","id":"creating-a-webhook-listener","depth":1},{"value":"Requirements","id":"requirements","depth":2},{"value":"Install Dependencies","id":"install-dependencies","depth":2},{"value":"Open a Terminal","id":"open-a-terminal","depth":3},{"value":"Import Dependencies","id":"import-dependencies","depth":2},{"value":"Create the Web Application","id":"create-the-web-application","depth":2},{"value":"Create the /track Endpoint","id":"create-the-track-endpoint","depth":3},{"value":"Sample Track Webhook Payload","id":"sample-track-webhook-payload","depth":3},{"value":"Event Timestamps","id":"event-timestamps","depth":3},{"value":"Putting It All Together","id":"putting-it-all-together","depth":2},{"value":"Running the Webhook Listener","id":"running-the-webhook-listener","depth":2},{"value":"Testing the Webhook Listener","id":"testing-the-webhook-listener","depth":2},{"value":"Validation","id":"validation","depth":2},{"value":"Authentication","id":"authentication","depth":2},{"value":"Getting to Production","id":"getting-to-production","depth":2}],"frontmatter":{"seo":{"title":"Creating a Webhook Listener"}},"lastModified":"2026-04-08T10:47:45.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/apis/shipengine/docs/tracking/webhooks","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}