Claude
Skills
Sign in
Back

functions

Included with Lifetime
$97 forever

Build serverless TypeScript functions on Zavu Cloud — declare agents + tools in code with defineAgent / defineTool, deploy with `zavu deploy`, debug with `zavu agents executions`. Use this skill whenever the user wants code-driven AI agents, custom tool handlers, or event-driven business logic.

Cloud & DevOps

What this skill does


# Zavu Functions

Zavu Functions = serverless TypeScript on Zavu Cloud + a declarative framework for AI agents.

```ts
import { defineAgent, defineTool } from "@zavu/functions"

defineAgent({
  senderId: process.env.SENDER_ID!,
  name: "Bella",
  provider: "zavu",
  model: "openai/gpt-4o-mini",
  prompt: "You are Bella, host at the restaurant. Be brief.",
})

defineTool({
  name: "check_availability",
  description: "Get free reservation slots for a date.",
  parameters: {
    type: "object",
    properties: { date: { type: "string" }, partySize: { type: "number" } },
    required: ["date", "partySize"],
  },
  handler: async ({ date, partySize }) => {
    return { available: true, slots: ["19:00", "21:00"] }
  },
})
```

That's a full agent + tool. `zavu deploy` reconciles the live state.

## When to use Functions vs the imperative AI Agent API

| Use case | Use |
|---|---|
| Customer wants a code-first agent with custom tool handlers in their own language | **Functions** |
| Tools need to query the user's database, call internal APIs, or transform data before returning | **Functions** |
| User wants reproducible config from a git repo (one source of truth) | **Functions** |
| User wants no-code config via the dashboard | imperative `senders.agent.create` API (see `ai-agent` skill) |
| User needs event-driven handlers (`message.inbound`, `broadcast.completed`) without dashboard wiring | **Functions** |

If the user mentions writing code, `defineAgent`, `defineTool`, `zavu deploy`, or "serverless" — use this skill. Otherwise route to `ai-agent`.

## CLI as primary interface

Functions are managed entirely via the `zavu` CLI, not API calls. Install once:

```sh
brew install zavudev/tools/zavu
# or grab a standalone binary from https://github.com/zavudev/zavu-cli/releases

zavu login
```

`zavu login` opens the browser and stores credentials in `~/.zavu/credentials.json`.

## Full lifecycle

### 1. Scaffold

```sh
zavu fn init order-bot --template blank
cd order-bot
```

Templates available: `blank`, `restaurant-booking`, `school-parent-notify`, `ecommerce-order-bot`.

The init writes `index.ts`, `package.json`, and a `.zavu/config.json` that links this directory to a Function record in the user's project. Once linked, every subsequent command auto-resolves the function.

### 2. Set secrets

Secrets are encrypted env vars injected into the function at deploy time.

```sh
zavu fn secrets set SENDER_ID jx7abc123def456
zavu fn secrets set DATABASE_URL "postgres://..."
zavu fn secrets list
zavu fn secrets unset OLD_KEY
```

Get the sender ID from `zavu senders list`.

### 3. Author the agent + tools

Edit `index.ts`:

```ts
import { defineAgent, defineTool, defineFunction } from "@zavu/functions"

defineAgent({
  senderId: process.env.SENDER_ID!,
  name: "Bella",
  provider: "zavu",              // Zavu's AI gateway (charged from project balance)
                                  // Or "openai" / "anthropic" / "google" / "mistral" with BYOK + apiKey
  model: "openai/gpt-4o-mini",   // For "zavu" provider, prefix with the underlying provider
  prompt: "You are Bella…",       // System prompt
  channels: ["whatsapp"],         // Optional: default ["*"] = all channels the sender supports
  // apiKey: process.env.OPENAI_API_KEY  // only for non-zavu providers
})

defineTool({
  name: "lookup_order",
  description: "Get current status of an order. Use when the customer asks about an order they placed.",
  parameters: {
    type: "object",
    properties: { orderId: { type: "string" } },
    required: ["orderId"],
  },
  handler: async (args, ctx) => {
    // ctx: { projectId, functionId, slug, awsRequestId, messageId?, contactPhone?, sessionId?, log }
    const res = await fetch(`https://pos.example.com/orders/${args.orderId}`, {
      headers: { Authorization: `Bearer ${process.env.POS_API_KEY}` },
    })
    return await res.json()
  },
})

// Optional: handle raw events (message.inbound from triggers, HTTP calls).
// NOT needed if you only declare agent + tools.
export default defineFunction(async (event, ctx) => {
  ctx.log("got event", event.type)
})
```

### 4. Deploy

```sh
zavu deploy
```

Output:

```
✓ Deployed in 6.4s
  Agents:
    + Bella           (sender_abc, whatsapp)
  Tools:
    + lookup_order    → Bella
```

The reconcile is idempotent — re-running with no changes shows `0 created, 0 updated, 0 deleted`.

### 5. Test

**Local invocation (skip cloud round-trip):**

```sh
# Call a tool handler with synthetic args
zavu fn invoke --tool lookup_order --args '{"orderId":"ORD-001"}'

# Simulate an inbound event for defineFunction
zavu fn invoke --event message.inbound --data '{"from":"+14155551234","text":"hi"}'
```

**End-to-end:** send a real message to the sender's WhatsApp/SMS/Telegram number. The agent runs the LLM, calls tools, and replies via the same channel.

### 6. Debug

When something fails, walk the chain top-down:

```sh
# 1. Did the inbound reach the agent?
zavu agents executions list --sender <senderId>

# 2. Detail of any failed run
zavu agents executions get <executionId> --sender <senderId>

# 3. Live tool handler logs (your console.log calls)
zavu fn logs --tail
```

The `--json` flag on `executions list` returns the full payload including `errorMessage` for parseable diagnostics.

## defineAgent reference

```ts
defineAgent({
  senderId: string,              // Required. The sender that receives inbound + dispatches the agent.
  name: string,                  // Required. Displayed in dashboard.
  provider: "zavu" | "openai" | "anthropic" | "google" | "mistral",
  model: string,                 // For "zavu": prefix with underlying provider e.g. "openai/gpt-4o-mini"
  prompt: string,                // System prompt.
  apiKey?: string,               // Required for non-"zavu" providers.
  channels?: string[],           // Default ["*"]. Subset of: sms, whatsapp, telegram, email, instagram, voice
  messageTypes?: string[],       // Default ["text"]. Filter by message type.
  temperature?: number,          // 0-2.
  maxTokens?: number,            // Cap on output tokens.
  contextWindowMessages?: number,// Past N messages included as context. Default 10.
  sessionTimeoutMinutes?: number,// Reset conversation context after N minutes. Default 60.
  includeContactMetadata?: boolean, // Inject contact's metadata into the system prompt. Default true.
  enabled?: boolean,             // Default true.
})
```

## defineTool reference

```ts
defineTool({
  name: string,                  // Required. snake_case, max 64 chars.
  description: string,           // Required. The LLM reads this to decide WHEN to call the tool.
  parameters: {
    type: "object",
    properties: { /* JSON Schema */ },
    required?: string[],
  },
  handler: async (args, ctx) => any,  // Required. Return any JSON-serializable value.
  agent?: string,                // Optional: which agent owns this tool. Defaults to the only agent in the file.
  enabled?: boolean,             // Default true.
})
```

### Handler `ctx` shape

```ts
{
  projectId: string,
  functionId: string,
  slug: string,
  awsRequestId: string,
  messageId?: string,            // ID of the triggering inbound (when called by agent)
  contactPhone?: string,
  sessionId?: string,            // Active flow session if any
  log: (...args) => void,        // console.log proxy that appears in `zavu fn logs --tail`
}
```

## defineFunction reference (optional)

Use only if you want to handle:
- **Raw HTTP requests** (function exposed at a public URL — set `httpEnabled: true` via dashboard)
- **Native event triggers** (`message.inbound`, `broadcast.completed`, etc — configured via `zavu fn triggers add`)

```ts
export default defineFunction(async (event, ctx) => {
  if (event.type === "message.inbound") {
    // event.data: { from, text, channel, messageId, ... }
  }
  return { ok: true }
})
```

## Triggers (event subscriptions)

To make `defineFunction` react to Zavu events:

```sh
zavu fn triggers
Files: 1
Size: 13.6 KB
Complexity: 19/100
Category: Cloud & DevOps

Related in Cloud & DevOps