Overview
| You want to… | Use | Direction |
|---|---|---|
| Be told when conversations, messages, tickets, verifications, comments or SMS reports happen | Outbound webhooks | Silux Chat → you |
| Let the bot look customers and orders up through a no-code workflow | Make / n8n / Zapier webhook connector | Bot → your workflow |
| Let the bot read from a simple JSON API you already have | Your own REST API connector | Bot → your API |
| Full verified account access with one-time codes you send | Account Connect | Bot → your endpoints |
Outbound webhooks
Instead of polling, register an HTTPS address and Silux Chat will POST each event to it as it happens. Every delivery is signed with a secret that only you and the platform know, retried if your endpoint is down, and recorded in a delivery log you can query.
- Signed, always —
X-Silux-Signatureis an HMAC-SHA256 of the exact request body under your endpoint’s secret. - Never slows the chat — deliveries happen in the background; a slow or failing endpoint cannot delay a customer’s reply.
- Retried, then paused — three attempts per event; an endpoint that fails 15 times in a row is deactivated with the reason recorded.
- Logged — every attempt, with your response code, so “we never received it” can be settled in seconds.
Register an endpoint
https://www.siluxchat.com/api/sandbox/webhooksX-API-KeyDespite the path, this registers the endpoint on whichever account the key belongs to: your live API key (Dashboard → API Settings) registers it on your live account; a sbx_ sandbox key registers it on your sandbox.
| Field | Type | Notes |
|---|---|---|
| url | string, required | Where to POST events. Must start with https:// (http:// is accepted but not recommended). Max 512 characters. |
| events | string[], optional | Events to receive. Omit to receive all of them. Unknown names are rejected with HTTP 400 listing the valid ones. |
| description | string, optional | Your own label, up to 255 characters. |
curl -s -X POST "https://www.siluxchat.com/api/sandbox/webhooks" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://your-system.example.com/hooks/silux","events":["comment.created","sms.delivery_report"],"description":"CRM sync"}'{
"success": true,
"webhook": {
"id": 12,
"url": "https://your-system.example.com/hooks/silux",
"events": ["comment.created", "sms.delivery_report"],
"description": "CRM sync",
"is_active": true,
"consecutive_failures": 0,
"last_error": null,
"last_delivered_at": null,
"created_at": "2026-09-26T10:00:00+00:00",
"secret": "k3v…(43 characters)…"
},
"note": "Store the secret now — it is not shown again. Verify X-Silux-Signature = HMAC-SHA256(secret, raw_body)."
}The secret is returned once
Store webhook.secret in your secrets manager straight away. It is not shown again; if it is lost, delete the endpoint and register it afresh.
Manage endpoints
| Method | Path | What it does |
|---|---|---|
| GET | /api/sandbox/webhooks | List your endpoints and every valid event name |
| POST | /api/sandbox/webhooks | Register an endpoint (returns the secret once) |
| POST | /api/sandbox/webhooks/{id}/test | Deliver a real, signed test event now and report the result |
| GET | /api/sandbox/webhooks/deliveries?limit=25 | Recent delivery attempts with status and your response code |
| DELETE | /api/sandbox/webhooks/{id} | Remove an endpoint |
KEY=YOUR_API_KEY
BASE=https://www.siluxchat.com/api/sandbox/webhooks
curl -s $BASE -H "X-API-Key: $KEY" # list endpoints + every valid event
curl -s -X POST $BASE/12/test -H "X-API-Key: $KEY" # send a real signed test event now
curl -s "$BASE/deliveries?limit=25" -H "X-API-Key: $KEY" # every attempt, with your response code
curl -s -X DELETE $BASE/12 -H "X-API-Key: $KEY" # remove endpoint 12A wrong or missing key returns HTTP 401 {"detail":"Invalid API key"}.
Events and payload
Every event below is sent. Events are raised when the change is saved, whichever way it happened — web chat, WhatsApp, your dashboard, the agent app or the API — and only after it is committed, so a change that was rolled back is never announced.
| Event | Fires when | data carries |
|---|---|---|
| conversation.started | A new conversation begins (any channel) | conversation_id, channel, customer_name, customer_phone, customer_email, status, started_at |
| message.received | A customer message arrives (not the bot’s or your team’s replies) | conversation_id, message_id, content, language, created_at |
| conversation.handed_over | The conversation is handed to your team | the conversation fields + handed_over_at, handed_over_to_user_id |
| conversation.ended | The conversation is closed (status becomes completed) | the conversation fields + ended_at |
| customer.identified | A customer passes identity verification (method "verified"), or is recognised as a returning customer (method "recognised", with customer_profile_id) | the conversation fields + method (+ customer_profile_id) |
| ticket.created | A support ticket is raised — by the bot, your team, or POST /api/chatbot-api/create-ticket | ticket_id, ticket_number, subject, description, priority, status, conversation_id, customer_name, customer_email, customer_phone, assigned_to_user_id, created_at |
| ticket.updated | A ticket’s status, priority or assignee changes (a notes-only edit does not fire) | the ticket fields + changes, e.g. {"status": {"from": "open", "to": "resolved"}} |
| comment.created | A comment is added to a conversation through the Conversations API | conversation_id, comment_id, comment, author, external_ref, identity |
| sms.delivery_report | An SMS you sent through Silux Chat settles: delivered, failed, dead or cancelled | tenant_id, message_id, client_msg_id, mobile_number, status, handled_by, attempts, last_error, sms_log_id |
- Timestamps are ISO 8601. A value the platform has not stored yet (for example a server-set time on a brand-new row) is
null. - Test and quality-check traffic (our own verification harnesses and owner test sessions) is never delivered to you.
- After you register an endpoint, events start within about 15 seconds.
The /test call always delivers a conversation.started sample (marked "test": true) so you can prove your receiver before real events arrive.
Every delivery is one compact JSON object with the same envelope; data depends on the event.
POST /hooks/silux HTTP/1.1
Content-Type: application/json
X-Silux-Event: comment.created
X-Silux-Signature: 5d41c0…(64 hex characters)
X-Silux-Delivery: 90412
User-Agent: SiluxChat-Webhooks/1.0
{"event":"comment.created","tenant_id":42,"created_at":"2026-09-26T10:00:00.123456+00:00","data":{ … }}| Header | Meaning |
|---|---|
| X-Silux-Event | The event name, so you can route before parsing |
| X-Silux-Signature | Hex HMAC-SHA256 of the raw body, keyed with your endpoint secret |
| X-Silux-Delivery | Unique delivery ID — the same across retries, so use it to de-duplicate |
| User-Agent | SiluxChat-Webhooks/1.0 |
{
"event": "conversation.started",
"tenant_id": 42,
"created_at": "2026-09-26T10:00:00.123456+00:00",
"data": {
"test": true,
"conversation_id": 0,
"channel": "whatsapp",
"identity": {"match_key": "lid:77712345678901@lid", "display_name": "Alex Morgan"}
}
}Verify the signature
Check the signature before you trust anything in the body. An unverified webhook is an open door to anyone who learns your URL.
- Compute HMAC-SHA256 over the raw request bytes, not a re-serialised object — any change in key order or whitespace changes the hash.
- Compare in constant time.
- Reply
2xxquickly (within 10 seconds) and do slow work afterwards, from a queue. - De-duplicate on
X-Silux-Delivery: a retry after a timeout can deliver the same event twice.
# Flask receiver for Silux Chat webhooks. pip install flask
import hashlib, hmac, os
from flask import Flask, abort, request
SECRET = os.environ["SILUX_WEBHOOK_SECRET"] # returned once when you registered the endpoint
app = Flask(__name__)
def valid(raw_body: bytes, signature: str) -> bool:
expected = hmac.new(SECRET.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature or "")
@app.post("/hooks/silux")
def silux_hook():
raw = request.get_data() # the RAW bytes — never a re-serialised object
if not valid(raw, request.headers.get("X-Silux-Signature", "")):
abort(401)
event = request.get_json()
delivery = request.headers.get("X-Silux-Delivery") # unique per delivery: use it to de-duplicate
print(delivery, event["event"], event["data"]) # hand off to a queue; reply fast
return "", 204Retries and the delivery log
| Behaviour | Value |
|---|---|
| Timeout per attempt | 10 seconds |
| Attempts per event | 3, with back-off between them |
| Counts as delivered | Any 2xx response |
| Retried | Any non-2xx response, timeout or connection error |
| Automatic deactivation | After 15 consecutive failed deliveries; last_error records why |
| Delivery log | GET /api/sandbox/webhooks/deliveries — status, attempts, your response code, error, timestamps |
To re-enable a deactivated endpoint, fix the cause, delete it and register it again, then use the /test call to confirm.
Test in the sandbox first
Your sandbox is a separate shadow account linked to yours, with its own sbx_ key and seeded sample conversations. It has no WhatsApp connection, so it physically cannot message a real person. Register your webhook there, fire test events, and switch to the live key only when your receiver is proven.
| Method | Path | What it does |
|---|---|---|
| GET | /api/sandbox | Whether a sandbox exists, and its key |
| POST | /api/sandbox | Create and seed it (call with your live key; returns the sbx_ key) |
| POST | /api/sandbox/reset | Wipe and re-seed |
| DELETE | /api/sandbox | Remove it entirely |
The complete platform reference — chat, conversations, tickets, SMS — is in the API documentation (sign in to view).
Make, n8n & Zapier: lookups through a workflow
The Make / n8n / Zapier webhook connector lets the bot find customers and read their orders through a workflow you build — useful when your data is in a spreadsheet, a database your workflow tool can reach, or several systems at once. The platform sends a POST with an action field to your catch-hook address; your workflow replies with JSON.
find_customer
{"action": "find_customer", "email": "jane@example.com", "phone": "07700900123",
"full_name": "Jane Smith", "postcode": "SW1A 1AA"}{"customers": [
{"id": "123", "name": "Jane Smith", "email": "jane@example.com",
"phone": "07700900123", "postcode": "SW1A 1AA"}
]}orders
{"action": "orders", "customer_id": "123", "email": "jane@example.com"}{"orders": [
{"order_number": "1001", "placed": "2026-09-20", "status": "dispatched",
"total": "39.99", "tracking": "TT123456789GB", "courier": "Royal Mail"}
]}- Reply
{"customers": []}or{"orders": []}when there is nothing — never an error for “not found”. - Only return the customer who matches the email or phone sent. The bot still checks the name and one more detail before sending a one-time code to the contact you return.
- Answer within a few seconds; each call is allowed about 15 seconds.
- The unguessable hook address is the credential. Keep it private, and create a new hook if it ever leaks.
Zapier catch hooks can't answer lookups
A Zapier “Catch Hook” always replies with a fixed acknowledgement, not with your data, so the bot would never see a customer. Use Make or n8n for lookups. Zapier remains a good fit for receiving outbound webhooks.
Build it in Make
- 1Create a scenario starting with Webhooks → Custom webhook. Copy its address (for example
hook.eu1.make.com/abc123…). - 2Add a Router with two routes filtered on
action:find_customerandorders. - 3On each route, search your data (Google Sheets, Airtable, a database, an HTTP call…) by
email/phoneor bycustomer_id. - 4End each route with Webhooks → Webhook response, status 200, header
Content-Type: application/json, and a body shaped exactly like the replies above. - 5Turn the scenario on (scheduling: immediately).
- 6In Dashboard → Integrations → Connectors, choose Make / n8n / Zapier webhook, paste the hook address without
https://, and press Connect & test.
Build it in n8n
- 1Add a Webhook node: HTTP method
POST, Respond set to Using ‘Respond to Webhook’ node. Use the production URL. - 2Add a Switch node on
{{ $json.body.action }}with outputs forfind_customerandorders. - 3Look the data up on each branch, then shape it with a Code or Set node into
{"customers": [...]}or{"orders": [...]}. - 4Finish each branch with Respond to Webhook → Respond With: JSON.
- 5Activate the workflow, then connect it on the dashboard as above.
How the connection test works
When you connect, the platform makes real calls with a made-up customer (connection-test@example.com). An empty list is a pass — it proves the workflow answers. Details in Troubleshooting.
Your own REST API
If you already have a JSON API with customer and order lists, the Your own REST API connector reads it directly with a bearer token. You enter the API base address (without https://), a customer lookup path, an orders path and the token; the platform calls:
GET https://api.yourcompany.com/v1/customers?email=jane%40example.com&phone=07700900123
Authorization: Bearer <your token>
Accept: application/json
200
{"data": [{"id": "123", "name": "Jane Smith", "email": "jane@example.com",
"phone": "07700900123", "postcode": "SW1A 1AA"}]}GET https://api.yourcompany.com/v1/orders?customer_id=123
Authorization: Bearer <your token>
Accept: application/json
200
{"data": [{"order_number": "1001", "placed": "2026-09-20", "status": "dispatched",
"total": "39.99", "tracking": "TT123456789GB", "courier": "Royal Mail"}]}| Field | Accepted source fields |
|---|---|
| id, name, email, phone, postcode | same names, inside data[] |
| order_number | order_number, else id |
| placed | placed, else created_at (shortened to a date) |
| status, total, tracking, courier | same names |
- Filter by the email or phone query parameter — return only that customer, never a whole list.
- Serve over HTTPS with a token that can only read.
- Return {"data": []} for no match, not a 404 or 500.
The REST connector reads; verification still happens on our side (name plus one more detail, then a one-time code to the contact your API returns). If you would rather send the code yourself and control sessions, build Account Connect instead.
Which route to choose
| Account Connect | REST connector | Make / n8n | Outbound webhooks | |
|---|---|---|---|---|
| Code on your side | Five endpoints | Two GET endpoints | None (visual workflow) | One receiver |
| Who sends the one-time code | You | Silux Chat | Silux Chat | — |
| Data the bot can read | Account, orders, invoices | Customers, orders | Customers, orders | — |
| Best for | Bespoke platforms | Existing JSON APIs | Sheets, Airtable, mixed systems | Ticket, conversation, comment and SMS-status sync |
Get help
Talk to our integration team
Not sure which route fits, or want a second pair of eyes on a workflow? Email support@siluxcall.co.uk with the tools you use and what the bot should answer.
Need help with your integration?
Our integration team can review your endpoints, help you choose the right connector and check a test run with you. Tell us which system you use and what you would like the bot to answer.