Contents

REST API - Plugixa Chat

Everything the admin app does, it does over REST, under the namespace plugixa-chat/v1. Anything the screens can do is therefore scriptable.

The base address is /wp-json/plugixa-chat/v1/.

This is the API the plugin’s own screens and widget use. It is not a separately versioned public API, and the plugin makes no promise that routes, parameters or response shapes stay the same between versions. Pin the plugin version you build against, and test after updating.

Authentication

Most routes need a logged-in user. The plugin adds no authentication of its own for them. It relies on whatever WordPress provides, and then checks the capabilities of the user WordPress identified.

Caller How
The admin app The logged-in cookie plus a REST nonce in the X-WP-Nonce header, the normal WordPress way
A script or another system Any method WordPress accepts for REST requests, such as an application password for an account that holds the capabilities below
The widget on your site The public routes, with a page token

Permissions

Each route requires one of the plugin’s capabilities.

Called in the tables below Capability required
View plugixa_chat_view
Create plugixa_chat_create
Edit plugixa_chat_edit
Delete plugixa_chat_delete
Export plugixa_chat_export
Settings plugixa_chat_manage_settings

Two more capabilities exist:

Capability Meaning
plugixa_chat_manage May open the admin app. It is never granted directly: anyone who holds any other capability in this list has it automatically
plugixa_chat_reply Defined for answering visitor conversations. No route in 1.0.0 requires it

Administrators hold all of them from activation, and an account that can manage options is always treated as holding them. No other role is given any. They are ordinary WordPress capabilities, so a role editor can grant them to a role or to one person.

A caller without the capability gets the error code plugixa_chat_forbidden with status 401 when not logged in, or 403.

The verdict can be changed with the plugixa_chat_user_can filter. See Hooks reference.

Response shape

A successful response wraps its payload:

{
  "success": true,
  "data": { },
  "meta": { "total": 12, "pages": 1 }
}

meta is present on lists.

A refusal is a standard WordPress REST error with a code starting plugixa_chat_, a readable message and an HTTP status. Validation errors on a form carry the messages per field in data.fields.

Lists

Every list route accepts the same parameters.

Parameter Values Default
page 1 or more 1
per_page 1 to 200 50
search Text Empty
orderby A sortable column of that record id
order asc or desc desc
view active or trash active

Records and the trash

Widgets, leads, and the Pro records are stored the same way and share a set of routes. Deleting a record moves it to the trash. A trashed record can be restored, or purged, which deletes it for good.

For a record at /{base}:

Method Route Does Permission
GET /{base} Lists records View
POST /{base} Creates one. Answers 201 Create
GET /{base}/{id} Reads one View
PUT, PATCH /{base}/{id} Updates one Edit
DELETE /{base}/{id} Moves one to the trash Delete
POST /{base}/{id}/restore Restores one from the trash Delete
DELETE /{base}/{id}/purge Deletes one for good Delete

A record that does not exist answers plugixa_chat_not_found with status 404.

Base Record Edition
widgets Widgets Free
leads Leads Free
agents Agents Pro PRO
webhooks Webhooks Pro PRO
crm CRM connections Pro PRO
experiments A/B tests Pro PRO

Routes

Core

Method Route Does Permission
GET /bootstrap The plugin version, your permissions, and the parts of the plugin that are loaded View
GET /settings The settings, which secrets are set (never their values), and the choices the screen offers Settings
PUT, PATCH /settings Saves settings. Send only the keys you want to change. Unknown keys are ignored. Secrets go in a secrets object Settings
GET /context Public. See Public routes None

The setting keys are: date_format, time_format, records_per_page, ip_mode, retention_days, render_ssr, shadow_dom, fail_mode, lead_notify, lead_recipients, turnstile_site_key, track_events, woo_button, woo_widget, woo_channel, woo_text, woo_message, and in Pro agent_rotation. The one secret the free edition uses is turnstile_secret.

Widgets

The shared record routes at /widgets, plus:

Method Route Does Permission
POST /widgets/{id}/duplicate Copies a widget, with its channels and rules, as an inactive widget Create
GET /channels The channel catalogue and its groups View
GET /content Searches pages, posts or terms for the targeting pickers. kind is post or term; q is the search text; ids is a comma-separated list to look up by ID; type narrows to one post type or taxonomy. Up to 20 results for a search View

A widget is read and written as one object:

{
  "name": "Main widget",
  "status": "active",
  "appearance": {
    "position": "right",
    "layout": "vertical",
    "size": 56,
    "color": "#0f766e",
    "icon_color": "#ffffff"
  },
  "channels": [
    {
      "slug": "whatsapp",
      "value": "+15550001234",
      "label": "",
      "message": "Hi! I'm on {title}",
      "is_active": true,
      "devices": "all"
    }
  ],
  "rules": [
    { "kind": "url", "mode": "hide", "operator": "contains", "values": [ "/checkout" ] }
  ]
}

Things to know:

  • On an update, sending channels replaces all of the widget’s channels, and sending rules replaces all of its rules. Leave a key out to keep what is stored.
  • status is active or inactive.
  • A name is required: plugixa_chat_widget_name_required, status 400.
  • Values are cleaned on the way in: sizes and spacings are brought into range, colours must be hex, an unknown channel slug or rule kind is dropped, and so is a rule with no values.
  • A list response’s meta also carries limit and used. limit is 0 unless a ceiling was set with the plugixa_chat_max_widgets filter. At the ceiling, creating, duplicating or restoring answers plugixa_chat_widget_limit, status 409.

Rule kinds are template, url, post, term, post_type, device, logged_in and referrer, and in Pro schedule, country and language.

Rule kinds the builder does not offer

The API accepts three more kinds. None can be added in the builder.

Kind Values Behaviour
visits A number, as a string Matches once this browser’s count has reached the number. The count is kept in the browser’s local storage as plugixa_chat_visits, goes up by one each time the rule is checked on a page, and is never sent anywhere. A browser that blocks storage always counts as 1
ab test:from-to Written by Pro’s A/B tests. Do not write it yourself
role Text Stored, but version 1.0.0 of the widget script has no check for it. It is answered as a check that could not be made, by the If a visitor check fails policy and the plugixa_chat_unevaluable_rule filter

Leads

The shared record routes at /leads, with these differences:

Method Route Does Permission
GET /leads Also accepts status (new or read) and widget_id View
POST /leads Refused. Answers plugixa_chat_leads_read_only, status 405. Leads are created only by the contact form -
PUT, PATCH /leads/{id} Changes the status only. Send {"status": "read"} or {"status": "new"} Edit
GET /leads/unread The number of new leads, as {"count": 3}. Accepts widget_id View
GET /leads/export The leads as a CSV file. Accepts status Export
GET /leads/{id}/answers The answers to custom form fields for one lead PRO View
POST /leads/submit Public. See Public routes None

A lead never includes the stored IP value.

Analytics

Method Route Does Permission
GET /analytics/summary Totals, the follow-through rate, a point per day and the top ten channels. days is 1 to 365, default 30. widget narrows to one widget, default 0 for all View
POST /events Public. See Public routes None
{
  "success": true,
  "data": {
    "days": 30,
    "totals": { "view": 1840, "open": 212, "click": 97 },
    "clickRate": 45.8,
    "series": [ { "date": "2026-10-05", "view": 61, "open": 8, "click": 3 } ],
    "channels": [ { "channel": "whatsapp", "hits": 70 } ],
    "widget": 0
  }
}

Pro routes

PRO These exist only on a site with the Pro edition. Each record also has the shared record routes at its base.

Method Route Does Permission
POST /webhooks/{id}/test Posts a sample lead to the webhook now and returns the result Edit
GET /crm/providers The services, and whether each is available View
POST /crm/lists Fetches the lists for a service. Send provider and api_key, or the id of a saved connection to use its key Edit
POST /crm/{id}/check Tests a connection’s key and list Edit
POST /experiments/{id}/start Starts a draft test Edit
POST /experiments/{id}/end Ends a running test. keep is a or b Edit
GET /experiments/{id}/results The figures for both widgets View
POST /orders/lookup Public. Registered only while WooCommerce is active. See Public routes None

Deleting a CRM connection also erases its API key. An API key is never returned by any route; a connection carries has_key instead. A running A/B test cannot be deleted: plugixa_chat_test_running, status 409.

Public routes

Four routes accept a visitor who is not logged in. They are what the widget calls.

Method Route Does Limit per visitor
GET /context Answers the questions a cached page cannot: the site’s current time, the visitor’s country, whether they are logged in, and fresh tokens None
POST /events Records analytics events. Answers 204 with no body 60 every five minutes
POST /leads/submit Stores a contact form message. Answers 201 5 every five minutes
POST /orders/lookup PRO Looks up a WooCommerce order 10 every five minutes

The page token

The three POST routes need a token. It is not a WordPress nonce, because the page that carries it is often served from a cache and shared by every visitor.

  • The token is printed into every page that loads the widget, in the tokens object of the JSON block with the ID plugixa-chat-config. There is one for events and one for lead. The order lookup uses the lead token.
  • Send it in the X-Plugixa-Chat-Token header, or as token in the JSON body.
  • A token is valid for between 12 and 24 hours. /context returns fresh ones.
  • When the request carries an Origin or Referer header, its host must be the site’s own.
Error code Status Meaning
plugixa_chat_missing_token 403 No token was sent
plugixa_chat_stale_token 403 The token has expired. Fetch /context and try again
plugixa_chat_bad_origin 403 The request came from another site
plugixa_chat_rate_limited 429 The visitor’s limit for this route is used up

The token says “this came from a page this site rendered recently”. It is not authentication. The limits, and the contact form’s own spam checks, are what make replaying it pointless.

GET /context

{
  "success": true,
  "data": {
    "now": { "minutes": 570, "weekday": 1, "date": "2026-10-05", "timezone": "Europe/Paris" },
    "country": "FR",
    "loggedIn": false,
    "tokens": { "events": "...", "lead": "..." },
    "failMode": "open"
  }
}

minutes is minutes since midnight in the site’s timezone, and weekday is 0 for Sunday to 6 for Saturday. country is empty when it is not known. The response is marked private and may be reused by the browser for five minutes.

POST /leads/submit

{
  "widget": 2,
  "fields": { "name": "Jane Doe", "email": "jane@example.com", "message": "Hello" },
  "consent": true,
  "subscribe": false,
  "website": "",
  "elapsed": 8400,
  "page": "https://example.com/contact/"
}
Key Meaning
widget The ID of an active widget that has a Contact form channel
fields name, email, phone, message, and in Pro the custom fields f1, f2 and so on
consent, subscribe The two checkboxes, as booleans
website The trap field. It must be empty
elapsed Milliseconds since the form was opened. Under 3000 is treated as a program
page The page address. Kept only when it is on this site
turnstile The Turnstile token, when Turnstile is on

A submission caught by the trap field or the timer is answered exactly like a real one, and is not stored.

Error code Status Meaning
plugixa_chat_no_form 404 The widget is not active or has no contact form
plugixa_chat_invalid_lead 400 A field is missing or invalid. data.fields has a message per field
plugixa_chat_turnstile_failed 400 The Turnstile check was missing or failed
plugixa_chat_lead_not_saved 500 The message could not be written

POST /events

{
  "token": "...",
  "events": [
    { "w": 2, "c": "", "t": "view" },
    { "w": 2, "c": "", "t": "open" },
    { "w": 2, "c": "whatsapp", "t": "click" }
  ]
}

w is the widget’s ID, c the channel’s slug and t one of view, open or click. At most 200 events are read from one request. Anything else in an event is ignored.

POST /orders/lookup

PRO

{
  "fields": { "email": "jane@example.com", "order": "1042" },
  "website": ""
}

A match answers with lines, an array of sentences, and link, which is null or an object with url and label. No match answers plugixa_chat_order_not_found, status 404. A missing email or number answers plugixa_chat_order_invalid, status 400.

What to do next

Quick Links