Developer

API Documentation

Add Caretta Assistant — WAPI's smart assistant — to your product over a plain HTTPS API. One key, no SDK — send a message, get an answer generated from your own content. You build and own the UI around it.

Overview

The WAPI API gives you programmatic access to Caretta Assistant, WAPI's smart assistant. It answers questions from your own knowledge base over HTTPS. Authenticate with a single API key; there is no SDK to install.

This API is AI-only — it answers questions and returns JSON. Everything else (the chat UI, when to show it, what to do with a low-confidence answer) is yours to build.

Base URL
https://www.wapiso.com/api/v1/chat/<action>

Authentication

Send your API key as a Bearer token on every request. The X-WAPI-Key header is accepted as an alternative.

curl "https://www.wapiso.com/api/v1/chat/start" \
  -H "Authorization: Bearer wapi_pk_YOUR_KEY"

Always call the API from your own backend so the key never ships in browser code. A missing, invalid, or regenerated (old) key returns HTTP 401. Find and rotate your key in the dashboard under Developer → API Key.

Response format

Every response is JSON. On success, the payload is nested under a data object alongside "success": true. On error, you get "success": false with an error code and a human-readable message, plus a matching HTTP status.

Success
{
  "success": true,
  "data": { /* the action’s fields */ }
}
Error
{
  "success": false,
  "error": "Error",
  "message": "Message is required"
}

Quickstart

The whole core of the API is one call: send a message, read the assistant reply from data.messages. Here it is end to end — call this from your server.

curl -X POST "https://www.wapiso.com/api/v1/chat/send" \
  -H "Authorization: Bearer wapi_pk_YOUR_KEY" \
  -d "message=How do I renew my domain?"
Response
{
  "success": true,
  "data": {
    "conversationId": 1234,
    "messages": [
      { "id": 9001, "role": "user",      "body": "How do I renew my domain?", "source": "system" },
      { "id": 9002, "role": "assistant", "body": "You can renew it from your domains page…", "source": "ai" }
    ],
    "lowConfidence": false
  }
}

Conversations & state

You do not pass a conversation id. send takes only message; the server resolves the conversation from the API-key owner. In practice that means one rolling conversation per API key — messages accumulate in the same thread.

The assistant reads the last 30 messages of that thread for context; older turns are summarized automatically, so nothing breaks on long threads — just keep important facts recent.

Building a multi-user chat where each visitor needs an isolated thread? All calls under one wapi_pk_ key share a thread — by design. For per-visitor conversations use the Embed Widget below: its wapi_emb_ token keys an isolated conversation per visitor automatically.

Reply language follows the message

The assistant answers in the language each message is written in. On every send the message language is auto-detected (Turkish, English or German), overrides the conversation’s previous language, and is saved on the conversation for the next turns. You don’t pass a language parameter on this surface — just write in the language you want answered. Supported: tr, en, de.

Caretta Chat API

Four conversational actions plus two read-only account endpoints. All are called as /api/v1/chat/<action> with your Bearer key. The older /api.php?endpoint=chat&action=… URLs keep working, so existing integrations need no change.

Conversational

GET /api/v1/chat/start Open or resume the conversation and return its message history.

No parameters.

curl "https://www.wapiso.com/api/v1/chat/start" \
  -H "Authorization: Bearer wapi_pk_YOUR_KEY"
Response
{
  "success": true,
  "data": {
    "conversationId": 1234,
    "lang": "en",
    "messages": []
  }
}
POST /api/v1/chat/send Send a visitor message and get the AI reply. The core action.
ParameterTypeDescription
messagerequired string The visitor’s message. Max 2,000 characters.
curl -X POST "https://www.wapiso.com/api/v1/chat/send" \
  -H "Authorization: Bearer wapi_pk_YOUR_KEY" \
  -d "message=How do I renew my domain?"
Response
{
  "success": true,
  "data": {
    "conversationId": 1234,
    "messages": [
      { "id": 9001, "role": "user",      "body": "How do I renew my domain?", "source": "system" },
      { "id": 9002, "role": "assistant", "body": "You can renew it from your domains page…", "source": "ai" }
    ],
    "lowConfidence": false
  }
}
GET /api/v1/chat/suggestions Starter help topics and seed questions to show before the first message.

No parameters.

curl "https://www.wapiso.com/api/v1/chat/suggestions" \
  -H "Authorization: Bearer wapi_pk_YOUR_KEY"
Response
{
  "success": true,
  "data": {
    "articles": [
      { "id": 12, "slug": "renew-domain", "title": "Renewing a domain", "excerpt": "…", "category_slug": "domains", "category_name": "Domains", "view_count": 1250 }
    ],
    "seeds": [ "How do I renew a domain?", "How do I add balance?" ]
  }
}
GET /api/v1/chat/sync Poll the conversation for messages newer than an id (e.g. live updates).
ParameterTypeDescription
sinceIdoptional int Return only messages with id greater than this. Defaults to 0.
curl "https://www.wapiso.com/api/v1/chat/sync?sinceId=9002" \
  -H "Authorization: Bearer wapi_pk_YOUR_KEY"
Response
{
  "success": true,
  "data": {
    "conversationId": 1234,
    "messages": [
      { "id": 9003, "role": "assistant", "body": "…", "source": "ai" }
    ]
  }
}
POST /api/v1/chat/feedback Rate an assistant message (thumbs). not_helpful auto-flags the answer for review and improves future replies.
ParameterTypeDescription
messageIdrequired int The assistant message id being rated (from start/send/sync).
ratingrequired string helpful or not_helpful.
commentoptional string Optional note shown to the reviewer. Max 500 characters.
Response
{
  "success": true,
  "data": {
    "saved": true
  }
}

The message object

start, send and sync all return message objects with these fields:

FieldTypeDescription
idintUnique message id (use the largest as sinceId for sync).
rolestringwho wrote it: user, assistant, or system.
bodystringThe message text.
sourcestringhow it was produced: ai, cache_tenant, cache_shared, system, fallback.
created_atstringTimestamp (UTC).
productsarrayassistant messages only — product cards attached to the reply (see below). Absent or empty when no products apply.

The product object (products[])

When your assistant’s Sales Mode is on and the visitor asks about products, assistant messages carry up to 8 product cards learned from your own pages. The API returns a plain array — render it however you like (the widget shows a horizontal carousel). Product data enters the system from your site during crawling (including schema.org JSON-LD you already publish); the API output itself is this plain array, never JSON-LD.

FieldTypeDescription
namestringProduct name. The only field that is always present.
urlstringLink to the product page, when learned.
pricestringDisplay price as text, when learned.
categorystringCategory label, when learned.
imagestringProduct image URL, when learned.

Account (read-only)

GET /api/v1/chat/apiKey Your key metadata, current account balance, and per-message pricing.
curl "https://www.wapiso.com/api/v1/chat/apiKey" \
  -H "Authorization: Bearer wapi_pk_YOUR_KEY"
Response
{
  "success": true,
  "data": {
    "apiKey": { "id": 1, "api_key_prefix": "wapi_pk_6c52c2d1", "status": "active", "last_used_at": "2026-05-30 14:12:52", "created_at": "2026-05-01 09:00:00" },
    "balance": 12.40,
    "pricing": { "input_per_m_usd": 1.10, "output_per_m_usd": 4.40, "typical_ai_usd": 0.0021 }
  }
}
GET /api/v1/chat/usageSummary Usage and spend for the last 30 days, plus all-time totals.
curl "https://www.wapiso.com/api/v1/chat/usageSummary" \
  -H "Authorization: Bearer wapi_pk_YOUR_KEY"
Response
{
  "success": true,
  "data": {
    "window_days": 30,
    "by_event": { "ai_call": { "count": 42, "cost": 0.0735 } },
    "total_cost": 0.0993,
    "total_events": 195,
    "all_time": { "events": 500, "cost": 0.35 },
    "recent": [ { "id": 1, "event_type": "ai_call", "cost_usd": 0.00175, "created_at": "2026-05-30 10:31:00" } ],
    "price_per_message_usd": 0.00175
  }
}
GET /api/v1/chat/getTone Current reply-tone setting of your assistant (preset + optional style note). Read here; change it in the dashboard under Developer → AI Assistant Configuration.
Response
{
  "success": true,
  "data": {
    "preset": "warm",
    "custom": "keep answers short",
    "presets": ["professional", "warm", "energetic"]
  }
}

Embed tokens (manage via API)

Server-side only: these management calls use your SECRET wapi_pk_ key — never call them from a browser. They manage the PUBLIC wapi_emb_ tokens your pages embed (see Embed Widget below). Limit: 5 active tokens.
GET /api/v1/chat/embedTokens List your embed tokens with today’s usage: spend, budget %, conversations and unique visitors.
Response
{
  "success": true,
  "data": {
    "tokens": [ {
      "id": 3, "token": "wapi_emb_…", "label": "Company site",
      "allowed_domains": ["example.com"], "daily_budget_usd": "1.0000", "status": "active",
      "today_spend_usd": 0.0421, "budget_used_pct": 4,
      "conversations": 12, "visitors": 9,
      "last_used_at": "2026-06-07 18:40:11", "created_at": "2026-06-01 09:00:00"
    } ]
  }
}
POST /api/v1/chat/embedTokenCreate Create a token. Returns the full wapi_emb_ value once.
ParameterTypeDescription
domainsrequiredstringAllowed domains, comma-separated (e.g. example.com, www.example.com). Subdomains of an allowed domain pass too.
labeloptionalstringDisplay label (max 80 chars).
daily_budget_usdoptionalfloatHard daily budget in USD (0.05–100, default 1.00). Spend at the cap pauses the widget until next UTC day.
Response
{
  "success": true,
  "data": {
    "token": "wapi_emb_f3a91c…"
  }
}
POST /api/v1/chat/embedTokenUpdate Update label, domains and/or daily budget of a token.
ParameterTypeDescription
idrequiredintToken id (from embedTokens).
labeloptionalstringDisplay label (max 80 chars).
domainsoptionalstringAllowed domains, comma-separated (e.g. example.com, www.example.com). Subdomains of an allowed domain pass too.
daily_budget_usdoptionalfloatHard daily budget in USD (0.05–100, default 1.00). Spend at the cap pauses the widget until next UTC day.
Response
{
  "success": true,
  "data": {
    "updated": true
  }
}
POST /api/v1/chat/embedTokenRevoke Permanently revoke a token — the widget using it stops immediately; this cannot be undone.
ParameterTypeDescription
idrequiredintToken id (from embedTokens).
Response
{
  "success": true,
  "data": {
    "revoked": true
  }
}

Marketing & Intelligence API

A separate API family from the Caretta chat above — a distinct model surface you call to CREATE, not to converse. Same Bearer key. Generate marketing copy and complete strategies (marketingGenerate) and run structured intelligence over your KB (intelligence). Grounded on your own tenant and never-invent, just like the chat.

Marketing generator

A general-purpose marketer, separate from the chat. Give it a brief and it applies proven marketing frameworks (AIDA, PAS, STP, anchoring and more) to write copy. When you pass a site_id you own, it grounds every specific claim in that site’s knowledge base and products — it never invents a price, discount or feature. Without site_id it grounds on your account.
POST /api/v1/chat/marketingGenerate Generate marketing copy (headline, body, CTA, variants, hashtags) from a brief, grounded on your knowledge base.
ParameterTypeDescription
briefrequiredstringThe marketing brief / topic. Max 2,000 characters. Describes intent only — never treated as fact.
productoptionalstringProduct or plan name to feature.
audienceoptionalstringTarget audience (e.g. small businesses, developers).
goaloptionalstringCampaign goal (e.g. signups, awareness, sales).
toneoptionalstringDesired voice (e.g. energetic, warm, professional).
formatoptionalstringOutput format hint (e.g. social post, email, ad copy).
variantsoptionalintHow many alternative headlines to return (1–5, default 3).
site_idoptionalintOne of your site ids to ground on (from mySites). You must own it; an unowned or omitted id falls back to your account — never another tenant’s data.
langoptionalstringOutput language: tr, en or de. Defaults to your account language.
depthoptionalstringfull → a saturated deliverable (positioning, angles, objections, channel adaptations, proof, rationale, A/B). strategy → a COMPLETE 12-section marketing strategy (market analysis + personas + JTBD forces, positioning, messaging, full funnel, channel mix, content plan, measurement/KPIs, roadmap, experiments). Omit for the lean output below.
curl -X POST "https://www.wapiso.com/api/v1/chat/marketingGenerate" \
  -H "Authorization: Bearer wapi_pk_YOUR_KEY" \
  -d "brief=Introduce our email marketing plans" \
  -d "audience=small businesses" \
  -d "goal=signups" \
  -d "site_id=5"
Response
{
  "success": true,
  "data": {
    "content": {
      "headline": "Smart Email Marketing for Small Businesses",
      "body": "WMail sends from your own domain with AI templates and real-time analytics…",
      "cta": "Start free",
      "variants": [ "Automate your email with WMail", "Big impact for small teams" ],
      "hashtags": [ "EmailMarketing", "SmallBusiness", "WMail" ],
      "strategy_used": "Kotler STP + AIDA"
    },
    "meta": { "lang": "en", "grounded": true, "kb_used": 24, "products": 3, "tokens": 3379 }
  }
}

The content object

By default (lean) marketingGenerate returns data.content with these fields, plus data.meta (depth, grounded, kb_used, products, tokens). With depth=full it instead returns the saturated set: positioning, segment, funnel_stage, angles[], objections[], channel_adaptations[], proof_points[], hashtags[], strategy_rationale, next_experiment.

FieldTypeDescription
headlinestringPunchy, benefit-first headline.
bodystringThe main marketing copy.
ctastringOne clear call to action.
variantsarrayAlternative headlines/hooks.
hashtagsarrayRelevant hashtags (without #), or empty.
strategy_usedstringWhich framework(s) were applied.

Intelligence API

One grounded engine, many shapes. Pick an operation, a format and a depth and get a grounded, never-invent result over your own KB. Path: /api/v1/intelligence/<op>.

POST /api/v1/intelligence/<op> answer | summarize | compare | extract | data — grounded on your KB, returned in the format you ask for.
ParameterTypeDescription
oprequiredpathThe operation is the URL path: answer, summarize, compare, extract, or data (data is a direct, LLM-free query of your products/faq/categories).
formatoptionalstringtext | markdown | json | table | list. Shapes the output (table → {columns, rows}; list → {items}; json → {data}; text/markdown → {result}).
depthoptionalstringlean (default) or full (a more thorough result). Applies to the LLM ops.
(inputs)optionalOp-specific inputs alongside the above: question (answer), topic (summarize), items[] or query (compare), fields + topic (extract), type=products|faq|categories + page/limit/category/query (data).
site_id, langoptionalint, stringOne of your site ids to ground on (you must own it); otherwise your account KB. lang: tr|en|de.
curl -X POST "https://www.wapiso.com/api/v1/intelligence/compare" \
  -H "Authorization: Bearer wapi_pk_YOUR_KEY" \
  -d "query=email plans" \
  -d "format=table" \
  -d "depth=full"
Response
{
  "success": true,
  "data": {
    "op": "compare",
    "format": "table",
    "result": {
      "columns": [ "Feature", "Starter", "Growth" ],
      "rows": [ [ "Own domain", "Yes", "Yes" ], [ "Real-time analytics", "—", "Yes" ] ]
    },
    "meta": { "depth": "full", "lang": "en", "grounded": true, "kb_used": 24, "products": 3, "tokens": 2100 }
  }
}

Returns data.result shaped by format, plus data.meta (op, format, depth, grounded, kb_used, products, tokens). The data op returns rows and a total in meta.

Responses & errors

The lowConfidence flag

send returns data.lowConfidence: true when the assistant could not answer confidently (low confidence, or the request was declined — e.g. the balance ran out). Use it to decide what to show on your side: a fallback message, or a link to your own contact page. The API only answers — what happens next is entirely up to your application.

Status codes

200Success (including the “out of balance” reply — inspect the message body and lowConfidence).
400Bad request (e.g. empty or too-long message).
401Missing, invalid, or regenerated (old) API key.
403Embed surface only — the call came from a domain outside the token’s allowed list, a write arrived without an Origin header, or the action is outside the token’s scope.
429Rate limited — you exceeded a request rate (see Limits). The response carries a Retry-After header with the seconds to wait.
500Server error. Safe to retry shortly.

Balance exhausted

When your balance can’t cover a request, the call still returns HTTP 200 — it is not an error. The assistant message explains the balance is insufficient, data.lowConfidence is true, and you are not charged. Top up to resume.

// HTTP 200 — NOT an error, and NOT charged
{
  "success": true,
  "data": {
    "conversationId": 1234,
    "messages": [
      { "id": 9004, "role": "assistant",
        "body": "Your account balance isn't enough to answer this message.",
        "source": "system" }
    ],
    "lowConfidence": true
  }
}

Limits

Limits are counted per API key (account-wide, shared by all your site’s visitors) — not per visitor.

Repeat questions are free against your quota

The daily limit only counts unique questions that miss the knowledge base and reach the AI. Answers served from your knowledge base (repeated or similar questions) are returned from cache before the quota is checked — they never consume it. So 1,000 visitors asking mostly the same things barely touch the daily cap once your knowledge base is warm. Build it up by crawling your site and adding Q&A.

Example: 1,000 daily visitors on a Starter plan is fine if their questions overlap — most are cache hits. The 200/day cap only bites if you get more than 200 genuinely distinct, never-seen questions in one day. Need more? Move up a plan or ask us to raise your account.

Daily unique-answer limit by plan

Starter 200 Unique AI answers / day
Growth 1,000 Unique AI answers / day
Scale 5,000 Unique AI answers / day
Custom Unlimited / negotiated Unique AI answers / day

Other limits

LimitValueAt the limit
Message length2,000 charactersA longer message returns HTTP 400. Trim or split before sending. (The embed surface has its own 1,000-character limit — see Embed Widget.)
Send rate15 sends/min per keyFaster sending returns HTTP 429 with a Retry-After header — wait that many seconds, or queue on your side.
Context windowlast 30 messagesThe assistant reads the most recent 30 messages of the conversation; older turns are summarized automatically.
Need higher limits for production traffic? Reach out from the Contact page — limits are per-account and can be raised.

Per-message pricing

Messages are billed by real token usage. The figure below is the typical all-in cost of one answer — the main reply plus the automatic planning, quality-check and context-summary sub-calls that run on a normal send. This is the same number shown on your dashboard.

Typical per message $0.0355 USD / messages
Estimated spend at the typical rateEst. cost
1,000 messages $35.50
10,000 messages $355.00
100,000 messages $3,550.00
Token typePer 1M tokens
Input tokens$3.00
Output tokens$25.00
Billing is metered on real token counts — every request's processed input/output tokens are measured; the per-message price above is these rates applied to an average message.
Repeat and knowledge-base answers are served from cache and are effectively free — only unique answers that reach the AI are billed. Plain greetings (“hi”, “how are you”) are answered locally and are always free.

Embed Widget

Drop Caretta Assistant onto any page of yours with one script tag. No backend needed: the widget uses a PUBLIC embed token (wapi_emb_…) that is safe to ship in HTML — protection comes from a server-side domain lock, per-visitor/IP rate limits and a hard daily budget you set.

Install

Create a token in the dashboard (Developer → Site Assistant (Embed)), then paste:

<script src="https://www.wapiso.com/embed.js?v=1788352057"
        data-wapi-token="wapi_emb_YOUR_TOKEN"
        data-wapi-lang="en"
        data-wapi-color="#0061FF" defer></script>

How it works

  • Public token (wapi_emb_…) — designed to be visible in your page source. It is NOT a secret; never confuse it with your wapi_pk_ server key.
  • Domain lock — the server only accepts requests whose Origin matches your token’s allowed domains (subdomains included). Other sites get HTTP 403.
  • Per-visitor conversations — each visitor gets an anonymous UUID (localStorage) and an isolated thread; visitors can never read each other’s messages.
  • Daily budget — you set a hard USD cap per token. When spend reaches it, the widget is NOT switched off: it degrades to cache-only mode — frequent questions keep getting answered from your knowledge base for free, new AI calls pause until the next UTC day, and you get an email notification. Answers are billed to YOUR balance at the same per-message rates above.
  • Honest answers only — the widget answers from your knowledge base. When it can’t, it says so; there is no human-handover promise to your visitors (a lowConfidence hint suggests rephrasing).

Widget rate limits

Per visitor: 10 messages/min, 100/day
Per token: 60 requests/min
Per IP: 30 requests/min
Per message: 1,000 characters (longer returns HTTP 400)
On top of the daily budget, always-on surge protection watches for abnormal spikes (e.g. a bot hammering your public token). When it trips, the widget quietly switches to the same cache-only mode instead of going down: visitors still get answers to common questions, nothing extra is billed, and it recovers on its own.
The script tag above is the supported way to put Caretta on a page. The HTTP calls the widget makes between your page and us are internal plumbing — they are not documented, not versioned, and change without notice; the boot handshake changed the same week this note was written. If you need your own chat interface, use the server-side Chat API with your secret wapi_pk_ key instead: it is the surface we keep stable for you.

Manage tokens

Create, revoke and budget tokens under Developer → Site Assistant (Embed) in your dashboard — the snippet is generated there for copy-paste. Prefer code? The same operations are available server-side with your wapi_pk_ key — see “Embed tokens” in the API reference above.

Security

Two credential types: wapi_pk_ is your SECRET server key — never put it in a web page, browser code or a public repository (anyone who reads your HTML would own your account). wapi_emb_ is the PUBLIC embed token — that one belongs in the page and is protected by domain lock + budget instead of secrecy.
  • Never put your wapi_pk_ key in public front-end code or a public repository. Call the API from your backend.
  • Rotate the key any time from the dashboard. The previous key is revoked the instant you regenerate.
  • Every request is rate-limited and billed; monitor usage under Developer → API Usage.

Need help?

Open the chat on your dashboard and type your question, or reach our team from the Contact page.