---
title: "Receive print job updates from BizPrint with webhooks"
description: "Set up a webhook URL so BizPrint tells your server when a station reports that a print job is processing, done, or failed."
url: https://docs.getbizprint.com/developers/webhooks/
section: "Developers"
last_updated: 2026-11-12
---

# Receive print job updates from BizPrint with webhooks

Section: Developers

> Set up a webhook URL so BizPrint tells your server when a station reports that a print job is processing, done, or failed.

This page shows you how to receive [webhooks](https://docs.getbizprint.com/start-here/glossary/#webhook) from BizPrint: messages that BizPrint sends to your server when a [station](https://docs.getbizprint.com/start-here/glossary/#station) reports progress on a [print job](https://docs.getbizprint.com/start-here/glossary/#print-job). It is for developers who create print jobs with the BizPrint Print API, for stations that run either the [BizPrint desktop app](https://docs.getbizprint.com/start-here/glossary/#bizprint-desktop-app) or the [BizPrint Android app](https://docs.getbizprint.com/start-here/glossary/#bizprint-android-app).

## Before you start

- Create an [application](https://docs.getbizprint.com/start-here/glossary/#application) that uses the **REST API**. See [Get API keys for the BizPrint Print API](https://docs.getbizprint.com/developers/get-api-keys/). Applications that use the **Legacy API** cannot send webhooks.
- Have the application’s [Public Key and Secret Key](https://docs.getbizprint.com/start-here/glossary/#public-key-and-secret-key). Keep the Secret Key on your server only. Never put it in browser code, an app you hand out, or a support message.
- Have a web address on your server that can receive a JSON `POST` request from the internet, for example `https://example.com/bizprint/webhook`. Use the exact address of your handler, not one that redirects.

## What BizPrint sends

BizPrint sends each webhook as a `POST` request with a JSON body. Every body has these fields:

| Field       | What it is                                                                                                                                               |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`      | The event: `test-connect` or `print-job-status-update`.                                                                                                  |
| `_id`       | A unique ID for this event. If BizPrint sends the same event again, the `_id` stays the same.                                                            |
| `publicKey` | The Public Key of your application.                                                                                                                      |
| `time`      | When the request was signed, in Unix seconds.                                                                                                            |
| `hash`      | The signature. See [Check that a webhook came from BizPrint](https://docs.getbizprint.com/developers/webhooks/#check-that-a-webhook-came-from-bizprint). |

### test-connect

BizPrint sends `test-connect` to check that your address answers. This happens when you save a new **Webhook URL** and when you click **Perform test connection** in the [BizPrint dashboard](https://docs.getbizprint.com/start-here/glossary/#bizprint-dashboard). The body has only the common fields:

```json
{
  "_id": "EVENT_ID",
  "publicKey": "YOUR_PUBLIC_KEY",
  "type": "test-connect",
  "time": 1767225600,
  "hash": "SIGNATURE"
}
```

Your handler must accept `test-connect` and answer it with success, even though there is nothing to do.

### print-job-status-update

BizPrint sends `print-job-status-update` when the station reports a new [print job status](https://docs.getbizprint.com/start-here/glossary/#print-job-status) for a job your application created. The body also has a `job` object with the job as it is when the webhook is sent: `id`, `status`, `description`, `url`, `printOption`, `printerId`, `createdAt` and `updatedAt`. These are the same fields you get when you look up a job with the Print API. See [Check print job status with the BizPrint Print API](https://docs.getbizprint.com/developers/job-status/).

```json
{
  "_id": "EVENT_ID",
  "publicKey": "YOUR_PUBLIC_KEY",
  "type": "print-job-status-update",
  "job": {
    "id": "YOUR_JOB_ID",
    "status": "done",
    "description": "Order 1001",
    "url": "https://example.com/receipts/1001.pdf",
    "printOption": {},
    "printerId": "YOUR_PRINTER_ID",
    "createdAt": "2026-01-01T09:00:00.000Z",
    "updatedAt": "2026-01-01T09:00:05.000Z"
  },
  "time": 1767258005,
  "hash": "SIGNATURE"
}
```

In a real webhook, `job.id` and `job.printerId` are numbers. The example uses placeholders.

The station reports these statuses, so these are the ones you receive:

- `processing`: the station started the job.
- `done`: the station reported that it printed the job.
- `failed`: the station could not print the job.

You do not get a webhook for `pending`, `connecting-to-printer` or `archived`, because the station does not report those. If a station is offline and never picks the job up, no webhook arrives. To catch these jobs, look them up with the Print API. See [Check print job status with the BizPrint Print API](https://docs.getbizprint.com/developers/job-status/).

## Set your webhook URL

1. Deploy your webhook handler first, so it can answer straight away.
2. Sign in to the BizPrint dashboard.
3. Click **Applications** in the menu.
4. Click your application.
5. Type your address in **Webhook URL**.
6. Click **Update**. BizPrint sends a `test-connect` webhook to the new address once, straight away.
7. Click **Perform test connection** under **Webhook Info** to check again at any time. The dashboard shows **Successfully connected with webhook server** when your handler answered with success.

For more about the test button and its messages, see [Test the webhook connection for a BizPrint application](https://docs.getbizprint.com/manage/test-webhook/).

## Check that a webhook came from BizPrint

BizPrint signs each webhook with your application’s Secret Key, the same way you sign a `POST` request to the Print API. See [Sign BizPrint Print API requests](https://docs.getbizprint.com/developers/authentication/). To check a webhook:

1. Take the parsed JSON body.
2. Remove the `hash` field. Keep every other field, in the order it arrived.
3. Turn the rest back into compact JSON text.
4. Add a colon and your Secret Key to the end of that text.
5. Make a SHA-256 hash of the result, written as lowercase hexadecimal.
6. Compare it with the `hash` you received. If they differ, ignore the request.
7. Check that `publicKey` is your application’s Public Key.
8. Check that `time` is recent. BizPrint signs every delivery attempt again, so `time` is always close to the moment it was sent. Choose a window that suits you, for example five minutes.

In Node.js, `JSON.stringify` on the parsed body gives the same text BizPrint signed. In other languages, make sure your JSON output has no extra spaces, keeps the field order, and does not escape `/` or non-English characters.

To check one webhook by hand while you develop, you can paste its body into the BizPrint signing tool. See [Sign and check BizPrint Print API requests with the signing tool](https://docs.getbizprint.com/developers/signing-tool/).

## Example handler in Node.js

This example uses Express. It checks the signature, saves the event, and answers straight away. Load your keys from your server’s environment, never from your code repository.

```js
import crypto from 'node:crypto';
import express from 'express';

// Set these on your server to YOUR_PUBLIC_KEY and YOUR_SECRET_KEY.
const PUBLIC_KEY = process.env.BIZPRINT_PUBLIC_KEY;
const SECRET_KEY = process.env.BIZPRINT_SECRET_KEY;
const MAX_AGE_SECONDS = 300;

function isFromBizPrint(body) {
  const { hash, ...signed } = body;
  if (typeof hash !== 'string' || signed.publicKey !== PUBLIC_KEY) return false;

  const expected = crypto
    .createHash('sha256')
    .update(`${JSON.stringify(signed)}:${SECRET_KEY}`)
    .digest('hex');

  const received = Buffer.from(hash, 'hex');
  const wanted = Buffer.from(expected, 'hex');
  if (received.length !== wanted.length || !crypto.timingSafeEqual(received, wanted)) {
    return false;
  }

  const age = Math.abs(Math.floor(Date.now() / 1000) - Number(signed.time));
  return age <= MAX_AGE_SECONDS;
}

const app = express();

app.post('/bizprint/webhook', express.json(), async (req, res) => {
  if (!isFromBizPrint(req.body)) {
    return res.status(401).end();
  }

  // Save the event quickly (for example, in your database), keyed by _id,
  // so a repeated delivery of the same event is stored only once.
  await saveEventOnce(req.body._id, req.body);

  // Answer straight away. Do slow work later, from your saved events.
  res.status(200).json({ received: true });
});

app.listen(3000);
```

`saveEventOnce` stands for your own code that stores the event and skips an `_id` it has already stored.

If your check rejects real webhooks, for example because the handler uses the wrong Secret Key, BizPrint counts every delivery as failed, and webhooks can stop. Use the keys of the same application that creates your print jobs.

## Answer quickly with success

- **Answer with a 2xx status**, such as `200`. You can send `{"received": true}` as the body. Any other status, or no answer, counts as a failed delivery.
- **Answer fast.** Save the event, answer, and then do the work, such as updating an order or sending an email.
- **Expect repeats.** If a delivery fails, BizPrint tries the same event again with the same `_id`. Use `_id` so you handle each event once.
- **Expect more than one update per job.** A job usually gets `processing` and then `done` or `failed`. A station can report the same status twice, and a reprint from the dashboard sends new updates for the same job.
- **Do not rely on the order.** A repeated delivery can arrive after a newer update. Compare `job.updatedAt`, or look the job up with the Print API when the order matters.

## When BizPrint stops sending webhooks

If a webhook still fails after BizPrint’s last attempt, BizPrint marks the application’s webhook as failing and **stops sending all `print-job-status-update` webhooks for that application**. Updates from that moment on are not sent, and they are not sent later either.

The application page in the dashboard then shows **Error connection to webhook server.** under **Webhook Info**, with the error your server gave, if any.

Webhooks start again only when one of these happens:

- You save a different **Webhook URL** for the application.
- A `test-connect` webhook succeeds, for example after you click **Perform test connection**.

To recover:

1. Fix your handler so it answers `test-connect` with a 2xx status.
2. Click **Perform test connection** on the application page in the dashboard.
3. Check that the dashboard shows **Successfully connected with webhook server**.
4. Look up the jobs you created while webhooks were stopped, to catch the updates you missed. See [Check print job status with the BizPrint Print API](https://docs.getbizprint.com/developers/job-status/).

For more help, see [BizPrint webhook test connection fails](https://docs.getbizprint.com/troubleshooting/webhook-connection/).

## Do not print twice because of a webhook

A `failed` update, or an update that never arrives, does not prove that nothing printed. Do not create a new print job automatically in response. Every new job prints again. Before anyone prints again:

1. Look at the paper at the [printer](https://docs.getbizprint.com/start-here/glossary/#printer) for that document.
2. Check the job’s status in the BizPrint dashboard, or with the Print API.
3. Let the station finish what it is printing, whatever status the job shows.
4. Do nothing more while the job is still `pending`, `connecting-to-printer` or `processing`.
5. Reprint the existing job from the dashboard only if it really did not print. The reprint goes to the printer the job was first sent to. See [Reprint a print job from the BizPrint dashboard](https://docs.getbizprint.com/print/reprint-a-job/).

## Related pages

- [Check print job status with the BizPrint Print API](https://docs.getbizprint.com/developers/job-status/)
- [Test the webhook connection for a BizPrint application](https://docs.getbizprint.com/manage/test-webhook/)
- [Sign BizPrint Print API requests](https://docs.getbizprint.com/developers/authentication/)
- [BizPrint webhook test connection fails](https://docs.getbizprint.com/troubleshooting/webhook-connection/)

Need help? Contact BizPrint support at <https://getbizprint.com/support/>.
