- Start here
- Getting Started
- Development Store
- Build an app
-
App Development
-
Webhooks
- Guides by use case
- Popular APIs
-
Logistic Apps
- Reference
- Authentication
- API access scopes
- API call limit
- Response status codes
- API 3.0 reference ↗
- API 4.0 reference ↗
- EasyStore App Store ↗
Webhooks
Webhooks let your app react as soon as something happens in a store, such as a new order, a product change or a stock update. Instead of calling the API every few minutes to check, you subscribe to a topic, and EasyStore sends an HTTP POST to your URL each time that event happens.
Common uses: sending new orders to an ERP or accounting system, keeping stock in sync, updating a CRM when customers change, and cleaning up when a merchant uninstalls your app.
Subscribe to a topic
Create a webhook with the Webhooks API, usually right after your app is installed:
curl --request POST 'https://{shop}/api/3.0/webhooks.json' \
--header 'EasyStore-Access-Token: {access_token}' \
--header 'Content-Type: application/json' \
--data '{
"webhook": {
"topic": "order/create",
"url": "https://apps.yourapps.com/webhooks/orders"
}
}'
- No extra scope is needed to manage webhooks.
- Each app has one URL per topic. Subscribing to the same topic again updates its URL.
- Check the topic spelling. Topics aren't validated: a misspelled topic is saved but never fires.
- Webhooks are removed when the merchant uninstalls your app. Subscribe again after a reinstall.
Topics
| Topic | Sent when |
|---|---|
| App | |
app/uninstall |
The merchant uninstalled your app. Sent immediately, before your app's webhooks, script tags and snippets are removed. Body: {"success":true} |
| Orders | |
order/create |
A new order was placed, from any sales channel |
order/update |
An order was changed |
order/paid |
An order was fully paid |
order/partially_paid |
An order received a partial payment |
order/cancel |
An order was cancelled |
| Fulfillments and refunds | |
fulfillment/create |
A fulfillment was created for an order |
fulfillment/update |
A fulfillment changed, for example its tracking or status |
fulfillment/cancel |
A fulfillment was cancelled |
refund/create |
A refund was created |
refund/cancel |
A refund was cancelled |
| Returns | |
order_returns/created |
A return was requested |
order_returns/accepted |
A return was accepted |
order_returns/updated |
A return was updated |
order_returns/completed |
A return was completed |
order_returns/cancelled |
A return was cancelled |
| Products and inventory | |
product/create |
A product was created |
product/update |
A product or its variants changed, including stock changes |
product/delete |
A product was deleted |
inventory_level/inventory_quantity_update |
Stock changed for a variant at a location. Includes variant_id, location_id, inventory_quantity, adjustment (SET, INCREASE or DECREASE) and inventory_level_updated_at |
| Customers | |
customer/create |
A customer was created |
customer/update |
A customer was changed |
customer/delete |
A customer was deleted |
| Store | |
store/update |
Store settings changed |
location/create |
A location was created |
location/update |
A location was changed |
location/delete |
A location was deleted |
currency/update |
A store currency was added or changed |
currency/delete |
A store currency was removed |
purchase_order/create |
A purchase order was created |
purchase_order/update |
A purchase order was changed |
Webhooks are only sent while your app is enabled in the store.
Receive a webhook
EasyStore sends an HTTP POST with a JSON body and these headers:
| Header | Value |
|---|---|
Easystore-Topic |
The topic, for example order/create |
Easystore-Shop-Domain |
The store's domain, for example example.easy.co. Use it to find the store in your database. |
Easystore-Hmac-Sha256 |
The signature to verify |
EasyStore-Webhook-Context |
On product/update and inventory topics only: what caused the change, for example
order/create or inventory_level/update |
EasyStore-Webhook-Trigger-By-App |
On product/update and inventory topics only: the handle of the app whose order caused a stock
change. Use it to ignore changes your own app caused. |
The body is the resource that changed, as JSON. Its shape depends on the topic: order topics send
{"order": {...}}, while product topics send the product's fields directly. Webhook payloads can differ
slightly from the same resource in the API, so if you need every field, fetch the record with the API using the ID
from the webhook.
Respond and retries
- Respond with
200 OK, ideally within 10 seconds. Any other status, including201,204and redirects, counts as a failure. - Do the work later: save the webhook to a queue, respond 200, then process it. See Best practices.
- Retries: a failed webhook is retried several times, for up to 2 days.
- Duplicates and order: a webhook can arrive more than once, and webhooks can arrive out of
order. Make your handler idempotent, and compare timestamps such as
updated_at(orinventory_level_updated_at) before overwriting newer data. - Webhooks can be missed. Run a periodic reconciliation with the API as a safety net. See Keeping data in sync.
Verify a webhook
The Easystore-Hmac-Sha256 header is a hex-encoded HMAC-SHA256 of the raw request
body, using your app's Client secret as the key. Compute it and compare it with the header; if they match, the
webhook came from EasyStore and wasn't changed.
<?php
$secret = getenv('CLIENT_SECRET');
$data = file_get_contents('php://input');
$hmac_header = $_SERVER['HTTP_EASYSTORE_HMAC_SHA256'];
$calculated_hmac = hash_hmac('sha256', $data, $secret);
$verified = hash_equals($calculated_hmac, $hmac_header);
For a test value to check your code, see Webhook HMAC verification.
Test your webhooks
Webhooks are sent from EasyStore's servers, so your URL must be publicly reachable. You can't use:
localhost- "Fake" domains like
www.example.com - EasyStore domains (
easy.co,easystore.coandwww.easystore.co)
Use HTTPS. During development, a tunnelling tool such as ngrok or Cloudflare Tunnel can expose your local server. Then trigger events in your development store, for example by placing a test order.
Also see Limitations and Best practices.