Kapso Functions are serverless JavaScript functions that deploy instantly and scale automatically. Perfect for webhooks, API integrations, and custom business logic.

Quick Start

# Set your API key (no project setup needed!)
export KAPSO_API_KEY=your-api-key

# Create a function
echo 'async function handler(request) {
  return new Response("Hello World");
}' > hello.js

# Deploy it
kapso functions push hello.js

# List your functions
kapso functions list

Writing Functions

Functions use the modern Cloudflare Workers syntax with access to persistent storage:
async function handler(request, env) {
  // Parse JSON body
  const body = await request.json();
  
  // Use KV storage for persistence
  await env.KV.put('last-visitor', body.name);
  const lastVisitor = await env.KV.get('last-visitor');
  
  // Process the data
  const result = {
    message: `Hello ${body.name}!`,
    lastVisitor: lastVisitor,
    timestamp: new Date().toISOString()
  };
  
  // Return JSON response
  return new Response(JSON.stringify(result), {
    headers: { 'Content-Type': 'application/json' }
  });
}

Available APIs

Functions run on Cloudflare Workers with access to:
  • 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.YOUR_SECRET - Access encrypted secrets (set in web app)
  • Standard JavaScript APIs

Environment Secrets

Functions can access encrypted environment variables for API keys and sensitive configuration. Secrets are set in the web app on the function’s page (Secrets tab) and accessed via the env parameter:
async function handler(request, env) {
  // Access your secrets
  const apiKey = env.API_KEY;
  const dbUrl = env.DATABASE_URL;
  const webhookSecret = env.WEBHOOK_SECRET;
  
  // Use them in your function
  const response = await fetch('https://api.example.com/data', {
    headers: {
      'Authorization': `Bearer ${apiKey}`
    }
  });
  
  return new Response('Success');
}
Important:
  • 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:
// Store data (with optional expiration)
await env.KV.put('user:123', JSON.stringify(userData));
await env.KV.put('session', token, { expirationTtl: 3600 }); // 1 hour

// Retrieve data
const user = await env.KV.get('user:123', { type: 'json' });
const session = await env.KV.get('session');

// Delete data
await env.KV.delete('user:123');

// List keys with prefix
const list = await env.KV.list({ prefix: 'user:' });

Example: WhatsApp Message Webhook

async function handler(request, env) {
  try {
    // Verify webhook signature for security
    const signature = request.headers.get('X-Webhook-Signature');
    const secret = env.WEBHOOK_SECRET; // Set in Secrets tab in web app
    
    // Parse the webhook payload
    const webhook = await request.json();
    
    // Handle different WhatsApp events
    const eventType = request.headers.get('X-Webhook-Event');
    
    switch (eventType) {
      case 'whatsapp.message.received':
        // Process incoming WhatsApp message
        const { message, conversation, whatsapp_config } = webhook;
        
        console.log(`New message from ${message.phone_number}: ${message.content}`);
        
        // Store conversation history in KV
        const history = await env.KV.get(`conversation:${conversation.id}`, { type: 'json' }) || [];
        history.push({
          timestamp: new Date().toISOString(),
          phone: message.phone_number,
          content: message.content
        });
        await env.KV.put(`conversation:${conversation.id}`, JSON.stringify(history));
        
        // Send to your CRM or ticketing system
        await fetch('https://api.yourcrm.com/messages', {
          method: 'POST',
          headers: { 
            'Authorization': `Bearer ${env.CRM_API_KEY}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            phone: message.phone_number,
            message: message.content,
            whatsapp_message_id: message.whatsapp_message_id,
            conversation_id: conversation.id,
            message_count: history.length
          })
        });
        break;
      
      case 'whatsapp.conversation.created':
        // New conversation started
        console.log(`New conversation with ${webhook.conversation.phone_number}`);
        break;
        
      case 'whatsapp.message.failed':
        // Handle failed message delivery
        console.error(`Message failed: ${webhook.message.id}`);
        break;
    }
    
    // Always return 200 to acknowledge receipt
    return new Response('OK', { status: 200 });
    
  } catch (error) {
    console.error('Webhook processing error:', error);
    // Return 200 even on error to prevent retries for bad data
    return new Response('Error logged', { status: 200 });
  }
}

CLI Commands

kapso functions push <file.js>

Upload or update a function. The function name is derived from the filename.
# Create or update function
kapso functions push process-order.js

# Use custom name
kapso functions push utils/helper.js --name my-helper

kapso functions list

List all functions with their status and invoke URLs.
kapso functions list

# Output:
# NAME              STATUS     UPDATED      INVOKE URL
# process-order     deployed   2 min ago    https://app.kapso.ai/api/v1/functions/abc123/invoke
# send-email        deployed   1 hour ago   https://app.kapso.ai/api/v1/functions/def456/invoke

kapso functions pull <name>

Download a function from Kapso.
# Download to functions directory
kapso functions pull process-order

# Download to specific location
kapso functions pull process-order -o my-function.js

Using Functions in Agents

Call functions from your agents using SubagentNode with WebhookTool:
from kapso.builder.nodes import SubagentNode
from kapso.builder.nodes.subagent import WebhookTool

# Create a SubagentNode
assistant = SubagentNode(
    name="assistant",
    prompt="Help users with their inquiries"
)

# Add your function as a webhook tool
assistant.add_tool(WebhookTool(
    name="process_whatsapp_webhook",
    url="https://app.kapso.ai/api/v1/functions/abc123/invoke",
    http_method="POST",
    description="Process WhatsApp message webhooks",
    headers={"X-API-Key": "#{KAPSO_API_KEY}"},
    body_schema={
        "type": "object",
        "properties": {
            "message": {
                "type": "object",
                "properties": {
                    "content": {"type": "string"},
                    "phone_number": {"type": "string"},
                    "message_type": {"type": "string"}
                }
            },
            "conversation": {
                "type": "object",
                "properties": {
                    "id": {"type": "string"},
                    "status": {"type": "string"}
                }
            }
        },
        "required": ["message", "conversation"]
    }
))

Best Practices

  1. Always return JSON for API responses
  2. Handle errors gracefully with try/catch
  3. Validate input before processing
  4. Use appropriate status codes (200, 400, 404, 500)
  5. Keep functions focused on a single task
  6. Log important events with console.log()

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');
}