Developer documentation

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
BasicsTypeDescription
Base URLurlYour deployment origin, e.g. https://chatbot.wpistic.cloud — every path below is relative to it.
Content-Typeheaderapplication/json for request bodies; responses are JSON.
TenancyconceptEvery request is scoped to the signed-in user's organization. There is no cross-org access.
Quick check
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 /login or the SSO flow. All /api/* routes (except webhooks and the public consent form) return 401 without 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_SECRET as a bearer token or query parameter.

Errors & rate limits#

Errors share one shape: an HTTP status plus a human-readable message.

Error shape
{ "error": "Provide either `recipients` (preferred) or `phone_numbers` — must be a non-empty array" }
Status
StatusTypeDescription
400statusValidation failed — the message says which field.
401statusMissing or expired session (or bad webhook signature).
403statusAuthenticated but not allowed — e.g. SMS blocked by the consent gate.
429statusPer-user rate limit hit. Message sends and broadcast starts have independent budgets; retry after the window resets.
500statusUnexpected 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.

GET/api/whatsapp/config

Returns the current provider, masked credentials, webhook URL, and SMS compliance settings.

POST/api/whatsapp/config

Create or replace the provider configuration. Send the field set that matches your provider:

Body — provider: meta
Body — provider: metaTypeRequiredDescription
provider"meta"requiredMeta WhatsApp Cloud API.
phone_number_idstringrequiredFrom Meta Business settings.
access_tokenstringrequiredSystem-user access token — stored encrypted.
waba_idstringoptionalWhatsApp Business Account id (enables template sync).
verify_tokenstringoptionalValue echoed during webhook verification.
Body — provider: twilio
Body — provider: twilioTypeRequiredDescription
provider"twilio"requiredWhatsApp via Twilio.
twilio_account_sidstringrequiredAccount SID (AC…).
twilio_auth_tokenstringrequiredAuth token — stored encrypted.
twilio_whatsapp_numberstringoptionalSender number; or use a Messaging Service.
twilio_messaging_service_sidstringoptionalMessaging Service SID (MG…).
Body — provider: jasmin (SMS)
Body — provider: jasmin (SMS)TypeRequiredDescription
provider"jasmin"requiredSelf-hosted Jasmin SMS gateway.
jasmin_base_urlstringrequiredGateway base URL.
jasmin_usernamestringrequiredGateway user.
jasmin_passwordstringrequiredGateway password — stored encrypted.
jasmin_default_senderstringoptionalDefault sender id / number.
PATCH/api/whatsapp/config

Update SMS compliance settings without touching credentials.

Parameters
ParameterTypeDescription
sms_quiet_hours_startnumber | nullHour 0–23; null disables quiet hours.
sms_quiet_hours_endnumber | nullHour 0–23; set together with start.
sms_timezonestringIANA zone, e.g. "America/New_York".
a2p_brand_idstringA2P 10DLC brand registration id.
a2p_campaign_idstringA2P 10DLC campaign id.
a2p_statusstringRegistration status you track.
DELETE/api/whatsapp/config

Remove 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.

POST/api/whatsapp/send
Parameters
ParameterTypeRequiredDescription
conversation_iduuidrequiredTarget conversation.
message_type"text" | "template" | mediarequiredWhat you are sending.
content_textstringoptionalBody text — required when message_type is text.
template_namestringoptionalApproved template — required when message_type is template.
template_paramsstring[]optionalPositional template variables.
media_urlstringoptionalPublic URL for media messages.
message_category"transactional" | "support" | "marketing"optionalSMS policy category; marketing is held to the strictest consent rules.
cURL
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.

POST/api/whatsapp/broadcast
Parameters
ParameterTypeRequiredDescription
recipients{ phone, params?, contact_id? }[]requiredPreferred shape — per-recipient variables. (Legacy phone_numbers: string[] is still accepted.)
template_namestringoptionalRequired for meta/twilio sends.
template_languagestringoptionalTemplate locale, e.g. "en_US".
message_textstringoptionalRequired for SMS sends; supports {{1}}, {{2}} placeholders.
cURL
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.

POST/api/whatsapp/templates/sync

Pulls 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.

GET/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.

GET/api/leads

Returns 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.

GET/api/tochat/widgets

List 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.

POST/api/tochat/widgets
Parameters
ParameterTypeRequiredDescription
namestringrequiredWidget name.
activebooleanoptionalWhether the widget is live.
colorstringoptionalHex brand color, e.g. #27d974.
rightposbooleanoptionaltrue = right side, false = left side.
isopenbooleanoptionalAuto-open the chat window on load.
widgetMessagestringoptionalGreeting shown in the chat bubble.
buttonMessagestringoptionalSend-button label.
offlineMessagestringoptionalShown when no agent is online.
iconUrlstringoptionalLauncher 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.

GET/api/tochat/widgets/{id}

Fetch a single widget owned by the caller's org.

PUT/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.

DELETE/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.

GET/api/tochat/operators

List every agent across the signed-in org's widgets.

POST/api/tochat/operators
Parameters
ParameterTypeRequiredDescription
namestringrequiredAgent display name.
numberstringrequiredWhatsApp number, e.g. 34627524218.
businessstringrequiredThe widget id this agent attaches to — must belong to the caller's org.
poststringoptionalJob title, e.g. "Sales".
messagestringoptionalGreeting shown before the chat opens.
iconUrlstringoptionalAgent avatar URL.
chatformbooleanoptionalCollect a lead-capture form before opening WhatsApp.
activateDirectlyChatbooleanoptionalSkip 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.

GET/api/tochat/operators/{id}

Fetch a single agent. Ownership is verified one level removed — via its parent widget's userClient tag.

PUT/api/tochat/operators/{id}

Update an agent, optionally re-attaching it to a different widget (re-verified the same way).

DELETE/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.

GET/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.

POST/api/tochat/faq-groups
Parameters
ParameterTypeRequiredDescription
titlestringrequiredGroup heading, e.g. "Frequently asked questions".
operatorIdstringrequiredThe agent this group belongs to — must belong to the caller's org.
faqs{ question, answer }[]requiredAt least one question/answer pair.
GET/api/tochat/faq-groups/{id}

Fetch a single FAQ group.

PUT/api/tochat/faq-groups/{id}

Replace a group's title and questions. The agent it belongs to can't be changed via this route.

DELETE/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.

GET/api/tochat/booking-configs?operatorId={id}

List the booking configs for one agent. operatorId is required.

POST/api/tochat/booking-configs
Parameters
ParameterTypeRequiredDescription
operatorIdstringrequiredThe agent this config belongs to.
startDate / endDatestring (YYYY-MM-DD)requiredBooking window.
durationnumberrequiredSlot length in minutes.
timezonestringrequiredIANA zone, e.g. "Europe/Madrid".
bookingTimes{ day, availableFrom, availableUntil }[]requiredWeekly availability — a day can have zero, one, or multiple windows (e.g. a morning/afternoon split).
breakTimenumberoptionalBuffer between slots, in minutes. Default 0.
availablePlacePerSlotnumberoptionalConcurrent bookings per slot. Default 1.
allowedHourUntilBookingnumberoptionalMinimum notice, in hours. Default 0.
blockingDaysstring[] (YYYY-MM-DD)optionalDates fully blocked regardless of the weekly schedule.
sendReminder / sendReminder48 / cancelBookingInReminderbooleanoptionalReminder behavior. Default true.
GET/api/tochat/booking-configs/{id}

Fetch a single booking config.

PUT/api/tochat/booking-configs/{id}

Replace a config's schedule and settings (same body shape as create, minus operatorId).

DELETE/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.

GET/api/automations

List the organization's automations.

POST/api/automations

Create an automation from a flow definition.

GET/api/automations/{id}

Fetch one automation, including its flow definition.

PATCH/api/automations/{id}

Update the definition, name, or enabled state.

DELETE/api/automations/{id}

Delete the automation and its pending executions.

POST/api/automations/{id}/duplicate

Clone an automation (disabled by default).

POST/api/automations/engine

Run the execution engine for triggered flows.

GET/api/automations/cron

Scheduler entry point — call it every minute from your cron host with the AUTOMATION_CRON_SECRET. It wakes executions whose wait steps expired.

Cron
* * * * * 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.

GET/api/ai/knowledge-base

List knowledge-base entries.

POST/api/ai/knowledge-base
Parameters
ParameterTypeRequiredDescription
titlestringrequiredEntry label.
contentstringrequiredThe text that gets embedded and retrieved.
DELETE/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.

GET/api/whatsapp/webhook

Meta verification handshake — echoes hub.challenge when hub.verify_token matches your configured verify token.

POST/api/whatsapp/webhook

Meta inbound events (messages, statuses). The body is verified against X-Hub-Signature-256 using META_APP_SECRET.

POST/api/whatsapp/twilio-webhook

Twilio inbound WhatsApp messages — request authenticity is validated via X-Twilio-Signature with TWILIO_AUTH_TOKEN.

POST/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.

POST/api/sms/preflight
Parameters
ParameterTypeRequiredDescription
contact_idsuuid[]requiredContacts you intend to message.
message_category"transactional" | "support" | "marketing"requiredPolicy category to evaluate.

Returns, per contact, whether a send would be allowed and why not (no consent, opted out, quiet hours).

GET/api/sms/consent?contact_id={id}

Read a contact's consent record.

POST/api/sms/consent

Record consent collected off-platform (paper form, verbal confirmation) with source and note.

POST/api/sms/consent/public

Public 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.

GET/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.

Token format
base64url(JSON payload) + "." + base64url(HMAC_SHA256(encodedPayload, SSO_SHARED_SECRET))
Claim
ClaimTypeRequiredDescription
substringrequiredStable subject, e.g. wp-42 — orgs are keyed on it.
emailstringrequiredMember email; the Supabase user is created from it.
namestringoptionalDisplay name.
planstringrequiredPlan slug: free | starter | growth | agency.
license_keystringoptionalLicenseistic key, stored on the org.
license_statusstringoptionalactive | inactive | expired | suspended.
agent_limit / widget_limit / domain_limit / contact_limitnumberoptionalEntitlement caps carried from the plan.
white_labelbooleanoptionalWhether the plan includes white-labelling.
allowed_domainsstring[]optionalDomains the license may run on.
iat / expnumberrequiredUnix seconds; default max skew is 300s.

Environment variables#

Self-hosting checklist — copy .env.local.example and fill these in.

Variable
VariableTypeRequiredDescription
NEXT_PUBLIC_SUPABASE_URLstringrequiredSupabase project URL.
NEXT_PUBLIC_SUPABASE_ANON_KEYstringrequiredSupabase anon key.
SUPABASE_SERVICE_ROLE_KEYstringrequiredService-role key for server-side provisioning.
ENCRYPTION_KEYhex(64)requiredAES-256-GCM key for provider credentials.
META_APP_SECRETstringrequiredVerifies Meta webhook signatures.
NEXT_PUBLIC_SITE_URLstringoptionalPublic origin used in generated links.
AUTOMATION_CRON_SECRETstringoptionalProtects the automation cron endpoint.
TWILIO_AUTH_TOKENstringoptionalValidates Twilio webhook signatures.
SMS_WEBHOOK_SECRETstringoptionalToken for the SMS gateway webhook.
SSO_SHARED_SECRETstringoptionalMust equal the WordPress bridge's secret.
SSO_MAX_SKEW_SECONDSnumberoptionalToken freshness window (default 300).
CLOUDFLARE_ACCOUNT_ID / CLOUDFLARE_API_TOKENstringoptionalEnable Workers-AI embeddings + drafts.
CHATBOTISTIC_API_URL / CHATBOTISTIC_API_KEYstringoptionalEnable the leads integration.
TOCHAT_API_EMAIL / TOCHAT_API_PASSWORDstringoptionalMaster Tochat.be account — enables Widget Studio (widgets/agents/bookings/campaigns).
TOCHAT_API_BASEstringoptionalOverride the Tochat.be API origin (default https://services.tochat.be).