Kanorio can notify your system in real-time when specific events occur on your website: order payments, shipment updates, form submissions, sending domain issues, quota alerts, or traffic milestones. By connecting to Zapier, Make, n8n, or your own custom code, you can automatically write data to spreadsheets, forward notifications to team chat groups, create support tickets, or sync with your ERP or CRM.
You can configure this in the Technical Settings section of your website console under the "Integrations" tab. Webhooks are a Business plan feature; only the website owner and admins can add or manage them. Other members cannot view URLs, secret keys, or delivery logs.
You can add up to 5 Webhooks per website.
After expanding each endpoint, you can configure "Subscribed Events":
Currently supported events (see "Event Reference" at the end for field details):
| Event | Trigger | Event Code |
|---|---|---|
| Order Paid | When a buyer completes payment | ec.order.paid |
| Shipment Update | When a package is shipped, arrives at store, delivered, or exception occurs | ec.shipment.statusChanged |
| Form Submission | When a visitor submits a form, includes full questions and answers | customers.form.submitted |
| Sending Domain Degraded | When your sending domain verification fails | customers.sendingDomain.degraded |
| Messaging Quota Alert | When quota is exhausted, auto-topped up, limit reached, or payment failed | customers.messagingQuota.alert |
| Traffic Milestone | When cumulative site views cross 100 or 500 | site.analytics.milestone |
| Integration Paused | When a Webhook is paused due to consecutive failures (will not send to that endpoint) | customers.teamDestination.disabled |
| Test Event | When "Send Test" is clicked | team.test |
Webhook delivery depends solely on these subscription settings. While individual scenario panels in Automations may show "N endpoints subscribed" or "Not connected," the actual control is here, not in Automations.
Webhooks do not replace your notification emails. After adding a Webhook, notification emails sent to the owner (e.g., new orders and form submissions) will still be sent. Team emails for shipment updates are disabled by default. To toggle team emails, please configure them in "Automations".
Expand any endpoint to see "Recent Deliveries": event, result, HTTP status, and time. Expand each entry to view attempt counts, latency, next retry time, and error reasons. Notifications within the last 7 days can be "Viewed" to see the payload or "Resent" to try again. Resending uses the same delivery ID, which the receiving end can use for deduplication.
"Send Test" does not count toward failure limits, so feel free to use it for troubleshooting.
Kanorio sends Webhooks from fixed IPs. If your receiving end only allows whitelisted sources, please add all three of the following IPv4 addresses (initial delivery, tests, and manual resends come from the first two; automatic retries come from the third):
| Purpose | IP |
|---|---|
| Initial delivery, test, manual resend | <MAIN_EGRESS_IP_1> |
| Initial delivery, test, manual resend | <MAIN_EGRESS_IP_2> |
| Automatic retry | 168.144.105.51 |
Whitelisting is an extra layer of protection and cannot replace signature verification: please always verify the X-Kanorio-Signature (see "For Engineers" below). If IPs need to change, we will announce it here and keep both old and new IPs running for a period before removing the old ones.
No. Webhooks do not send emails through Kanorio and do not count toward your email quota.
Delivery to endpoints will be paused. Settings and keys will be preserved, and the list will be marked as "Exceeds Plan". It will resume immediately after upgrading back to the Business plan.
Kanorio retries when the receiving end responds too slowly or errors out; you may also have manually resent it. Please use X-Kanorio-Delivery (or the id in the JSON) to determine if it has already been processed.
Currently, you can forward notifications via Zapier or Make: use a Webhook to receive the Kanorio notification, then send it to a Slack channel or LINE.
Webhooks send order and form data to systems outside of Kanorio, so URLs and signing secrets are visible only to the owner and admins. Other members can only see the endpoint name and status.
There is no separate sandbox. Use "Send Test" to verify the connection and "View Content" to see the actual event payload. During development, you can point the endpoint to a service like webhook.site to observe the format.
Each notification is a POST request with Content-Type: application/json and the following headers:
| Header | Content |
|---|---|
X-Kanorio-Event | Event code, same as event in JSON |
X-Kanorio-Delivery | Delivery ID, same as id in JSON; remains unchanged for retries and resends |
X-Kanorio-Signature | Signature, format t=timestamp,v1=signature; two v1 values may exist during key rotation |
User-Agent | Kanorio-Notifications/1.0 |
Any 2xx response from the receiving end is considered a success; the response body is ignored. Please respond within 8 seconds. If longer processing is needed, return 200 first and process asynchronously. Kanorio does not follow redirects; 3xx responses are treated as failures.
All event JSONs share the same shell, so the receiving end only needs to be configured once:
{ "version": "1", "id": "3f9c2a…", "event": "ec.order.paid", "occurredAt": "2026-09-29T12:00:00.000Z", "websiteId": "cmk…", "title": "New order #1024, NT$1,280", "summary": "'My Store' received a paid order.", "fields": [{ "label": "Order ID", "value": "#1024" }], "data": { "…": "Varies by event, see reference below" }, "actionUrl": "https://app.kanorio.com/store/transactions?tx=…" }
| Field | Type | Description |
|---|---|---|
version | string | Currently "1". Adding fields does not change the version; removing or renaming fields triggers a version bump, which will be announced here |
id | string | Delivery ID (32 chars), same as X-Kanorio-Delivery. Remains unchanged for retries/resends; use for deduplication |
event | string | Event code, see table above |
occurredAt | string | Event time, ISO 8601 (UTC) |
websiteId | string | Website ID. An endpoint only receives events for the website that created it |
title, summary | string | A single line of title and summary localized to the site's language, ready for chat forwarding |
fields | array | Array of { label, value } for human-readable details (order ID, amount, form answers). Labels change with site language; use data for programmatic logic |
data | object | Raw event data, language-independent. See below for event-specific fields |
actionUrl | string | null | Link back to the Kanorio dashboard to view this data |
Below are the data fields for each event. "Nullable" means the field may be null.
ec.order.paid Order PaidSent when a buyer completes payment; once per order.
| Field | Type | Nullable | Description |
|---|---|---|---|
orderId | string | Order ID, same as ?tx= in the dashboard URL | |
orderNumber | number | ✓ | Human-readable order number (e.g., 1024); very old orders may not have this |
amount | number | Total paid (items + shipping), in minor units of the currency (ISO 4217): NT$1,280 is 128000, US$12.80 is 1280, JPY ¥1,280 is 1280. To convert to display amount, divide by 10 to the power of the currency's decimal places | |
currency | string | ISO 4217 uppercase, e.g., TWD, USD | |
itemCount | number | Number of items | |
customerName | string | ✓ | Buyer name |
customerEmail | string | ✓ | Buyer email |
ec.shipment.statusChanged Shipment UpdateSent when a shipment enters shipped, arrived, delivered, or exception status; once for each status change per shipment.
| Field | Type | Nullable | Description |
|---|---|---|---|
orderId | string | Order ID | |
orderNumber | number | ✓ | Order number |
shipmentId | string | Shipment ID; one order may have multiple shipments | |
status | string | IN_TRANSIT (Shipped), AT_STORE (Arrived), DELIVERED (Delivered), EXCEPTION (Exception) | |
carrier | string | Logistics service code, e.g., PAYUNI_LOGISTICS, MANUAL | |
trackingNumber | string | ✓ | Tracking number |
storeName | string | ✓ | Convenience store name; null for home delivery |
customers.form.submitted Form SubmissionSent when a visitor submits a form and passes spam checks; once per submission.
| Field | Type | Nullable | Description |
|---|---|---|---|
formId | string | Form ID | |
formName | string | Form name | |
submissionId | string | Submission ID | |
submitter | object | { name, email, phone }, each nullable; auto-identified from form fields | |
answers | array | Array of objects per question (see table below) |
answers[] items:
| Field | Type | Description |
|---|---|---|
fieldId | string | Question ID, fixed per form; use this instead of label |
type | string | Type: short_text, long_text, email, phone, name, choice, date, time, rating, file |
label | string | Question text |
value | string | string[] | number | null | Raw answer. Single choice is string, multiple is string array, rating is number, date/time is string (YYYY-MM-DD, HH:mm), file is file ID(s); null if empty |
display | string | Human-readable string, same as fields; file name for file questions |
customers.sendingDomain.degraded Sending Domain DegradedSent when a verified custom sending domain fails verification; once per day per domain.
| Field | Type | Description |
|---|---|---|
domain | string | Degraded domain |
customers.messagingQuota.alert Messaging Quota AlertSent when quota status changes.
| Field | Type | Description |
|---|---|---|
reason | string | exhausted, auto_upgraded, auto_upgrade_limit, payment_failed |
quota | number | Current period quota |
levels | number | Current auto-upgrade level |
periodStart | string | Period start time, ISO 8601 |
site.analytics.milestone Traffic MilestoneSent when cumulative site views cross a milestone; once per milestone.
| Field | Type | Description |
|---|---|---|
milestone | number | 100 or 500 |
customers.teamDestination.disabled Integration PausedSent when a Webhook endpoint is automatically paused (consecutive failures or 410 response). Sent only to other healthy endpoints.
| Field | Type | Description |
|---|---|---|
destinationId | string | Paused endpoint ID |
failures | number | Consecutive failure count |
lastError | string | Last error, e.g., HTTP 503, timeout |
team.test Test EventSent when "Send Test" is clicked; once, no retries, not logged in delivery history.
Verification steps:
t and all v1 from X-Kanorio-Signature.t, a period, and the raw request body (unparsed, re-serialized), e.g., 1790000000.{"version":"1",…}.v1. Accept only if they match.Node.js example:
import { createHmac, timingSafeEqual } from "node:crypto"; function verify(rawBody, header, secret) { const parts = header.split(",").map((part) => part.split("=")); const t = parts.find(([key]) => key === "t")?.[1]; const signatures = parts.filter(([key]) => key === "v1").map(([, value]) => value); if (!t || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex"); return signatures.some( (v1) => v1.length === expected.length && timingSafeEqual(Buffer.from(expected), Buffer.from(v1)) ); }
Services like Zapier, Make, and n8n usually don't support custom verification logic; their receiving URLs contain random paths, so verification is generally not required.
Secrets can be viewed by expanding the endpoint in the integration list and clicking "Show". If you suspect a leak, click "Generate New Secret":
v1s); either one matching is sufficient.