ZappBot

Back to the home page

API and webhooks

Written from the code. Not yet tested against a live receiver. Questions: contact us.

Overview

What ZappBot offers to connect your own tools, written from the code: one outbound webhook (a new lead), a CSV download of your leads, and one inbound call to add a lead from your own tool. There is no other API: you cannot read chats or change settings from outside.

1. Lead webhook (outbound)

Plans: Growth and Pro, and group contracts (every integration switched on). The ZappBot operator sets it up for you on request (one URL and an optional secret). Starter and trial: off.

URL rules: https only, no user:password, no private or local address (localhost, 10.x, 192.168.x, 172.16-31.x, 169.254.x, .local, .internal). Otherwise nothing is sent.

When: each time a lead alert fires, that is, when a visitor leaves a contact or the chat raises a request or hand-off. It also fires while alerts are snoozed.

Request: POST with Content-Type: application/json and User-Agent: ZappBot-Webhook/1. The timeout is 3.5 seconds. Redirects are not followed.

Answer: any 2xx means delivered. Anything else, a timeout or a network error means failed.

Retries: a failed delivery is queued and retried every 10 minutes, at most 3 times, at least 5 minutes apart, and dropped after 2 hours. Your receiver must accept the same lead twice (see the duplicate key below).

Delivery log and resend: the owner sees the last 20 real sends in the dashboard settings (time, delivered or failed with the reason, lead number, try number; no contact or body is kept there) and can tap Send again on a failed row. A resent lead has the same signature and "resent": true.

Body of a lead

Read the fields you need and ignore the rest, because fields can be added. Every lead has event, tenant, at, lead_id, kind, summary and contact. tags appears only when the business's tag rules matched.

{ "event": "lead", "tenant": "Le Phan Hotel", "at": "2026-11-02T00:12:09.000Z",
  "lead_id": 42, "kind": "interested", "summary": "Interested in Deluxe Room, 3 nights from 12 Dec",
  "contact": "0903 555 121", "tags": ["Wedding"] }

The operator's test button sends event "test" with kind "test" and contact null. Answer 2xx and store nothing.

Verifying the signature

Only when a secret is set. The header X-ZappBot-Signature is the lowercase hex HMAC-SHA256 of the raw request body, using your secret. Compute it over the raw bytes you received, before parsing the JSON, and compare in constant time. Node example:

import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody, header, secret) {
  const want = createHmac("sha256", secret).update(rawBody).digest("hex");
  return typeof header === "string" && header.length === want.length && timingSafeEqual(Buffer.from(header), Buffer.from(want));
}

The signature has no timestamp, so a captured request can be replayed: dedupe on the key below. Tools that cannot read headers (Google Apps Script) cannot check it: put a long random key in the URL instead.

Duplicate key: lead_id + kind + the first 60 characters of summary. A retry sends the identical body, so the same key means the same alert.

2. Leads CSV (download)

GET /api/d/leads?csv=1&t=<your dashboard link token>. Newest first, up to 1000 rows, UTF-8 with BOM, every cell quoted. A cell that starts with = + - or @ gets a leading apostrophe. Columns: Reference, Created, Status, Kind, Contact, Summary, and Tags when the business uses tag rules. A read-only staff link cannot download it.

The token is a password: keep it out of shared scripts and logs. If it leaks, ask the operator to rotate it.

3. Send leads in (inbound)

Key: the owner makes it in the dashboard settings, under Send leads in from your own tool. It is shown once, only its SHA-256 is kept, a new key replaces the old one, and the owner can turn it off.

Call: POST /api/v1/lead with the header Authorization: Bearer lpi_<id>_<hex> and a JSON body with name, phone, email, note and source. The lead goes to the key's own business. Input is checked and cut to length.

curl -X POST https://YOUR-DOMAIN/api/v1/lead \
  -H "Authorization: Bearer lpi_<id>_<hex>" \
  -H "Content-Type: application/json" \
  -d '{ "name": "An", "phone": "0903 555 121", "email": "", "note": "Asked about a room", "source": "landing page" }'

Answers: 200 saved. The same 401 for any bad, old or switched-off key. 429 after 60 calls an hour.

Alert and webhook: an API lead alerts the owner on the channels already set up (snooze, quiet hours and the daily cap apply) and fires the lead webhook with "via": "api", plus source and note. A failing e-mail or hook never fails the call.

Not available

Reading data back through an API (leads, chats, settings), updating or deleting leads, webhooks for chat messages, bookings or payments, signing timestamps, several keys, per-key scopes and per-event subscriptions.

Data location, retention and outside services