Documentation Index
Fetch the complete documentation index at: https://docs.kapso.ai/llms.txt
Use this file to discover all available pages before exploring further.
Kapso Functions run on Cloudflare Workers. Use them for webhooks, custom business logic, workflow steps, and agent tools.
Create and deploy
- Open Functions in your project
- Create or edit a function
- Deploy it
- Attach it to a workflow node, an agent tool, or call its endpoint directly
The Kapso CLI does not manage functions yet. Use the dashboard or API for function create/update/deploy.
Invoke endpoints
Deployed Cloudflare functions expose a Kapso-hosted invoke URL:
https://api.kapso.ai/platform/v1/functions/{function_id}/invoke
public_endpoint: false (default) keeps the invoke URL private. Send X-API-Key with the request.
public_endpoint: true allows invoke requests without an API key. This is only supported for Cloudflare functions.
- Private and unknown functions both return
404 from the invoke route.
- Newly created functions use
invoke_response_mode: passthrough. Successful invoke responses preserve the function body, success status code, and Content-Type.
- Older wrapped functions can be updated to
passthrough if you need to remove the legacy { data: ... } wrapper.
Runtime contract
Kapso Cloudflare Worker functions must define:
async function handler(request, env) {
const body = await request.json().catch(() => ({}));
return new Response(JSON.stringify({
ok: true,
received: body
}), {
headers: { "Content-Type": "application/json" }
});
}
Kapso wraps your code and calls handler(request, env). Do not use export default.
Bindings and env
async function handler(request, env) {
const body = await request.json().catch(() => ({}));
await env.KV.put("last-request", JSON.stringify(body));
const { results } = await env.DB.prepare(
"SELECT id, email FROM customers ORDER BY created_at DESC LIMIT 5"
).all();
return new Response(JSON.stringify({
recentCustomers: results
}), {
headers: { "Content-Type": "application/json" }
});
}
fetch() - Make HTTP requests
Request/Response - Handle HTTP
URL/URLSearchParams - Parse URLs
crypto.randomUUID() - Generate IDs
TextEncoder/TextDecoder - Text encoding
env.KV - Persistent key-value storage
env.DB - Project database bound as Cloudflare D1
env.YOUR_SECRET - Access encrypted secrets set in the function page
- Standard JavaScript APIs
Secrets
Secrets are available as string keys on env:
async function handler(request, env) {
const apiKey = env.API_KEY;
const webhookSecret = env.WEBHOOK_SECRET;
const response = await fetch("https://api.example.com/data", {
headers: {
Authorization: `Bearer ${apiKey}`
}
});
return new Response("Success");
}
- Secrets must be set in the Kapso web app (function page → Secrets tab)
- Secret names should use UPPERCASE_WITH_UNDERSCORES
- Values are encrypted and never exposed after creation
- Functions must be deployed before adding secrets
KV storage
Each project has its own KV namespace for persistent data storage:
await env.KV.put("user:123", JSON.stringify(userData));
await env.KV.put("session", token, { expirationTtl: 3600 });
const user = await env.KV.get("user:123", { type: "json" });
const session = await env.KV.get("session");
await env.KV.delete("user:123");
const list = await env.KV.list({ prefix: "user:" });
Functions in flows
Kapso sends workflow data in the JSON request body. Parse it with await request.json().
Function node
async function handler(request, env) {
const body = await request.json();
const executionContext = body.execution_context || {};
const vars = executionContext.vars || {};
const context = executionContext.context || {};
const user = {
phoneNumber: context.phone_number,
plan: vars.plan || "free"
};
return new Response(JSON.stringify({
vars: {
user
}
}), {
headers: { "Content-Type": "application/json" }
});
}
Payload keys:
execution_context
flow_info
flow_events
whatsapp_context when the run comes from WhatsApp
Decide node
async function handler(request, env) {
const body = await request.json();
const availableEdges = body.available_edges || [];
const vars = body.execution_context?.vars || {};
let nextEdge = availableEdges[0] || "default";
if ((vars.customer_tier || "").toLowerCase() === "vip" && availableEdges.includes("vip")) {
nextEdge = "vip";
}
return new Response(JSON.stringify({
next_edge: nextEdge
}), {
headers: { "Content-Type": "application/json" }
});
}
next_edge is only used by decide nodes.
Agent tool arguments are inside body.input, not at the root:
async function handler(request, env) {
const body = await request.json();
const input = body.input || {};
const vars = body.execution_context?.vars || {};
return new Response(JSON.stringify({
vars: {
last_lookup_email: input.email || null
}
}), {
headers: { "Content-Type": "application/json" }
});
}
Payload keys:
input - tool arguments chosen by the agent
execution_context
flow_info
flow_events
whatsapp_context when the run comes from WhatsApp
Common patterns
WhatsApp CRM integration with session tracking
async function handler(request, env) {
const webhook = await request.json();
// Only process message received events
if (request.headers.get('X-Webhook-Event') !== 'whatsapp.message.received') {
return new Response('OK');
}
// Extract customer info
const { message, conversation } = webhook;
const customerPhone = message.phone_number;
// Track customer session in KV
const sessionKey = `session:${customerPhone}`;
const session = await env.KV.get(sessionKey, { type: 'json' }) || {
firstContact: new Date().toISOString(),
messageCount: 0
};
session.lastMessage = message.content;
session.lastContact = new Date().toISOString();
session.messageCount++;
// Store session with 24-hour expiration
await env.KV.put(sessionKey, JSON.stringify(session), {
expirationTtl: 86400 // 24 hours
});
// Create or update CRM contact with session data
await fetch('https://api.hubspot.com/contacts/v1/contact', {
method: 'POST',
headers: {
'Authorization': `Bearer ${env.HUBSPOT_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
properties: [
{ property: 'phone', value: customerPhone },
{ property: 'last_whatsapp_message', value: message.content },
{ property: 'whatsapp_conversation_id', value: conversation.id },
{ property: 'total_messages', value: session.messageCount },
{ property: 'first_contact_date', value: session.firstContact }
]
})
});
return new Response('OK');
}