{% extends "base.html" %} {% block title %}Getting Started — Waffle{% endblock %} {% block content %}

Getting Started

A linear walkthrough — do step 1, then 2, then 3. Hub URLs are filled in for {{ request.url.scheme }}://{{ request.url.netloc }}.
1

Install

Create a new Poetry project and add the Waffle SDK plus the Claude agent runtime.

mkdir my-agent && cd my-agent
poetry init --no-interaction --name my-agent --python ">=3.11,<4.0"
poetry add heywaffle claude-agent-sdk
Tell Waffle where your agent lives — add this to pyproject.toml
[tool.waffle]
agent = "my_agent.agent:app"
After this step you have a working Python environment with both packages installed.
2

Write a minimal agent

Create src/my_agent/agent.py. The Agent object is the entry point; decorate a function with @app.webhook to handle incoming events from a named source.

from waffle_sdk import Agent, AgentContext

app = Agent("my-agent", project="default")


@app.webhook(source="greet")
async def greet(item: dict, ctx: AgentContext) -> dict:
    name = item.get("name", "world")
    return {"message": f"Hello, {name}!"}

Also create an empty src/my_agent/__init__.py so Python sees it as a package.

You now have a handler called greet that accepts a JSON payload and returns a reply. No hub needed yet.
3

Run it once — no hub required

waffle test invokes a single handler locally, prints the result, and exits. Useful for iterating quickly without running a full server.

List all discovered handlers
poetry run waffle test
Run the greet handler with a payload
poetry run waffle test greet --input '{"name": "Alice"}'
The terminal prints {"message": "Hello, Alice!"}. If this hub is reachable, a session also appears in Sessions.
4

Connect to the hub

Run waffle serve to keep the agent running and connected to this hub. It will claim inbox items over SSE and dispatch them to your handlers automatically.

Pass the hub URL as a flag
poetry run waffle serve --hub {{ request.url.scheme }}://{{ request.url.netloc }}
Or set it as an environment variable (handy for scripts & systemd units)
export WAFFLE_HUB_URL={{ request.url.scheme }}://{{ request.url.netloc }}
poetry run waffle serve
Leave this terminal running. The agent dot in the sidebar turns green within a few seconds, confirming the connection is live.
5

Add a webhook trigger

With the agent connected, send a payload to the hub's webhook endpoint. The hub enqueues it as an inbox item and the agent picks it up automatically.

Trigger the greet handler from anywhere
curl -sX POST {{ request.url.scheme }}://{{ request.url.netloc }}/webhooks/greet \
  -H 'content-type: application/json' \
  -d '{"name": "Alice"}'

The URL pattern is /webhooks/<source> — the source matches the string you passed to @app.webhook(source="..."). Any external system that can send an HTTP POST (Linear, GitHub, n8n, your own app) can trigger your handler this way.

Subscription filters narrow which payloads reach a handler
# Only fire when the payload contains action:"create"
@app.webhook(source="linear", filter={"action": "create"})
async def on_linear_create(item, ctx): ...

# Match nested keys by dot-path
@app.webhook(source="linear", filter={"type.name": "Issue"})
async def on_issue(item, ctx): ...

# No filter = match everything from that source
@app.webhook(source="greet")
async def greet(item, ctx): ...
The hub returns an inbox item ID immediately. The handler runs in the background; the result appears in the UI's Inbox and Sessions views once it completes.
{% endblock %}