The Wallu API
API Reference
The Wallu API
Authenticate with an API key and call the Wallu HTTP endpoints for Assist replies, transactional email, and cart recovery.
The Wallu API
Wallu exposes a small HTTP API for connecting external plugins, bots, and stores to your account. Every request is JSON over HTTPS. With one exception — the public ticket endpoint, documented in the next article on this page — every request is authenticated with an API key.
Base URL: https://wallu.ai
Authentication
The /api/v1/* endpoints authenticate with a bearer token in the Authorization header:
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx
Two key formats are accepted:
sk_live_...— created from your dashboard (see below), tied to your account.wr_api_...— a WordPress / Reach plugin key. If you connected the Wallu WooCommerce or WordPress plugin, it identifies your account with this format. See WooCommerce.
If the header is missing or does not start with Bearer , you get 401:
{ "error": "Missing or invalid Authorization header" }
If the key matches no account, 401:
{ "error": "Invalid API Key" }
Creating an API key
API keys are created and revoked from your dashboard, not through the API itself.
- Open Integrations in your dashboard.
- Select the API Keys tab.
- Generate a key, give it a name, and copy the value. It starts with
sk_live_— copy it when it is created.
Generating a key requires a paid plan. On a free or trial plan the create call returns 403:
{ "error": "API keys require a paid plan. Upgrade to get started." }
For reference, the underlying endpoints are GET /api/keys, POST /api/keys with body { "name": "..." } (a name is required, else 400 { "error": "Key name is required" }), and DELETE /api/keys/:id. These are authenticated by your dashboard login session, not by an API key.
<!-- SCREENSHOT: The API Keys tab under Integrations, with a generated sk_live_ key and a Delete button -->
Rate limits
All /api/ routes share a global limit of 1500 requests per 15 minutes per IP (roughly 100/min) in production. Exceeding it returns 429. Individual endpoints add their own, stricter limits on top of this.
POST /api/v1/on-message
Generate an AI reply for an incoming message using your Assist agent. This is the endpoint a plugin or bot calls to let Wallu answer a chat message.
Requires a Pro or Enterprise plan. Other plans get 403:
{ "error": "Wallu Assist API requires a Pro or Enterprise plan." }
Request body:
{
"message": { "content": "How do I reset my password?" },
"channel": { "id": "..." },
"user": { "username": "..." },
"addon": { "name": "..." }
}
Only message.content is required and used to generate the reply; channel, user, and addon are optional metadata for your own logging.
Success 200:
{ "reply": "To reset your password, ..." }
The generated reply is billed against your Assist credit usage. If message.content is missing you get 400 { "error": "Message content is required" }; if generation fails, 500 { "error": "AI failed to generate response" }.
POST /api/v1/send-email
Send a single transactional email through your Wallu Reach sending setup.
Request body:
{
"to": "customer@example.com",
"subject": "Your order shipped",
"html_body": "<p>Hi there...</p>",
"text_body": "Hi there...",
"from": "you@yourdomain.com"
}
to, subject, and at least one of html_body / text_body are required. from is optional.
Success 200:
{ "success": true, "message": "Email sent successfully" }
Errors:
400 { "error": "Missing required fields", "required": ["to", "subject", "html_body OR text_body"] }400 { "error": "Invalid email address format" }429 { "error": "Email quota exceeded", "message": "..." }— you have hit your Reach sending limit.
Each send counts against your Reach email quota. See Reach and email campaigns.
Cart recovery endpoints
These two endpoints power abandoned-cart recovery for connected stores. The WooCommerce / WordPress plugin calls them automatically, but you can also call them directly.
POST /api/v1/cart-abandoned — record a cart when a shopper enters their email at checkout. A recovery email is scheduled.
{
"email": "shopper@example.com",
"items": [],
"total": 49.0,
"currency": "USD",
"checkoutUrl": "https://store.example.com/checkout"
}
email is required (400 { "error": "Email is required" } otherwise). Success:
{ "success": true, "message": "Cart tracked, recovery email scheduled" }
POST /api/v1/cart-recovered — tell Wallu the shopper completed the order, so pending recovery emails are cancelled and the sale is attributed.
{ "email": "shopper@example.com", "orderId": "1234", "total": 49.0 }
email is required. Success:
{ "success": true, "message": "Cart marked as recovered" }
See WooCommerce for the plugin that wires these up for you.
Error format
Errors are returned as JSON with an error field and the matching HTTP status. Unexpected server failures return 500 with { "error": "Internal Server Error" }; on wallu.ai (production) the response body carries only the error string. Build your integration to read the HTTP status first and the error string second.
Public ticket endpoint
POST /api/tickets — the one unauthenticated endpoint, for creating support tickets from an embedded form or widget.
Public ticket endpoint
POST /api/tickets is the one Wallu API endpoint that does not require an API key. It lets an embedded website form or chat widget create a support ticket in your dashboard on behalf of a visitor. Every ticket lands in Tickets and triggers an email notification to your team.
Base URL: https://wallu.ai
Request
POST /api/tickets with a JSON body:
{
"email": "visitor@example.com",
"message": "I can't log in to my account.",
"name": "Jane Doe",
"priority": "high",
"widgetToken": "your-widget-token"
}
Fields:
email(required) — the visitor's email address.message(required) — the ticket body.name(optional) — the visitor's name.priority(optional) — a label, defaults tonormal. Common values:low,normal,high.widgetTokenoragentId(one required) — routes the ticket to your account.
Routing: widgetToken vs agentId
You must identify which account the ticket belongs to. There are two ways:
widgetToken— the secret your chat widget already uses. This is preferred: it is verified server-side. When a token is present, anyagentIdin the body is ignored entirely.agentId— your agent / account id. Use this when no widget token is available.
Send neither and you get 400 { "error": "widgetToken or agentId is required to route the ticket" }.
Success
200:
{ "success": true, "ticketId": "clx...", "message": "Ticket created successfully" }
The ticket is created with status open and the supplied priority. A notification email is sent to your account's email (or the platform support address if you have none set). The email is best-effort — a failed notification does not fail the request.
Errors
400 { "error": "Email and message are required" }—emailormessagemissing.400 { "error": "widgetToken or agentId is required to route the ticket" }— no routing field.404 { "error": "Invalid widget token" }— awidgetTokenwas supplied but matched no account.404 { "error": "Agent not found" }— anagentIdwas supplied but matched no account.429 { "error": "Too many ticket submissions. Please try again later." }— rate limited.500 { "error": "Failed to create ticket" }.
Rate limit
This endpoint is capped at 5 submissions per IP per 10 minutes to prevent spam from the public form. That is separate from — and stricter than — the global API rate limit.
Notes and limits
priorityis stored verbatim; there is no server-side validation of the value, so keep to a small known set likelow/normal/highfor consistent filtering.- The notification goes to your account email if set, otherwise the platform's support address. The full thread and replies are managed inside Tickets.
- To create a ticket from an existing inbox conversation instead, the dashboard uses a separate, authenticated endpoint (
POST /api/tickets/from-inbox). See the Unified Inbox. - Because the endpoint is unauthenticated, treat your
agentIdas low-sensitivity routing info; thewidgetTokenis the value you should keep to the widget. For general setup, start with Getting started.