Skip to content
Knowledge ERP Docs

API Reference ↗
API

Webhooks

Subscribe to events and receive signed HTTP callbacks the moment things happen in Knowledge ERP — with a delivery log for troubleshooting.

Webhooks push events to your systems in real time, so you don't have to poll the API. Register a URL, pick the events you care about, and Knowledge ERP will POST to it when they occur.

Registering a webhook #

Create a webhook with:

  • a name to identify it,
  • a URL to receive the callbacks,
  • a list of events to subscribe to,
  • an active flag to pause/resume it.

The system generates a random secret for you on creation. It is returned once — in the response to the create request, or, if you create the webhook in the app, in the message that appears when you save it — and never again on subsequent list, show, or update calls, so store it securely. It is used to verify the signature of incoming payloads.

In the app #

Under Account Administration → Integrations → Webhooks, choose New webhook, give it a name and a URL, tick the events you want (or Every event), and save. The signing secret appears in a message at the top of the page you land on. Copy it then: it is not shown on the list, the webhook's own page, or the edit form, and there is no way to display it later — if you lose it, delete the webhook and create a new one. You can rename a webhook, change its URL or events, or pause it from its edit page. The screen is for people who can change the account's settings, on a plan that includes the API.

Available events #

Events follow a resource.action naming scheme, including:

  • customer.created / .updated / .deleted
  • inventory.item.created / .updated / .deleted / .checked_out / .checked_in
  • inventory.movement.created
  • sales_order.created / .updated / .deleted
  • shipment.created / .updated (see Shipments below)
  • purchase_order.created / .updated / .received
  • customer_invoice.created / .paid, vendor_invoice.created / .paid
  • work_order.created / .updated / .status_changed
  • opportunity.created / .won / .lost / .stage_changed
  • appointment.created / .updated / .deleted
  • rental.reserved / .picked_up / .returned / .closed / .cancelled
  • survey.completed / .low_score
  • questionnaire.completed

To subscribe to every event with a single webhook, use the * wildcard as the only entry in the events list.

Shipments #

A shipment is one box of a sales order: shipment.created is sent when a box is first shipped, and shipment.updated when more of the order is shipped into the same box. Each sends the box as it now is, with every line it holds:

{
  "id": "…",
  "sales_order_id": "…",
  "tracking_number": "1Z999AA10123456784",
  "carrier": "ups",
  "shipped_at": "2026-10-20T14:05:00+00:00",
  "source": "ship_page",
  "lines": [
    { "sales_order_item_id": "…", "sku": "WIDGET-1", "quantity": "2.0000" }
  ],
  "created_at": "…",
  "updated_at": "…"
}

source is ship_page or shipstation. A line's sku is its product's code (null when there is none), and its quantity is how much of that order line is in the box.

Verifying the signature #

Every delivery is signed. Knowledge ERP computes an HMAC-SHA256 of the request body using your webhook's secret and sends it as a header, alongside the event name:

X-Webhook-Signature: sha256=<hmac>
X-Webhook-Event: sales_order.created

Recompute the HMAC on your end with the same secret and compare — reject the request if it doesn't match.

Delivery log #

Each attempt is recorded in a delivery log (event, response, status), so you can confirm what was sent and diagnose failures without guessing.

Doing it from the API #

# List your webhooks
curl "https://your-domain.com/api/v1/webhooks" \
  -H "Authorization: Bearer $TOKEN"

# Create a webhook — the response includes the signing secret this one time
curl -X POST "https://your-domain.com/api/v1/webhooks" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Order notifications",
    "url": "https://example.com/hooks/knowledge-erp",
    "events": ["sales_order.created", "customer_invoice.paid"]
  }'

The secret field is only present in this create response. Capture it now — it is never returned again. Use "events": ["*"] to receive every event.