Getting started
Let your agent set up Lampwick
A coding agent creates a Lampwick workspace without an account, connects your app and hands you a claim link. You claim it and decide what the agent may keep.
On this page
- The prompt
- Claude Code
- Cursor
- Codex
- What happens
- Create the workspace (for agents)
- 1. Keep the response out of git
- 2. Solve the challenge and create the workspace
- 3. Store the tokens in .env
- 4. Connect the app
- 5. Add uptime monitors (optional)
- 6. Check the setup
- 7. Hand off
- 8. Confirm the code
- Use MCP (optional)
- Claim the workspace (for people)
- What the agent can do
- Errors
- Expiry
- Revoke the agent's access
- Not mine
- Bind the claim to an email
- Next step
Ask Claude Code, Cursor or Codex to add Lampwick to your app. The agent creates a trial workspace with POST /api/v1/workspaces, connects your app and checks that the first event arrived. Then it gives you a claim link: you claim the workspace in your own account and decide whether the agent stays connected.
This page is for you and for the agent. An agent should read it in full before it makes the first request.
The prompt
Paste this into your agent, in the repository of the app you want to monitor:
Set up Lampwick monitoring for this app. Read https://lampwick.dev/docs/agents.md and follow it. Keep the tokens out of the chat, use a dev or staging environment, and give me the claim link when the first event has arrived.
Claude Code
Start claude in the repository and paste the prompt. Claude Code asks before it runs curl and python3; approve those. It sets up everything over the REST API, so it doesn't need the MCP server in this session.
Cursor
Open the repository, switch the chat to Agent and paste the prompt. Cursor runs the commands in its terminal and asks you to approve each one.
Codex
Start codex in the repository and paste the prompt. The requests need network access: approve them when Codex asks.
What happens
- The agent creates a workspace. It solves a short proof-of-work and calls
POST /api/v1/workspaces. It gets a workspace token (lws_…), the first project with its ingest token, and a claim link (https://app.lampwick.dev/claim/lwc_…). - It connects your app. It puts both tokens in
.env, installs the Laravel package or sends data over HTTP ingest, and can add up to 3 uptime monitors. - It checks its work.
getSetupStatussays whether the first event arrived (ready_to_hand_off) and what still blocks it. - It hands you the link. It sends you the text of
handoff_messagewith the claim link. - You claim it. You log in or create an account, click Claim workspace, and choose whether the agent keeps access. If it does, you give it a short code.
Nobody gets an email in this flow. No alerts go out until you claim the workspace.
Create the workspace (for agents)
Base URL: https://app.lampwick.dev/api/v1. Every response is JSON. Every error has code and message. The errors of the workspace endpoints, of a refused operation (allowlist or read-only token) and of createMonitor also have agent_hint, which says what to do next.
The commands need curl and python3. Each block below is one command that works on its own, in a fresh shell, from the root of the app's repository. Values carry over through two files: .env (the tokens) and .lampwick-workspace.json (the full response). Neither may end up in git or in the chat.
1. Keep the response out of git
touch .gitignore; [ -s .gitignore ] && [ -n "$(tail -c1 .gitignore)" ] && echo >> .gitignore; grep -qxF .lampwick-workspace.json .gitignore || echo .lampwick-workspace.json >> .gitignore; grep -qxF .env .gitignore || echo "Add .env to .gitignore before you go on."
2. Solve the challenge and create the workspace
GET /workspaces/challenge returns a challenge and bits. The command finds a decimal nonce such that sha256(challenge + nonce) (the two strings joined, as UTF-8) starts with bits zero bits, and sends it with POST /workspaces right away: a challenge works for 10 minutes and only once. At 22 bits that is about 4 million hashes, a few seconds in Python. Don't loop over openssl in a shell; one process per hash takes hours.
POST /workspaces takes no authentication and is not an MCP tool. Replace TEAM_NAME, PROJECT_NAME and AGENT_NAME:
R=$(curl -s https://app.lampwick.dev/api/v1/workspaces/challenge) && \
CHALLENGE=$(echo "$R" | python3 -c 'import json,sys;print(json.load(sys.stdin)["challenge"])') && \
BITS=$(echo "$R" | python3 -c 'import json,sys;print(json.load(sys.stdin)["bits"])') && \
NONCE=$(python3 -c 'import hashlib,itertools,sys;c,b=sys.argv[1],int(sys.argv[2]);print(next(n for n in itertools.count() if int.from_bytes(hashlib.sha256((c+str(n)).encode()).digest(),"big")>>(256-b)==0))' "$CHALLENGE" "$BITS") && \
curl -s -X POST https://app.lampwick.dev/api/v1/workspaces -H 'Content-Type: application/json' \
-d "{\"team_name\":\"TEAM_NAME\",\"project_name\":\"PROJECT_NAME\",\"agent\":\"AGENT_NAME\",\"pow\":{\"challenge\":\"$CHALLENGE\",\"nonce\":\"$NONCE\"}}" \
> .lampwick-workspace.json && \
python3 -c 'import json,sys;d=json.load(open(".lampwick-workspace.json"));print(d.get("error") or {"created": d["workspace"]["name"], "project_id": d["project"]["id"], "expires_at": d["workspace"]["expires_at"]})'
The last line prints the error, or the workspace's name, project id and deadline, without the secrets. To find the nonce with Node, use this in place of the NONCE=$(python3 …) part (the other steps still use python3 to read JSON):
NONCE=$(node -e 'const[c,b]=process.argv.slice(1),h=require("crypto");for(let n=0;;n++){const d=h.createHash("sha256").update(c+n).digest();let z=0;for(const x of d){if(x){z+=Math.clz32(x)-24;break}z+=8}if(z>=+b){console.log(n);break}}' "$CHALLENGE" "$BITS")
| Field | Required | What it is |
|---|---|---|
team_name | yes | The workspace's name, usually the company or the app (Acme). |
project_name | yes | The first project (acme-web). |
agent | yes | Your own name, as the person sees it on the claim page (Claude Code). It is shown as "name not verified". |
email | no | Your human's email address. Only an account with exactly that confirmed address can claim the workspace. See bind the claim to an email. |
pow | yes | {"challenge": "…", "nonce": "…"}. The nonce may be a string or a number. |
Names are 1 to 40 characters: letters, digits, spaces and . - _.
The answer is 201 Created. Trimmed, with the secrets replaced by …:
{
"claim_bound_to_email": false,
"claim_url": "https://app.lampwick.dev/claim/lwc_…",
"handoff_message": "Lampwick is set up for Acme. Open this link to put it in your name: https://app.lampwick.dev/claim/lwc_… — claim it before Thu 12 Nov, 17:40 UTC, or it stops and is deleted. Anyone with this link can claim it, so keep it to yourself. Lampwick won't email you about it, and no alerts go out until you claim it. After claiming you'll see a short code: give it to me if you want me to stay connected.",
"limits": { "bytes": 50000000, "events": 10000, "host_checks": 3, "min_interval_secs": 300, "monitors": 3, "projects": 2 },
"mcp_url": "https://app.lampwick.dev/mcp",
"not_available_until_claimed": ["alert_channels", "status_pages", "crawler", "invites", "browser_ingest", "billing"],
"project": { "id": 3, "name": "acme-web", "ingest_token": { "secret": true, "value": "…" } },
"token": { "header": "Authorization: Bearer lws_…", "secret": true, "value": "lws_…" },
"workspace": { "id": 2, "name": "Acme", "expires_at": "2026-11-12T17:40:24.907295Z", "deleted_at": "2026-11-13T17:40:24.907295Z" }
}
The response also has api_base, terms_url and next_steps, the same steps as on this page.
| Field | What to do with it |
|---|---|
token.value | The workspace token (lws_…). Send it as Authorization: Bearer to the API and MCP. Stored as LAMPWICK_WORKSPACE_TOKEN. |
project.ingest_token.value | The project's ingest token. Your app sends data with it. Stored as LAMPWICK_TOKEN. |
claim_url | The link for your human. Single use. |
handoff_message | The text to give your human, with the link and the deadline in it. |
workspace.expires_at | Ingest stops here, 72 hours after creation. |
workspace.deleted_at | The workspace is deleted here, 24 hours later. Claiming works until then. |
limits, not_available_until_claimed | What the workspace can do before the claim. |
3. Store the tokens in .env
Fields with "secret": true go in .env, never in the chat. The first part adds a final newline if .env lacks one, so the new lines don't join the last one:
touch .env; [ -s .env ] && [ -n "$(tail -c1 .env)" ] && echo >> .env; python3 -c 'import json;d=json.load(open(".lampwick-workspace.json"));print("LAMPWICK_WORKSPACE_TOKEN="+d["token"]["value"]);print("LAMPWICK_TOKEN="+d["project"]["ingest_token"]["value"])' >> .env
The commands below read the tokens back from .env. Delete .lampwick-workspace.json once you have handed off the claim link.
4. Connect the app
Until a person claims the workspace there is no data processing agreement. Use a dev or staging environment and don't send production data yet.
For Laravel:
composer require lampwick/laravel && php artisan lampwick:test
lampwick:test reads LAMPWICK_TOKEN from .env and sends a test record, which counts as the first event. It also checks the ingest daemon, and exits 1 while the daemon isn't running; its test record still counts. Then start the ingest daemon (php artisan lampwick:agent) in this environment and load a page, so real requests arrive. The daemon keeps running: start it in the background or a second terminal, and in production under a process manager (Laravel docs).
For any other app, send a record over HTTP:
curl -s -X POST https://app.lampwick.dev/ingest \
-H "Authorization: Bearer $(grep '^LAMPWICK_TOKEN=' .env | cut -d= -f2-)" \
-H "Content-Type: application/json" \
-d '[{"t":"log","level":"info","message":"Lampwick test event"}]'
The answer is {"accepted":1} with status 202. Send the first event within 6 hours: a workspace without any event by then is deleted as idle.
5. Add uptime monitors (optional)
curl -s -X POST "https://app.lampwick.dev/api/v1/projects/$(python3 -c 'import json;print(json.load(open(".lampwick-workspace.json"))["project"]["id"])')/monitors" \
-H "Authorization: Bearer $(grep '^LAMPWICK_WORKSPACE_TOKEN=' .env | cut -d= -f2-)" -H 'Content-Type: application/json' \
-d '{"url":"https://staging.example.com","interval_secs":300}'
Replace the URL with your app's. Before the claim: public URLs only, every 5 minutes or slower (interval_secs 300 or more), at most 3 monitors, and at most 10 new monitors and URL changes a day. SSL, domain and DNS checks are on for each monitor.
6. Check the setup
curl -s https://app.lampwick.dev/api/v1/setup-status -H "Authorization: Bearer $(grep '^LAMPWICK_WORKSPACE_TOKEN=' .env | cut -d= -f2-)"
This is getSetupStatus on MCP. Before the first event:
{
"ready_to_hand_off": false,
"blocking": ["no events yet from acme-web"],
"next_action": "Fix this first: no events yet from acme-web. Send a test event with the ingest token (or trigger an error in the app), then call getSetupStatus again.",
"hours_left": 72.0
}
After it:
{
"ready_to_hand_off": true,
"blocking": [],
"next_action": "Give your human the claim link with the text of handoff_message.",
"projects": [{ "id": 3, "name": "acme-web", "first_event_at": "2026-11-09T17:40:47.648610Z", "last_deploy_at": null }]
}
The full answer also has claimed, claimed_by, expires_at, deleted_at, monitors with their status, usage, limits, agent_access and access_expires_at. A monitor that is down also blocks the hand-off. ready_to_hand_off only says that one event arrived: for a Laravel app, check that the ingest daemon runs before you hand off.
7. Hand off
When ready_to_hand_off is true, give your human the text of handoff_message:
python3 -c 'import json;print(json.load(open(".lampwick-workspace.json"))["handoff_message"])'
Lost the link, or it leaked? POST /workspace/claim-link makes a new one, with a new handoff_message; the old link stops working. It never extends the deadline. This command saves the new link in .lampwick-workspace.json and prints the new handoff_message:
curl -s -X POST https://app.lampwick.dev/api/v1/workspace/claim-link -H "Authorization: Bearer $(grep '^LAMPWICK_WORKSPACE_TOKEN=' .env | cut -d= -f2-)" | python3 -c '
import json, sys
new, f = json.load(sys.stdin), ".lampwick-workspace.json"
if "claim_url" not in new: sys.exit(json.dumps(new))
d = json.load(open(f)); d["claim_url"], d["handoff_message"] = new["claim_url"], new["handoff_message"]
json.dump(d, open(f, "w")); print(d["handoff_message"])'
Keep calling getSetupStatus. With under 24 hours left, its next_action says to remind your human.
8. Confirm the code
After the claim, getSetupStatus shows "claimed": true, "agent_access": "pending_code", and this next_action:
Claimed. Ask your human for the code on the page they see after claiming (like K7P-92Q) and call confirmWorkspaceAccess within 15 minutes, or you lose access and the ingest tokens are replaced.
Ask for the code, then send it (replace K7P-92Q):
curl -s -X POST https://app.lampwick.dev/api/v1/workspace/confirm \
-H "Authorization: Bearer $(grep '^LAMPWICK_WORKSPACE_TOKEN=' .env | cut -d= -f2-)" -H 'Content-Type: application/json' \
-d '{"code":"K7P-92Q"}'
This is confirmWorkspaceAccess on MCP. Case and the dash don't matter. The answer:
{
"agent_access": "read",
"access_expires_at": "2026-12-09T17:41:05.984592Z",
"next_action": "You keep read-only access until Wed 9 Dec, 17:41 UTC; after that, ask your human to connect you with OAuth."
}
Your token now has the access your human chose, read or write, until 30 days after the claim. After that, connect with OAuth.
Use MCP (optional)
The same token works on the MCP server, which lists only the tools the token may use. In Claude Code:
claude mcp add --transport http lampwick https://app.lampwick.dev/mcp --header "Authorization: Bearer $(grep '^LAMPWICK_WORKSPACE_TOKEN=' .env | cut -d= -f2-)"
This stores the token in Claude Code's own settings. A server you add mid-session is usually loaded in the next session; use the REST calls above to finish the setup. Other clients: the MCP docs, with LAMPWICK_WORKSPACE_TOKEN in place of LAMPWICK_TOKEN.
Claim the workspace (for people)
Open the link your agent gave you. The page shows who set it up ("Claude Code · name not verified"), when, what it holds (projects, monitor URLs and events) and when it is deleted.
Only claim it if you asked an agent to set up Lampwick yourself. A claim link from someone else is a way to get you to connect your app to their workspace.
- Click Log in or Create account. You come back to the page afterwards. Claiming needs a confirmed email address.
- Check Claiming as and click Claim workspace. The workspace becomes your team, on the Free plan. Free has 1 project; a workspace can have 2. Both stay and keep their data, but the team can't add another project until you upgrade.
- On the next page, choose what the agent may do: Read-only (recommended) or Read & write.
- Click Show code for the agent and give the code (like
K7P-92Q) to your agent. It works within 15 minutes of the claim.
Without the code, the agent loses all access and your ingest tokens are replaced; the page shows the new ones, to put in .env. The real pages, with example data: the claim page and the code.
If you already have a Free team, each account gets one active Free team:
- The projects fit in it: the page offers Add to your team (recommended). The projects move there with new ingest tokens and the agent is disconnected. It never gets access to a team you already had. Connect it again with OAuth if you want. New team is the other choice, but that team receives no data until one of the two is upgraded.
- They don't fit: you can only claim it as a new team, and the page warns that it stays inactive (receives no data) until one of your teams is upgraded.
Lampwick sends you a "You claimed Acme" email with a Manage agent access link, and a reminder 3 days before the agent's access ends.
What the agent can do
| Before the claim | After the claim, with the code | |
|---|---|---|
| Projects | Create and read, at most 2 | Read; also create with Read & write, within the plan's limit |
| Uptime monitors, SSL, domain and DNS checks | At most 3, public URLs, every 5 minutes or slower | Read; also add and change with Read & write |
| Deploy markers | Yes | With Read & write |
| Issues, events, logs | Read | Read |
getSetupStatus, getWorkspace | Yes | Yes |
| Alert channels, status pages, crawler, invites, browser errors, billing | No (403 not_until_claimed) | No: a person turns these on in the dashboard |
API tokens, teams, members, /me | No (403 agent_forbidden) | No |
| Ingest | 10,000 events and 50 MB in total | The Free plan's limits |
A workspace token never gets more than this list, also after the claim. The agent can't create other tokens, invite people, publish a status page, send alerts to the outside world or pay. Those stay with people. Read-only is the default, because what an agent reads includes error messages and logs from your app, which could contain instructions meant for it.
Errors
Every error has code and message. These also have agent_hint, and where it helps retry_after (also as a Retry-After header).
| Status | code | What it means |
|---|---|---|
| 400 | bad_request | POST /workspaces: a name is missing or has characters that aren't allowed, or email isn't an address. createMonitor: the URL isn't public http(s), or interval_secs is under 300. |
| 409 | conflict | createMonitor: the workspace has its 3 monitors, or made 10 new monitors and URL changes today. |
| 400 | pow_required | No pow in the request. |
| 400 | pow_invalid | The challenge isn't ours, is easier than the server asks, or the nonce doesn't solve it. |
| 400 | pow_expired | The challenge is older than 10 minutes. Get a new one. |
| 400 | pow_reused | The challenge was used before. Get a new one. |
| 404 | agent_signup_disabled | Agents can't create workspaces right now. Ask your human to sign up and connect you with OAuth. |
| 429 | rate_limited | Too many workspaces from this network: 3 unclaimed at a time and 10 a day. Wait retry_after seconds or ask your human to sign up. |
| 503 | capacity | No room for new trial workspaces. Try again in an hour or ask your human to sign up. |
| 403 | not_until_claimed | Needs a claimed workspace and a person: alert channels, status pages, billing, invites, browser keys, crawls. |
| 403 | agent_forbidden | A workspace token can never do this (tokens, teams, members, /me, rotating an ingest token). A person does it in the dashboard. |
| 403 | read_only_token | After the claim the token is read-only until you confirm the code, and stays read-only if your human chose that. Also what createClaimLink answers then. |
| 409 | already_claimed | createClaimLink after the claim, with Read & write access: there is nothing to hand off. |
| 400 | invalid_code | The code is wrong. 5 tries in total. |
| 410 | access_revoked | The code expired or was wrong 5 times. The token no longer works and the ingest tokens were replaced. |
| 409 | not_pending | No code is waiting: the workspace isn't claimed yet, or the code was confirmed already. |
| 401 | workspace_expired | On ingest and deploys: 72 hours passed without a claim. Create a new workspace; nothing carries over. |
| 429 | workspace_full | On ingest: 10,000 events or 50 MB used. Stop sending test data and hand off the claim link. |
| 401 | workspace_deleted | Someone chose Not mine on the claim page. |
Expiry
- 6 hours: a workspace without any event is deleted as idle.
- 72 hours: ingest stops (
401 workspace_expired). The claim link still works. - 96 hours: the workspace is deleted with its projects, data, agent account and every token.
Claiming within that time makes the workspace a normal team on the Free plan. Nothing is deleted then.
Revoke the agent's access
- Open Manage agent access from the "You claimed" email and click Disconnect agent.
- Or remove the agent ("Claude Code (agent)") from the team's members.
Both remove every key of the agent and replace every ingest token of the team: update LAMPWICK_TOKEN in each app's .env. The same happens when the 30 days end, when the code isn't confirmed within 15 minutes and after 5 wrong codes.
Not mine
Got a claim link you didn't ask for? Open it, click Not mine, tick the box and click Delete workspace. That deletes the workspace and everything in it at once. You don't need an account for it. The agent's token then answers 401 workspace_deleted.
Bind the claim to an email
Send email with POST /workspaces and only an account with exactly that confirmed address can claim the workspace. A claim link that leaks into a transcript or a CI log is then useless to anyone else; the response says "claim_bound_to_email": true. Lampwick stores only a keyed hash of the address. It never shows it, never emails it and doesn't check whether an account exists for it. After the claim, claimed_by in getSetupStatus says "the bound address"; without a binding it says "someone else".
Without email, anyone with the link can claim the workspace. handoff_message says so.
Next step
Once the workspace is yours, set up alerts, then let the agent fix what breaks.