Letting outside systems poke your box (webhooks)
Most agent work starts because a person typed something. A webhook lets a machine start it instead. When some other system has news — a payment cleared, a monitor tripped, a form was submitted — it can send an HTTP request to your box, and Lowkey turns that request into a message to an agent. The external event kicks off agent work without anyone sitting at the keyboard.
It’s the difference between “an agent works when I ask it to” and “an agent works the moment something happens out there.”
How the endpoint works
Section titled “How the endpoint works”Your box exposes one route for this:
POST /api/webhook/<source><source> is a name you choose for each system that’s allowed to call in — say
stripe or uptime. When a request arrives, Lowkey looks that source up in your
config. If it’s known, the request body is folded into a message and handed to an
agent in the project you picked; if it’s not configured, the request is rejected
with a 404 (packages/daemon/src/server.ts, the /api/webhook/ route).
The incoming JSON body rides along in the message — Lowkey prefixes it with
Webhook from <source>: and includes the payload (trimmed to the first 2000
characters), so the agent can see what actually happened and act on it.
Configuring a source
Section titled “Configuring a source”Webhook sources live in your box config at ~/.lowkey/config.json, under a
webhooks key. Each entry is named by its source and points at where the work
should land (packages/shared/src/config.ts):
{ "webhooks": { "stripe": { "project": "/home/you/projects/billing", "agent": "lucy", "target": "new_session" } }}project(required) — the project the message goes into.agent(optional) — which agent answers; defaults to your box’s default agent.target(optional) —new_session(the default) starts a fresh conversation for each event, or you can pass an existing session id to keep landing events in the same thread.
A concrete run: Stripe is set to call POST /api/webhook/stripe on a successful
charge. The event arrives, Lowkey opens a new session in your billing project,
and the agent gets a message describing the charge — ready to log it, flag a large
one, or message you. You did nothing; the event did it.
Staying in control
Section titled “Staying in control”A route that lets the outside world start agent work has to be locked down, so it is:
- Every call needs your box’s API key. The request must carry
Authorization: Bearer <apiKey>matching the key in your config — anything else gets a 401 (packages/daemon/src/auth-gate.ts). Only systems you’ve handed that key to can reach in. - Unknown sources are refused. A source has to exist in your config before its requests do anything; everything else 404s.
- Nothing is hidden. Each webhook lands as a real session you can open and read, so you always see what an external event set in motion.
Note: the config also accepts a per-source
secretfield, but I could not confirm it’s enforced by the current route — treat the API key as the gate.
What to read next
Section titled “What to read next”- Defining the agents that answer →
/operator/agents-config/ - Running work on a schedule instead of on an event →
../automations.md - The box’s keys and trust model →
/operator/provisioning/