REST API
Every server route the plugin adds lives under the fahad-ai/v1 namespace. This page lists each endpoint, its method, the permission gate that protects it, and the shape of what it returns. Nothing here is invented; it mirrors the routes the plugin registers on your store.
Namespace and base URL
All routes are registered against the WordPress REST API under the fahad-ai/v1 namespace, so the base path on your site is:
https://your-store.com/wp-json/fahad-ai/v1/
The endpoints fall into four groups, each with a different security boundary: the storefront chat endpoints, the admin copilot endpoints, the WhatsApp webhook, and the store-as-an-agent gateway.
How the storefront endpoints are gated
The chat, cart, feedback, and visual search endpoints are intentionally public, because a storefront assistant has to answer guests who are not logged in. They are not left open, though. Each request passes through one shared gate that does two things:
- Nonce check. The request must carry a valid
wp_restnonce in theX-WP-Nonceheader. The nonce is not an identity check (every visitor gets one), but it blocks cross origin and CSRF style abuse. A missing or expired nonce returns403. - Per client rate limit. A fixed window counter caps how many billable calls and cart mutations any one client can trigger. It is keyed per user when logged in, otherwise per IP. The default is 20 requests per 60 seconds. When the window is exhausted the request returns
429.
Both limits are adjustable with filters, covered in Hooks & filters:
add_filter( 'fahad_ai_rate_limit', fn() => 40 ); // requests per window
add_filter( 'fahad_ai_rate_window', fn() => 30 ); // window length in seconds
Storefront chat endpoints
These are the routes the chat widget calls. All four share the gate above: wp_rest nonce plus the per client rate limit, returning 429 when the limit is exceeded.
POST /message
Runs one non streaming conversation turn. You send a messages array of { role, content } entries; the assistant answers using grounded store tools. The response is a JSON object:
{
"message": "Here are two options under $50 ...",
"cards": [ /* grounded product cards */ ],
"comparison": [ /* optional side-by-side rows */ ]
}
An empty or malformed messages array returns 400. If every configured provider fails, the endpoint still returns 200 with a friendly fallback message rather than a raw error, so the shopper never hits a dead end.
POST /stream
The same turn as /message, but streamed as Server Sent Events (Content-Type: text/event-stream) so replies render token by token. Each event has a type and a small data payload:
chunkcarries a piece of the reply text.toolannounces a grounded tool call by name.productsdelivers product cards.comparisondelivers a comparison table.donecloses the turn.errorreports a validation problem (for example a missingmessagesarray).
POST /cart
Mutates the WooCommerce cart directly, so card buttons (add to cart, change quantity, remove) do not need an agent round trip. It takes an action plus product_id, quantity, variation_id, or cart_item_key as the action needs, reuses the same cart tools the assistant uses (including their stock and variation validation), and returns the verified cart state as JSON. An unknown action returns 400. It returns JSON rather than SSE so WooCommerce can set its session cookie and guest carts persist.
POST /feedback
Records a thumbs up or thumbs down on a bot reply. You send a rating of up or down, an optional short reason, and opaque conversation_ref and message_ref values. It is telemetry only and stores no personal data. The response confirms the stored row:
{ "ok": true, "id": "fb_a1b2c3" }
Any rating that is not up or down returns 400 before anything is stored.
Visual search
POST /visual-search
The "shop the look" route. It accepts a multipart upload with an image part and optional category, min_price, max_price, and limit filters, then returns visually similar in stock products. It uses the same gate as the chat endpoints (nonce plus rate limit), because a vision lookup is billable. The response shape is:
{ "available": true, "found": 3, "products": [ /* cards */ ] }
Image validation failures return 413 (too large), 415 (wrong type), or 400 (missing). The image ranking itself is a pluggable seam, so with no vision provider registered the route degrades gracefully to { "available": false, "found": 0, "products": [], "message": ... } rather than failing.
WhatsApp webhook
GET and POST /whatsapp
The omnichannel webhook for the WhatsApp channel. Its security boundary is not the chat nonce, because Meta cannot send one. Instead:
- The
GETrequest is Meta's verification handshake, checked against your configured verify token and echoing back the challenge. - The
POSTrequest carries inbound messages and is validated by theX-Hub-Signature-256HMAC signature.
The outbound send is a pluggable seam (fahad_ai_whatsapp_send); no live Meta call ships in core.
Admin copilot endpoints
The merchant copilot routes live under /admin. They are gated by the manage_woocommerce capability, not the storefront nonce, so only store managers can reach them. Every one is read only or draft only; nothing here writes to your store. All are GET requests grounded in real WooCommerce data.
GET /admin/insightsreturns a grounded sales summary for a recent window: order count, gross revenue, refund total and count, and currency.GET /admin/sale-candidatesreturns products that look like good discount candidates.GET /admin/product-contextreturns grounded context for a single product.GET /admin/review-draftsreturns draft review replies for the manager to edit and post.
A caller without the manage_woocommerce capability is rejected by the permission gate before any handler runs.
Agent gateway endpoints
The store as an agent gateway lives under /agent. These are public read only routes so external AI agents can discover and shop your catalogue using the same grounded data a human shopper sees. They expose only published catalogue data, and the handoff only returns URLs; nothing here mutates the store or charges anyone. See Agent endpoints for the full walkthrough.
GET /agent/llmsreturns a plain text usage policy (llms.txt style) that points agents at the feed and states what is allowed.GET /agent/catalogreturns a structured product feed:{ "count": N, "products": [ ... ] }built from trusted WooCommerce fields.GET /agent/searchreuses the chatsearch_productstool, taking aqquery and returning grounded card data.GET /agent/productreusesget_product_detailsfor a single productid.GET /agent/checkout-handofftakes a comma separatedidslist and returns{ "note": ..., "items": [ { "product_id": N, "add_to_cart": "..." } ] }. The agent never holds a cart or pays; a human opens the link and completes checkout in their own session.
Summary table
METHOD PATH GATE RETURNS
POST /message nonce + rate limit { message, cards, comparison }
POST /stream nonce + rate limit SSE: chunk, tool, products,
comparison, done, error
POST /cart nonce + rate limit verified cart state (JSON)
POST /feedback nonce + rate limit { ok, id }
POST /visual-search nonce + rate limit { available, found, products }
GET /whatsapp verify token challenge echo
POST /whatsapp X-Hub-Signature-256 delivery ack
GET /admin/insights manage_woocommerce sales summary
GET /admin/sale-candidates manage_woocommerce discount candidates
GET /admin/product-context manage_woocommerce product context
GET /admin/review-drafts manage_woocommerce draft review replies
GET /agent/llms public read text usage policy
GET /agent/catalog public read { count, products }
GET /agent/search public read grounded card data
GET /agent/product public read product details
GET /agent/checkout-handoff public read { note, items }
Common status codes
200the request succeeded, including the graceful fallbacks described above.400the request was malformed (for example a missingmessagesarray or an invalid rating).403thewp_restnonce was missing or expired, or the admin capability check failed.429the per client rate limit was exceeded; wait for the window to reset and retry.