REST API¶
The plugin registers four routes under the cfbe/v1 namespace. All responses send Cache-Control: no-store.
| Route | Method | Used by | Auth |
|---|---|---|---|
/wp-json/cfbe/v1/subscribe |
POST | the signup forms and the popup | none (public); honeypot, fill-time token, rate limits |
/wp-json/cfbe/v1/form-token |
GET | the form script | none (public) |
/wp-json/cfbe/v1/bluefox-hook |
POST | bluefox webhooks | Bearer API key or X-CFBE-Secret |
/wp-json/cfbe/v1/cart-email |
POST | the classic checkout (abandoned carts) | wp_rest nonce |
Subscribe¶
POST /wp-json/cfbe/v1/subscribe with a JSON body. It is public and nonce-less on purpose, so forms work on cached pages.
| Field | Description |
|---|---|
source |
Required: shortcode, block, promo or popup. Other values return 400. |
email |
Email address. |
name |
Name (optional, depending on the Form tab). |
interests |
Array of interest tags (only configured tags are accepted). |
consent |
true – the consent checkbox. |
cfbe_cfg |
The signed form configuration printed in the form: list, extra tags and placement name (placement-<name> tag). Unsigned or tampered values are ignored. |
cfbe_hp |
Honeypot; must be empty. |
cfbe_token |
Fill-time token from /form-token. |
The consent text is never taken from the request: the plugin stores the text configured on the Form tab.
A signed list only applies when it is one of the project's lists an admin has loaded (General tab, Load lists, remembered in the cfbe_known_lists option) or a list chosen in the settings (default, popup, checkout or coupon list). Otherwise the signup goes to the default list.
Protection:
- body larger than 4 KB → 413;
- honeypot filled, or
cfbe_tokenmissing, invalid, younger than 3 seconds or older than 2 hours → a fake success, nothing is stored; - more than 5 requests per IP in 10 minutes (IPv6: per /64 range) → 429;
- more than 30 signups per minute for the whole site (
cfbe_signup_site_limit) → 429; - more than 3 signups per mailbox in an hour → 429. This limit is applied by the signup service, so it covers every source: forms, checkout, My Account and form plugin integrations. Mailbox means the address lowercased, without a
+tag, and for Gmail without dots (googlemail.comcounts asgmail.com).
The visitor IP comes from the Visitor IP address setting on the Privacy tab, then the cfbe_client_ip filter.
Response:
{ "success": true, "code": "pending", "message": "Thank you! We have sent you a confirmation email…" }
code |
success |
Meaning |
|---|---|---|
pending |
true | Every successful outcome: new signup, already subscribed, confirmation re-sent, queued for retry, or a suppressed / bounced address. |
already_active |
true | Already an active (or paused) subscriber. Only when Form → Show the "Already subscribed" message is on. |
invalid |
false | Validation failed; fields maps field names to messages (HTTP 400). |
rejected |
false | The plugin is not configured, or another error. |
rate_limited |
false | Too many attempts (HTTP 429). |
The response is neutral on purpose, so the form never reveals who is subscribed. The internal codes (resent, already_active, queued, suppressed) are still passed to the cfbe_subscribed action.
message is meant for the visitor: the success, already-subscribed and error texts come from the Form tab. Errors never disclose the reason bluefox gave.
Form token¶
GET /wp-json/cfbe/v1/form-token returns { "token": "<time>.<signature>" }, a fill-time token signed by the site (HMAC with the site's salt), sent with Cache-Control: no-store. The form script fetches it when the form becomes visible (popup: when it opens) and sends it as cfbe_token. If the visitor submits sooner than 3 seconds after the fetch, the script waits out the rest, so fast autofill is not dropped. Because the token is fetched at run time, forms keep working on cached pages.
bluefox webhook¶
POST /wp-json/cfbe/v1/bluefox-hook – the endpoint is always registered, whether or not the Inbound webhooks module is on. See Webhook for the setup.
Authentication, compared in constant time:
- Project webhook –
Authorization: Bearer <API key>, equal to the configured API key. If the server hides the header, the plugin also readsREDIRECT_HTTP_AUTHORIZATIONandapache_request_headers(). - Automation webhook step – header
X-CFBE-Secret: <webhook secret>.
Payloads:
- Project webhook:
{ "type": "subscribe", "subscription": { "email", "status", "subscriberList": { "_id" } }, … }. While Inbound webhooks is on, every authenticated event firescfbe_webhook_received. Subscription events update the local list status. Onlysubscribe/resubscribewith statusactive, on the list of the email's own signup, queue a confirmation check. - Automation step:
{ "automationId", "contactId", "contact": { "email", … } }. Queues a confirmation check if the email has a local signup.
The webhook only queues work. The confirmation itself re-reads the subscriber's status from the bluefox API.
| Status | Body | Meaning |
|---|---|---|
| 200 | {"ok":true,"action":"queued"} |
A confirmation check was queued. |
| 200 | {"ok":true,"action":"ignored"} |
Not relevant (other list, other status, unknown contact). |
| 200 | {"ok":true,"action":"received"} |
A delivery or engagement event (bounce, open, …). |
| 400 | {"ok":false} |
Automation payload without a valid email. |
| 401 | {"ok":false} |
Authentication failed. |
| 413 | {"ok":false} |
Body larger than 64 KB. |
| 429 | {"ok":false} |
More than 60 requests per minute from one IP. |
Cart email¶
POST /wp-json/cfbe/v1/cart-email – registered by the Abandoned cart emails module. On the classic checkout, the browser sends the email address the shopper typed (email) so the cart can be stored for a guest. It needs the wp_rest nonce in X-WP-Nonce and is limited to 20 requests per IP in 10 minutes and 5 per mailbox in an hour (429). The block checkout does not use it.
Response: { "stored": true|false }.
Not REST¶
The admin screens use admin-ajax.php actions (cfbe_*) protected by nonces and the cfbe_capability capability. The AI agent abilities are exposed by the WordPress Abilities API, not by routes of this plugin.