Chatbotistic API
Everything the dashboard does runs through this JSON API — the same endpoints are available to your own scripts and integrations on your deployment.
Overview#
The API is served by your CRM deployment itself. All endpoints accept and return JSON unless noted otherwise.
| Basics | Type | Description |
|---|---|---|
| Base URL | url | Your deployment origin, e.g. https://chatbot.wpistic.cloud — every path below is relative to it. |
| Content-Type | header | application/json for request bodies; responses are JSON. |
| Tenancy | concept | Every request is scoped to the signed-in user's organization. There is no cross-org access. |
curl -s https://chatbot.wpistic.cloud/api/whatsapp/config \
-H 'Cookie: <your session cookie>'
# → { "configured": true, "provider": "meta", ... }Authentication#
The API uses the same Supabase session cookie as the dashboard. Sign in through the app (or via SSO) and reuse the cookie; there are no separate API keys.
- Session cookie — set by
/loginor the SSO flow. All/api/*routes (except webhooks and the public consent form) return401without it. - Webhook secrets — inbound webhooks authenticate with provider-specific verification instead: Meta uses an HMAC-SHA256 signature header, Twilio uses
X-Twilio-Signature, and the SMS gateway uses a?token=query secret. See Webhooks. - Cron secret — the automation cron endpoint requires
AUTOMATION_CRON_SECRETas a bearer token or query parameter.
Errors & rate limits#
Errors share one shape: an HTTP status plus a human-readable message.
{ "error": "Provide either `recipients` (preferred) or `phone_numbers` — must be a non-empty array" }| Status | Type | Description |
|---|---|---|
| 400 | status | Validation failed — the message says which field. |
| 401 | status | Missing or expired session (or bad webhook signature). |
| 403 | status | Authenticated but not allowed — e.g. SMS blocked by the consent gate. |
| 429 | status | Per-user rate limit hit. Message sends and broadcast starts have independent budgets; retry after the window resets. |
| 500 | status | Unexpected server error — check deployment logs. |
Provider configuration#
One provider config per organization. Credentials are encrypted with AES-256-GCM before they touch the database; reads return masked values.
/api/whatsapp/configReturns the current provider, masked credentials, webhook URL, and SMS compliance settings.
/api/whatsapp/configCreate or replace the provider configuration. Send the field set that matches your provider:
| Body — provider: meta | Type | Required | Description |
|---|---|---|---|
| provider | "meta" | required | Meta WhatsApp Cloud API. |
| phone_number_id | string | required | From Meta Business settings. |
| access_token | string | required | System-user access token — stored encrypted. |
| waba_id | string | optional | WhatsApp Business Account id (enables template sync). |
| verify_token | string | optional | Value echoed during webhook verification. |
| Body — provider: twilio | Type | Required | Description |
|---|---|---|---|
| provider | "twilio" | required | WhatsApp via Twilio. |
| twilio_account_sid | string | required | Account SID (AC…). |
| twilio_auth_token | string | required | Auth token — stored encrypted. |
| twilio_whatsapp_number | string | optional | Sender number; or use a Messaging Service. |
| twilio_messaging_service_sid | string | optional | Messaging Service SID (MG…). |
| Body — provider: jasmin (SMS) | Type | Required | Description |
|---|---|---|---|
| provider | "jasmin" | required | Self-hosted Jasmin SMS gateway. |
| jasmin_base_url | string | required | Gateway base URL. |
| jasmin_username | string | required | Gateway user. |
| jasmin_password | string | required | Gateway password — stored encrypted. |
| jasmin_default_sender | string | optional | Default sender id / number. |
/api/whatsapp/configUpdate SMS compliance settings without touching credentials.
| Parameter | Type | Description |
|---|---|---|
| sms_quiet_hours_start | number | null | Hour 0–23; null disables quiet hours. |
| sms_quiet_hours_end | number | null | Hour 0–23; set together with start. |
| sms_timezone | string | IANA zone, e.g. "America/New_York". |
| a2p_brand_id | string | A2P 10DLC brand registration id. |
| a2p_campaign_id | string | A2P 10DLC campaign id. |
| a2p_status | string | Registration status you track. |
/api/whatsapp/configRemove the provider configuration for the organization.
Messages#
Send a single message into an existing conversation. SMS sends pass through the compliance gate (consent + quiet hours) before hitting the gateway.
/api/whatsapp/send| Parameter | Type | Required | Description |
|---|---|---|---|
| conversation_id | uuid | required | Target conversation. |
| message_type | "text" | "template" | media | required | What you are sending. |
| content_text | string | optional | Body text — required when message_type is text. |
| template_name | string | optional | Approved template — required when message_type is template. |
| template_params | string[] | optional | Positional template variables. |
| media_url | string | optional | Public URL for media messages. |
| message_category | "transactional" | "support" | "marketing" | optional | SMS policy category; marketing is held to the strictest consent rules. |
curl -X POST https://chatbot.wpistic.cloud/api/whatsapp/send \
-H 'Content-Type: application/json' \
-H 'Cookie: <session>' \
-d '{
"conversation_id": "3f6f4f1e-…",
"message_type": "text",
"content_text": "Your order shipped 🎉"
}'Broadcasts#
Start a campaign to many recipients. On Meta/Twilio this sends an approved template; on the SMS provider it sends free-form text with {{1}}-style substitution. Every SMS recipient is consent-checked first.
/api/whatsapp/broadcast| Parameter | Type | Required | Description |
|---|---|---|---|
| recipients | { phone, params?, contact_id? }[] | required | Preferred shape — per-recipient variables. (Legacy phone_numbers: string[] is still accepted.) |
| template_name | string | optional | Required for meta/twilio sends. |
| template_language | string | optional | Template locale, e.g. "en_US". |
| message_text | string | optional | Required for SMS sends; supports {{1}}, {{2}} placeholders. |
curl -X POST https://chatbot.wpistic.cloud/api/whatsapp/broadcast \
-H 'Content-Type: application/json' \
-H 'Cookie: <session>' \
-d '{
"template_name": "spring_sale",
"template_language": "en_US",
"recipients": [
{ "phone": "+15551234567", "params": ["Maya", "20%"] },
{ "phone": "+15559876543", "params": ["Leo", "20%"] }
]
}'Templates#
Message templates are managed in Meta Business Manager; the CRM keeps a synced local copy for pickers and broadcasts.
/api/whatsapp/templates/syncPulls the approved template list from Meta for the configured WABA and upserts it locally. Requires waba_id in the provider config.
Media#
Inbound WhatsApp media is referenced by id; this endpoint proxies the download with your credentials so the browser never sees them.
/api/whatsapp/media/{mediaId}Streams the media file for an inbound message attachment. Session-scoped.
Leads#
Pull chatbot-captured leads from your connected Chatbotistic account into the CRM.
/api/leadsReturns the lead list from the configured Chatbotistic API (CHATBOTISTIC_API_URL + CHATBOTISTIC_API_KEY). Use it to review and convert leads into contacts.
Tochat widgets#
Org-scoped proxy to the Tochat.be widget API — the first slice of Widget Studio. Requires the master Tochat.be account (TOCHAT_API_EMAIL / TOCHAT_API_PASSWORD) to be configured; this is a separate integration from the Leads sync above, which only reads the lead-export feed.
/api/tochat/widgetsList the signed-in org's widgets. The response also includes embedBaseUrl — the Tochat.be API origin — so the UI can build embed script URLs ({embedBaseUrl}/widget/{id}/load.js) without hardcoding it.
/api/tochat/widgets| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | required | Widget name. |
| active | boolean | optional | Whether the widget is live. |
| color | string | optional | Hex brand color, e.g. #27d974. |
| rightpos | boolean | optional | true = right side, false = left side. |
| isopen | boolean | optional | Auto-open the chat window on load. |
| widgetMessage | string | optional | Greeting shown in the chat bubble. |
| buttonMessage | string | optional | Send-button label. |
| offlineMessage | string | optional | Shown when no agent is online. |
| iconUrl | string | optional | Launcher icon URL. |
Additional Tochat widget fields (banners, landing colors, translations, targeting rules) are passed through as-is — see the Tochat.be API reference for the full schema. The Widget Studio UI at /widgets currently manages the field set above.
/api/tochat/widgets/{id}Fetch a single widget owned by the caller's org.
/api/tochat/widgets/{id}Replace a widget's fields (same body shape as create). Re-verifies the widget's userClienttag matches the caller's org before writing — Tochat.be is a single shared master account across every org on this platform, so widget ids alone don't prove ownership.
/api/tochat/widgets/{id}Delete a widget, after the same ownership check.
Tochat agents#
WhatsApp operators (agents) — each one attaches to exactly one widget. Same Tochat.be integration as widgets above; requires TOCHAT_API_EMAIL / TOCHAT_API_PASSWORD.
/api/tochat/operatorsList every agent across the signed-in org's widgets.
/api/tochat/operators| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | required | Agent display name. |
| number | string | required | WhatsApp number, e.g. 34627524218. |
| business | string | required | The widget id this agent attaches to — must belong to the caller's org. |
| post | string | optional | Job title, e.g. "Sales". |
| message | string | optional | Greeting shown before the chat opens. |
| iconUrl | string | optional | Agent avatar URL. |
| chatform | boolean | optional | Collect a lead-capture form before opening WhatsApp. |
| activateDirectlyChat | boolean | optional | Skip the agent picker when this is the preferred agent. |
business is a plain widget id here, not the raw Tochat.be IRI — this route translates between them and verifies the target widget belongs to your org before attaching the agent.
/api/tochat/operators/{id}Fetch a single agent. Ownership is verified one level removed — via its parent widget's userClient tag.
/api/tochat/operators/{id}Update an agent, optionally re-attaching it to a different widget (re-verified the same way).
/api/tochat/operators/{id}Delete an agent, after the same ownership check.
Tochat FAQ groups#
Frequently asked questions an agent answers automatically. Every FAQ group belongs to exactly one agent, which belongs to exactly one widget.
/api/tochat/faq-groups?operatorId={id}List the FAQ groups for one agent. operatorId is required — ownership is verified two levels removed (agent → widget → userClient) before anything is returned.
/api/tochat/faq-groups| Parameter | Type | Required | Description |
|---|---|---|---|
| title | string | required | Group heading, e.g. "Frequently asked questions". |
| operatorId | string | required | The agent this group belongs to — must belong to the caller's org. |
| faqs | { question, answer }[] | required | At least one question/answer pair. |
/api/tochat/faq-groups/{id}Fetch a single FAQ group.
/api/tochat/faq-groups/{id}Replace a group's title and questions. The agent it belongs to can't be changed via this route.
/api/tochat/faq-groups/{id}Delete a FAQ group, after the same ownership check.
Tochat booking configs#
Appointment-scheduling rules for one agent: booking window, slot length, weekly availability, and reminders. Same ownership model as FAQ groups — every config belongs to exactly one agent.
/api/tochat/booking-configs?operatorId={id}List the booking configs for one agent. operatorId is required.
/api/tochat/booking-configs| Parameter | Type | Required | Description |
|---|---|---|---|
| operatorId | string | required | The agent this config belongs to. |
| startDate / endDate | string (YYYY-MM-DD) | required | Booking window. |
| duration | number | required | Slot length in minutes. |
| timezone | string | required | IANA zone, e.g. "Europe/Madrid". |
| bookingTimes | { day, availableFrom, availableUntil }[] | required | Weekly availability — a day can have zero, one, or multiple windows (e.g. a morning/afternoon split). |
| breakTime | number | optional | Buffer between slots, in minutes. Default 0. |
| availablePlacePerSlot | number | optional | Concurrent bookings per slot. Default 1. |
| allowedHourUntilBooking | number | optional | Minimum notice, in hours. Default 0. |
| blockingDays | string[] (YYYY-MM-DD) | optional | Dates fully blocked regardless of the weekly schedule. |
| sendReminder / sendReminder48 / cancelBookingInReminder | boolean | optional | Reminder behavior. Default true. |
/api/tochat/booking-configs/{id}Fetch a single booking config.
/api/tochat/booking-configs/{id}Replace a config's schedule and settings (same body shape as create, minus operatorId).
/api/tochat/booking-configs/{id}Delete a booking config, after the same ownership check.
Automations#
Automations are JSON flow definitions (trigger + steps). The engine executes runs; a cron pinger drains time-based wait steps.
/api/automationsList the organization's automations.
/api/automationsCreate an automation from a flow definition.
/api/automations/{id}Fetch one automation, including its flow definition.
/api/automations/{id}Update the definition, name, or enabled state.
/api/automations/{id}Delete the automation and its pending executions.
/api/automations/{id}/duplicateClone an automation (disabled by default).
/api/automations/engineRun the execution engine for triggered flows.
/api/automations/cronScheduler entry point — call it every minute from your cron host with the AUTOMATION_CRON_SECRET. It wakes executions whose wait steps expired.
* * * * * curl -s "https://chatbot.wpistic.cloud/api/automations/cron?secret=$AUTOMATION_CRON_SECRET"AI knowledge base#
RAG store behind the AI reply drafts — entries are embedded with Cloudflare Workers AI on insert.
/api/ai/knowledge-baseList knowledge-base entries.
/api/ai/knowledge-base| Parameter | Type | Required | Description |
|---|---|---|---|
| title | string | required | Entry label. |
| content | string | required | The text that gets embedded and retrieved. |
/api/ai/knowledge-base?id={id}Remove an entry and its embedding.
Webhooks#
Point your provider at these URLs to receive inbound messages and delivery status. Each one authenticates differently — never disable the checks.
/api/whatsapp/webhookMeta verification handshake — echoes hub.challenge when hub.verify_token matches your configured verify token.
/api/whatsapp/webhookMeta inbound events (messages, statuses). The body is verified against X-Hub-Signature-256 using META_APP_SECRET.
/api/whatsapp/twilio-webhookTwilio inbound WhatsApp messages — request authenticity is validated via X-Twilio-Signature with TWILIO_AUTH_TOKEN.
/api/sms/webhook?token={secret}Jasmin SMS gateway callbacks: inbound messages (MO) and delivery receipts (DLR). Authenticated by the token query matching SMS_WEBHOOK_SECRET. STOP/HELP keywords update consent automatically.
SMS compliance#
The send gate: express consent per contact, message categories, quiet hours, and an audit log. These endpoints let you check and record consent explicitly.
/api/sms/preflight| Parameter | Type | Required | Description |
|---|---|---|---|
| contact_ids | uuid[] | required | Contacts you intend to message. |
| message_category | "transactional" | "support" | "marketing" | required | Policy category to evaluate. |
Returns, per contact, whether a send would be allowed and why not (no consent, opted out, quiet hours).
/api/sms/consent?contact_id={id}Read a contact's consent record.
/api/sms/consentRecord consent collected off-platform (paper form, verbal confirmation) with source and note.
/api/sms/consent/publicPublic endpoint behind the hosted opt-in form (/sms-optin/{key}) — submissions are stored as web-form express consent.
SSO login#
Sell memberships on WordPress (Memberistic + Licenseistic) and let members land in the CRM already signed in. The bridge redirects the browser here with a short-lived, HMAC-signed token.
/api/sso/login?token={sso-token}Verifies the token, provisions the user and organization from the claims, then redirects into /dashboard with a session. Failures land on /login with an error message.
base64url(JSON payload) + "." + base64url(HMAC_SHA256(encodedPayload, SSO_SHARED_SECRET))| Claim | Type | Required | Description |
|---|---|---|---|
| sub | string | required | Stable subject, e.g. wp-42 — orgs are keyed on it. |
| string | required | Member email; the Supabase user is created from it. | |
| name | string | optional | Display name. |
| plan | string | required | Plan slug: free | starter | growth | agency. |
| license_key | string | optional | Licenseistic key, stored on the org. |
| license_status | string | optional | active | inactive | expired | suspended. |
| agent_limit / widget_limit / domain_limit / contact_limit | number | optional | Entitlement caps carried from the plan. |
| white_label | boolean | optional | Whether the plan includes white-labelling. |
| allowed_domains | string[] | optional | Domains the license may run on. |
| iat / exp | number | required | Unix seconds; default max skew is 300s. |
Environment variables#
Self-hosting checklist — copy .env.local.example and fill these in.
| Variable | Type | Required | Description |
|---|---|---|---|
| NEXT_PUBLIC_SUPABASE_URL | string | required | Supabase project URL. |
| NEXT_PUBLIC_SUPABASE_ANON_KEY | string | required | Supabase anon key. |
| SUPABASE_SERVICE_ROLE_KEY | string | required | Service-role key for server-side provisioning. |
| ENCRYPTION_KEY | hex(64) | required | AES-256-GCM key for provider credentials. |
| META_APP_SECRET | string | required | Verifies Meta webhook signatures. |
| NEXT_PUBLIC_SITE_URL | string | optional | Public origin used in generated links. |
| AUTOMATION_CRON_SECRET | string | optional | Protects the automation cron endpoint. |
| TWILIO_AUTH_TOKEN | string | optional | Validates Twilio webhook signatures. |
| SMS_WEBHOOK_SECRET | string | optional | Token for the SMS gateway webhook. |
| SSO_SHARED_SECRET | string | optional | Must equal the WordPress bridge's secret. |
| SSO_MAX_SKEW_SECONDS | number | optional | Token freshness window (default 300). |
| CLOUDFLARE_ACCOUNT_ID / CLOUDFLARE_API_TOKEN | string | optional | Enable Workers-AI embeddings + drafts. |
| CHATBOTISTIC_API_URL / CHATBOTISTIC_API_KEY | string | optional | Enable the leads integration. |
| TOCHAT_API_EMAIL / TOCHAT_API_PASSWORD | string | optional | Master Tochat.be account — enables Widget Studio (widgets/agents/bookings/campaigns). |
| TOCHAT_API_BASE | string | optional | Override the Tochat.be API origin (default https://services.tochat.be). |