{% extends "base.html" %} {% block title %}Examples — Waffle{% endblock %} {% block content %}
{{ request.url.scheme }}://{{ request.url.netloc }}.
Every recipe below assumes a Poetry project with the SDK installed.
mkdir my-agent && cd my-agent poetry init --no-interaction --name my-agent --python ">=3.11,<4.0" poetry add heywaffle claude-agent-sdk
from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient, ResultMessage
from waffle_sdk import AgentContext, run
async def handle(item: dict, ctx: AgentContext) -> dict:
options = ClaudeAgentOptions(
system_prompt="You are a helpful assistant.",
permission_mode="bypassPermissions",
)
async with ClaudeSDKClient(options=options) as client:
await client.query(item["prompt"])
async for msg in client.receive_response():
if isinstance(msg, ResultMessage):
return {"reply": msg.result or ""}
return {"reply": ""}
if __name__ == "__main__":
run(handle,
agent="hello",
project="default",
subscriptions=[{"source": "hello", "filter": {}}])For iterating on prompts, debugging tool-use, or calling the agent as a one-off script. The SDK will try to stream the Claude session JSONL to the hub if it's reachable; otherwise it silently runs offline.
poetry run python agent.py once --input '{"prompt":"hi"}'Force-disable trace streaming:
poetry run python agent.py once --input '{"prompt":"hi"}' --no-traceOr point at a remote hub:
poetry run python agent.py once --input '{"prompt":"hi"}' \
--hub {{ request.url.scheme }}://{{ request.url.netloc }}
The agent connects to the hub, claims items from its queue over SSE, and handles them
one (or concurrency) at a time. Items arrive via POST /webhooks/<source>
whenever an external system (Linear, GitHub, n8n, your own app) sends a payload that
matches the agent's subscriptions filter.
poetry run python agent.py inbox \
--hub {{ request.url.scheme }}://{{ request.url.netloc }}curl -sX POST {{ request.url.scheme }}://{{ request.url.netloc }}/webhooks/hello \
-H 'content-type: application/json' \
-d '{"prompt":"what's the capital of Peru?"}'The hub returns the inbox item id immediately. The result appears in the UI's Inbox view + as a session in Sessions once the handler finishes.
subscriptions=[
# Fire only when the payload contains action:"create"
{"source": "linear", "filter": {"action": "create"}},
# Nested keys match by path — type.name == "Issue"
{"source": "linear", "filter": {"type": {"name": "Issue"}}},
# Empty filter matches anything from that source
{"source": "hello", "filter": {}},
]
The hub's scheduler fires your agent on a cron expression. Each tick creates an inbox
item with source matching the cron's source and a payload you supply.
Useful for daily digests, periodic checks, reminders.
if __name__ == "__main__":
run(handle,
agent="daily-check",
project="default",
crons=[
{"schedule": "0 9 * * *", "source": "cron", "payload": {"prompt": "morning check"}},
{"schedule": "0 17 * * 1-5", "source": "cron", "payload": {"prompt": "end-of-day"}},
])Keep the agent running in inbox mode — crons create inbox items, which the agent claims the same way as webhook-triggered items.
POST /api/invoke/<agent> blocks until the agent finishes and returns the
result in one HTTP response — no inbox polling, no SSE. Useful for AI features in a web
app, Slack slash commands, anywhere you just need an answer.
curl -sX POST {{ request.url.scheme }}://{{ request.url.netloc }}/api/invoke/hello \
-H 'content-type: application/json' \
-d '{"prompt":"summarise Waffle in one sentence"}'const res = await fetch("{{ request.url.scheme }}://{{ request.url.netloc }}/api/invoke/hello", {
method: "POST",
headers: {
"content-type": "application/json",
"Authorization": "Bearer " + YOUR_TOKEN, // only needed when WAFFLE_API_TOKEN is set
},
body: JSON.stringify({ prompt: "summarise Waffle" }),
});
const data = await res.json();
console.log(data);
The agent must be running in inbox mode when you call /api/invoke —
the hub routes the item to a free worker and holds the HTTP response open until it completes.
# /etc/systemd/system/waffle.service Environment=WAFFLE_CORS_ORIGINS=https://app.example.com,http://localhost:3000 Environment=WAFFLE_API_TOKEN=$(openssl rand -hex 32) # generate once, keep secret
Each agent is an independent Python process. On a single machine, run them side by side
with a process manager like honcho
(a Python port of foreman) — one systemd unit for all agents, add a line to the
Procfile for each new one.
# ~/waffle/Procfile hello: bash -c 'cd /home/sqrt/agents/hello && poetry run python agent.py inbox' triage: bash -c 'cd /home/sqrt/agents/linear-triage && poetry run python agent.py inbox' scraper: bash -c 'cd /home/sqrt/agents/recipe-scraper && poetry run python agent.py inbox'
See deploy/ in the Waffle repo for a full systemd + Caddy example.