Skip to main content
Webhooks let your app react to changes in the merchant’s ERP without polling. You declare the events you care about in your manifest, and JTL posts a signed HTTP request to your endpoint each time one of them occurs. Deliveries carry a key, not a record. When an item changes, you receive the key of that item and fetch its current state through the GraphQL API. This keeps payloads small and means your app always acts on current data rather than a snapshot that may already be stale.

Delivery Flow

Each delivery moves from the merchant’s ERP to your endpoint and back into the API.

Declaring Subscriptions

Webhook subscriptions live under capabilities.erp.webhooks in your app manifest.
Each subscription pairs a set of topics with one endpoint, so you can route different domains to different handlers. The full set of available topics is on the Webhook Topics page. Topics are matched in full, so list each one individually. Edit the manifest through the Manifest Editor in the partner portal, or update app.json and register it from the CLI. Subscriptions take effect once the updated manifest is saved.

Endpoint Requirements

Your webhook endpoint is a public HTTP route that JTL posts to directly.
  • Accept POST with a JSON body.
  • Serve over HTTPS with a certificate that validates.
  • Respond within a few seconds.
  • Return 2xx on success. Any other status is treated as a failed delivery and retried.
Choose a path that is not guessable from your app’s public URL, and treat it as a credential. A route at /webhooks/a8f3c1d92e/items is meaningfully harder to find than /webhooks/items.

The Event Payload

Every delivery is a CloudEvents envelope wrapping a small data object.
The envelope identifies the event and the tenant it belongs to. The data object describes what changed and who changed it.
The tenant identifier in the payload is tenantid, all lowercase. The GraphQL API expects it as the X-Tenant-ID header.

Delivery Headers

Alongside the JSON body, each delivery carries three headers. Read them by their lowercase names, since HTTP header names are case-insensitive and frameworks differ on the casing they expose.
svix-timestamp is useful for logging and for measuring the delay between an event occurring and your handler running. A widening gap between it and your own clock points at a backlog worth investigating.

Receiving a Delivery

Parse the payload, check the tenant, acknowledge, then process.
Keep the handler short. Queue the work and return, rather than calling the GraphQL API inside the request. A handler that waits on an upstream call risks a timeout, and a timeout turns a delivered event into a retried one.

Fetching the Changed Entity

Use objectKey from the payload and tenantid as the tenant header.
A 200 response can still contain an errors array, so check the response body rather than the status code alone. Your handler needs an access token for these calls, which it obtains through the client credentials grant described in Authentication & Login. Deletion events are the exception. The entity is already gone by the time the delivery arrives, so act on objectKey directly rather than fetching.

What’s Next?

Best Practices

Production patterns for error handling, security, and performance.

Webhook Topics

The full set of ERP events your app can subscribe to.

Authentication & Login

Obtain the access token your handler needs to fetch entities.

Webhooks & Events

How ERP webhooks compare to AppBridge events and SCX polling.