Developer API · v1
Build on CAAC Go
Send WhatsApp messages and generate AI replies from your own apps. The API is REST over HTTPS, JSON in and out, authenticated with a workspace API key.
AI coding tools & agents
Everything here is machine-readable. Point your agent at it instead of pasting docs by hand.
Install the agent skill
A portable SKILL.md that teaches Claude Code, Codex, Cursor or Gemini CLI how to install and verify the widget. No API key, works on every plan.
mkdir -p .agents/skills/caac-go && curl -sL https://caacgo.cresclab.com/caac-go-skill/SKILL.md -o .agents/skills/caac-go/SKILL.mdSwap .agents/skills for .claude/skills if your agent reads that path instead.
Connect the MCP server
Lets an agent read your install snippet, run the install check, update the widget, and add knowledge sources. Authenticates with a workspace API key, so it needs the Growth plan or above — installing the widget itself needs neither a key nor an upgrade.
https://caacgo.cresclab.com/api/mcp
Authorization: Bearer cwa_your_keyAsk an assistant about these docs
Base URL & authentication
All requests go to the base URL below. Pass your key (created under Settings → API keys) in the Authorization header. Keep it secret — it grants access to your workspace.
Base URL https://caacgo.cresclab.com
Header Authorization: Bearer cwa_your_keyEndpoints
/api/v1/meVerify a key and return the workspace it belongs to.
curl https://caacgo.cresclab.com/api/v1/me \
-H "Authorization: Bearer cwa_your_key"Response
{ "ok": true, "workspace": { "id": "…", "name": "Glow Skin Clinic", "plan": "free" } }/api/v1/messagesSend a WhatsApp text message. Subject to WhatsApp's 24-hour customer-care window and template rules for proactive sends.
curl -X POST https://caacgo.cresclab.com/api/v1/messages \
-H "Authorization: Bearer cwa_your_key" \
-H "Content-Type: application/json" \
-d '{"to":"+5511988887777","text":"Hi! Thanks for reaching out 👋"}'Response
{ "ok": true }/api/v1/ai/replyGenerate an AI reply from your business content. Pass url to read answers from a website, or context with raw text.
curl -X POST https://caacgo.cresclab.com/api/v1/ai/reply \
-H "Authorization: Bearer cwa_your_key" \
-H "Content-Type: application/json" \
-d '{"message":"How much is a haircut?","url":"https://your-site.com"}'Response
{ "reply": "A men's haircut is $30 and takes about 30 minutes — want me to book you in?" }Webhooks
Get notified when things happen in your workspace. Add an endpoint in Settings → Webhooks and we’ll POST a signed JSON body to it when these events fire:
message.received— A visitor sent a message to your agent.conversation.escalated— The AI flagged a conversation for a human to follow up.contact.created— A new contact / lead was captured.lead.captured— The AI saved a visitor as a lead (the money event).appointment.requested— The AI recorded a booking request for your team to confirm.
Delivery
POST https://yourapp.com/webhooks/caac
Content-Type: application/json
X-CAAC-Event: message.received
X-CAAC-Signature: <hex HMAC-SHA256 of the raw body, keyed with your signing secret>
{
"id": "5f1c…",
"event": "message.received",
"createdAt": "2026-06-23T08:00:00.000Z",
"data": {
"conversationId": "…", "contactId": "…",
"channelType": "web", "body": "Hi! Do you ship overseas?",
"contact": { "name": null, "phone": null, "externalId": "…" }
}
}More payloads
// conversation.escalated — AI handed off; open a ticket in your helpdesk.
// Carries the requester identity + recent transcript so you can create the
// ticket straight from this event (contact is null if the visitor gave none).
// contact.metadata holds attributes you passed via the widget identify() API
// (see below) — e.g. the logged-in user's plan / your member id — so you can
// reverse-look-up your own DB on handoff.
{ "event": "conversation.escalated", "data": {
"conversationId": "…", "contactId": "…",
"question": "Can I get a refund on order #1234?",
"contact": {
"name": "Alex Tan", "email": "alex@example.com", "phone": "+6591234567",
"metadata": { "plan": "growth", "userId": "u_123" }
},
"transcript": [
{ "role": "visitor", "text": "Can I get a refund on order #1234?" },
{ "role": "agent", "text": "Let me connect you with the team." }
]
} }
// lead.captured — the money event
{ "event": "lead.captured", "data": {
"conversationId": "…", "contactId": "…",
"name": "Alex Tan", "email": "alex@example.com",
"phone": "+6591234567", "interest": "pricing"
} }
// appointment.requested
{ "event": "appointment.requested", "data": {
"conversationId": "…", "contactId": "…",
"service": "Consultation", "preferredTime": "Fri 3pm",
"name": "Alex Tan", "phone": "+6591234567"
} }Verify the signature (Node)
import crypto from "node:crypto";
const signature = req.headers["x-caac-signature"];
const expected = crypto.createHmac("sha256", WEBHOOK_SIGNING_SECRET)
.update(rawBody) // the exact raw request body string
.digest("hex");
const ok = signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));Respond 2xx to acknowledge. The last delivery status is shown next to each endpoint, and you can send a test event from the Webhooks page.
Pass visitor identity & context
Call caacgo("identify", …) with whatever you know about the visitor. email & name populate the contact; every other field lands in contact.metadata and — this is the powerful part — the AI sees these attributes when it answers. Combine with your agent's custom instructions to adapt by segment: “if plan is trial, mention the upgrade; if cart_items is set, help them check out.” The same fields also arrive on the conversation.escalated webhook, so on handoff you can reverse-look-up your own member DB. Values are asserted by your page — the AI uses them for tailoring only, and you should verify them against your own records before granting anything (signed identity verification is on the roadmap).
<script src="https://caacgo.cresclab.com/widget.js" data-key="wk_xxx"></script>
<script>
// call with anything you know — logged-in identity, funnel stage, cart state:
caacgo("identify", {
email: user.email, // -> contact.email
name: user.fullName, // -> contact.name
plan: user.plan, // -> contact.metadata.plan (visible to the AI)
stage: "trial", // -> contact.metadata.stage (visible to the AI)
userId: user.id // -> contact.metadata.userId
});
</script>Errors
Errors return the matching HTTP status with a JSON body: { "error": "…" }. Each endpoint is rate-limited per key (/me 120, /messages 60, /ai/reply 20 requests per minute) — back off and retry on 429.
| Status | Meaning |
|---|---|
| 400 | Invalid or missing parameters. |
| 401 | Missing or invalid API key. |
| 422 | Could not read the provided URL. |
| 429 | Rate limit exceeded — slow down and retry. |
| 502 | Upstream WhatsApp or AI call failed. |
OpenAPI spec
Browse the full API in the interactive reference, or import the machine-readable spec into Postman, Insomnia or Swagger UI:
Ready to start? Create your API key →