Skip to content
BolderBolder Developers
Esc
↑↓navigate↵open⌘Jpreview
On this page

Receive order webhooks

Subscribe a URL to order events, answer the activation ping, protect the endpoint with basic auth and handle deliveries and failures.

Goal: your server learns when an order is created, updated, closed or shipped, without polling. You need a public HTTPS endpoint and a token with hub.write plus read access to the topic’s resource (orders.read for order topics).

1. Answer the activation ping

When you subscribe, Bolder first sends one POST to your URL with an X-Hook-Ping header and a body whose topic is activation. The subscription only becomes active if your endpoint answers 2xx with the same value in an X-Hook-Pong header. Deploy the handler before you subscribe:

import express from 'express'

const app = express()

app.post('/webhooks/bolder', express.json(), (req, res) => {
  const ping = req.get('X-Hook-Ping')
  if (ping) return res.set('X-Hook-Pong', ping).sendStatus(200)

  const event = req.body
  console.log(event.topic, event.item_id)
  res.sendStatus(200)
})

app.listen(3000)

2. Subscribe

import { createBolder } from 'bolder-api-client'

const bolder = await createBolder({ token: process.env.BOLDER_TOKEN, shopId: 1234 })

const hook = await bolder.webhooks.create({
  topic: 'order',
  url: 'https://myapp.com/webhooks/bolder',
  auth: { type: 'basic', username: 'bolder', password: process.env.WEBHOOK_PASSWORD }
})

This calls POST /v2/webhooks?shop_id=1234; POST /v2/sellers/{seller_id}/webhooks does the same. Subscriptions belong to the seller (the account), not to a single shop.

By default a subscription doesn’t receive events caused by the app that created it (an order your integration created through the API, for example). Pass notify_origin: true to receive those too.

Topics are hierarchical: order receives every order event, order.updated receives all its subtopics (order.updated.closed, order.updated.shipped, …), and order.created only that one. The full list of topics is in the Webhooks reference.

3. Verify deliveries

Deliveries are not signed. To make sure a request comes from Bolder, subscribe with auth.type: basic (as above): every delivery, the activation ping included, then carries those credentials in the Authorization header. Check them before anything else (register this middleware before the route):

const expected = 'Basic ' + Buffer.from(`bolder:${process.env.WEBHOOK_PASSWORD}`).toString('base64')

app.use('/webhooks/bolder', (req, res, next) => {
  if (req.get('Authorization') !== expected) return res.sendStatus(401)
  next()
})

Use HTTPS so the credentials are not sent in clear text.

4. Handle the payload

Each delivery is a JSON POST with the event:

Field
topic The event topic, singular (order.updated.closed).
item_type, item_id The record the event is about (order, 123).
changes Changed fields as {field: [old, new]}.
payload Extra data for the event, if any.
created_on When the event happened.
shop_id, shop_subdomain The shop it happened in.
_embedded.item The record itself, as the v2 API returns it.

Answer 2xx quickly (requests time out after 30 seconds) and do slow work in a background job. Deliveries can arrive more than once and out of order: use item_id and created_on to keep your records consistent, or fetch the order again with GET /v2/orders/{id}.

Failures

  • A failed delivery is retried up to 10 times over about 24 hours.
  • Answering 410 Gone disables the subscription at once.
  • After 100 failed attempts in a row (retries included) the subscription is marked as failed and stops receiving events. Timeouts and 502, 512 and 523 errors don’t count towards that limit.
  • When a subscription stops, a webhook.disabled event is sent and the merchant gets an alert.
  • Fix the endpoint, then call PUT /v2/webhooks/{id}/reactivate for a failed subscription. One disabled by a 410 has to be subscribed again.
  • GET /v2/webhooks/{id}/history lists the recent events of the subscription’s topic.

Next

Was this page helpful?