Lampson

Lampson as an engine

HTTP API

Every Lampson project (workspace) exposes an API for your apps, your bots and other agents:

https://your-lampson/w/<workspace>/v1/…

With no public URL, the base is http://127.0.0.1:8080/w/<workspace>/v1 — from this PC only. To use it from outside: lampson --domain or a tunnel.

API keys§

The API always requires a key, even from the same PC.

lampson key add --agent tienda --name "whatsapp bot"
lampson key add --agent tienda,soporte --rate 60 --callback https://hooks.my-app.com/
lampson key list
lampson key remove k6KJgJHpu

Or from the web UI: API in the left panel → + crear una API key.

  • The key is shown only once, with the API and MCP URLs ready to copy. Lampson stores only its hash.
  • --agent: the agents it may use (with none, all of them).
  • --rate N: requests per minute (default 120; 0 = no limit). Past the limit it answers 429 with Retry-After.
  • --callback URL: the only URLs Lampson will send callbacks to with this key: same scheme, host and port, and the path as a folder (https://app.com/hooks allows /hooks/…, not app.com.evil.net nor /hooks-x). Without it, the key cannot ask for callbacks. Keys created before Lampson 0.5.1 reach any public host name, and lampson key list flags them so you replace them.
  • An API key only opens /v1: never the web UI, the terminal or the chat.

A workspace with keys doesn't shut down for inactivity: otherwise your bot's next message would find it off.

Sending a message§

curl -X POST https://your-lampson/w/tienda/v1/messages \
  -H "Authorization: Bearer lk_…" -H "Content-Type: application/json" \
  -d '{"to": "tienda", "thread": "whatsapp:+5491122334455", "text": "do you have whole-wheat bread?"}'
{"agent": "tienda", "thread": "whatsapp:+5491122334455", "session": "20261016-132332-8546",
 "text": "Hi! Yes, we have sourdough whole-wheat bread at $3,200.", "steps": 2,
 "usage": {"input": 1453, "output": 148, "cached": 1024}, "stopped": "done",
 "batch": ["m59429972irl8"], "lead": "m59429972irl8"}

The body is an envelope:

field
textthe message (required)
tothe agent (default: the workspace's)
threadwho/what this conversation is: same thread = same conversation. Each key has its own: another app with the same thread starts another one
sessionor a known session, from this same key (another key's gives 404)
images[{"media_type": "image/jpeg", "data": "<base64>"}] — models with vision see them
from, metafree-form: they travel with the message (ids from your system, the customer's name)
sent_atwhen the customer wrote (unix): orders a burst that arrived out of order
callbacksee below

Errors: 400 (invalid envelope), 401 (no key or invalid key), 403 (agent outside the key, or callback not allowed), 404 (unknown agent), 429 (limit).

Three ways to get the answer§

  • JSON — POST /v1/messages: the request waits for the agent and returns the result.
  • Streaming — POST /v1/messages/stream: Server-Sent Events with each step (session, assistant, tool_call…, done), like lampson run --json.
  • Callback — POST /v1/messages with "callback": "https://…": it answers 202 {"job": "m…", "status": "queued"} right away and, when the agent finishes, Lampson POSTs to your URL. That's what WhatsApp-style webhooks need, since they demand a quick response.

The callback carries {job, jobs, batch, status, agent, thread, session, text, steps, usage, stopped, meta}. It is signed with the secret of the key that asked for it, which lampson key add shows once next to the key: X-Lampson-Signature: sha256=<HMAC-SHA256 of the body>, and X-Lampson-Key says which key. So another app using the same Lampson cannot sign a fake callback for yours. Keys created before Lampson 0.5.1 sign with LAMPSON_WEBHOOK_SECRET, as before. Verify it with the SDK or:

import hmac, hashlib
ok = hmac.compare_digest("sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest(), signature)

GET /v1/jobs/<id> returns the status of a message sent with a callback.

The mailbox§

Each conversation (agent + thread) has its mailbox. This comes built in, there's nothing to configure:

  • Never two turns at once on the same conversation. Different customers do run in parallel.
  • Bursts are merged. The turn starts after 3 seconds with no new messages (or 15 after the first): "hi" / "how much is the gluten-free bread?" / "is it in stock?" go in one turn and get one answer — the same one in all three requests, with batch (the messages in the batch) and lead (the first one). With a callback, one POST goes out per batch.
  • What arrives mid-turn joins the turn in progress, before the agent answers. If it arrives while the model is already writing the answer, Lampson drops it and answers again with everything: an answer that ignores the customer's latest message never goes out.

The wait is tuned per agent with "buffer": {"quiet_s": 3, "max_s": 15}, or "buffer": false to answer without waiting (the queue still applies). → Agents

Objectives§

An objective is something an agent (or a team) works on by itself until it's done.

curl -X POST https://your-lampson/w/shop/v1/objectives -H "Authorization: Bearer lk_…" \
  -d '{"agent": "cto", "text": "Add filtering by status, with tests; ask me before merging.", "budget_tokens": 400000}'
POST /v1/objectivescreate: {text, agent? | team?, budget_tokens?, round_seconds?} → 201 with the objective
GET /v1/objectivesthe ones this key can see (its agents')
GET /v1/objectives/<id>one: status (active, waiting, done, failed, cancelled, paused), progress (last report), question and question_kind (question or approval), result, tasks (its board), ledger (its record), spent
POST /v1/objectives/<id>/replyanswer: {text} to a question, {approved: true|false, text?} to an approval
POST /v1/objectives/<id>/cancelstop it

A waiting objective doesn't move or spend until you answer.

Other endpoints§

GET /v1/agentsthe agents this key may use
GET /v1/jobs/<id>status of a message sent with a callback
POST /v1/mcp · /v1/mcp/streamMCP over HTTP → Lampson as an MCP server
GET /v1/openapi.jsonthe OpenAPI 3.1 of this API

OpenAPI§

/v1/openapi.json describes only the public API — the web UI, the terminal and the channel webhooks don't appear. Use it to generate a client in your language (openapi-generator for Go, Java, C#, PHP…) or to try the API from your favorite tool. For JavaScript and Python there's already an SDK.

View as Markdown