/lanes-forms
Use when the user wants a form backend or contact form set up (a live POST endpoint that captures submissions and emails them), OR wants an agent to fill in / submit to a form on their behalf. Lanes Forms creates and manages hosted form endpoints from Claude Code. Triggers on
$ npx -y skills add lanes-sh/app --skill lanes-forms --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/lanes-forms
Context preview
The summary Claude sees to decide when to auto-load this skill.
Use when the user wants a form backend or contact form set up (a live POST endpoint that captures submissions and emails them), OR wants an agent to fill in / submit to a form on their behalf. Lanes Forms creates and manages hosted form endpoints from Claude Code. Triggers on
SKILL.md
lanes-forms.SKILL.mdname: lanes-forms
description: Use when the user wants a form backend or contact form set up (a live POST endpoint that captures submissions and emails them), OR wants an agent to fill in / submit to a form on their behalf. Lanes Forms creates and manages hosted form endpoints from Claude Code. Triggers on "set up a contact form", "provision a form", "form backend", "wire up an HTML form", "Lanes Forms", "submit this to the form", or "fill in this form".
Lanes Forms
Lanes Forms is a form backend as a service. It gives an agent two capabilities:
1. **Provision** a live form endpoint that captures submissions from the first second and delivers them (email forwarding and/or stored in the dashboard). 2. **Fill** a form on a user's behalf by POSTing a submission to a live endpoint.
The API base URL is always `https://api.lanes.sh` — use this production host for every Lanes Forms call, including when wiring a form into a site you run on `localhost`. A form's endpoint lives only on the Lanes Forms API (the form ID exists nowhere else), and `localhost` is an allowed submit origin, so the production endpoint works from local dev too. **Never** point a form at a local or app-relative API base — that is the common mistake that silently posts to the wrong host in development. (The only exception is a Lanes maintainer developing the `api/` service itself, who may target a local `http://localhost:8080` checkout; end users never should.) Every error is a JSON envelope: `{"error": {"code", "message", "docs_url"}}`, so read `error.message` when a call fails.
Fastest path: the MCP server
The `lanes` MCP server wraps every call below and is the easiest way to work. If it isn't connected yet, run **`/setup-mcp`** to register it — it points at the hosted server (`https://api.lanes.sh/mcp`), authenticated with a workspace key. Its five tools are `create_form`, `get_form`, `update_form`, `generate_form_snippet`, and `submit_form`. With a workspace key configured, `create_form` provisions a form **born claimed** into that workspace, and the management tools (`get_form` / `update_form` / `generate_form_snippet`) require it. Without the MCP, call the API directly as shown below.
Provision a form
Send a `POST` to `/v1/forms`. Only `schema` is required.
POST https://api.lanes.sh/v1/forms
Authorization: Bearer lfk_... # workspace key — see "Get a workspace key"
Content-Type: application/json
{
"name": "Contact form",
"recipients": ["you@company.com"],
"allowed_origins": ["example.com"],
"schema": [
{"name": "email", "type": "email", "required": true},
{"name": "message", "type": "textarea", "description": "How can we help?"}
]
}- `schema` is **required** (1–50 fields). Each entry is `{name, type, required?, max_length?, description?}`. Field types: `text`, `email`, `textarea`, `number`, `checkbox`, `hidden`. Names must be unique, 1–80 chars; `_gotcha` is reserved (the spam honeypot) and is rejected in a schema. `description?` is optional helper text (≤500 chars, trimmed; blank becomes null) shown as subtext under the question on the hosted form page and as a `<small>` hint in generated HTML snippets.
- `name`, `allowed_origins`, and `recipients` are optional. `allowed_origins` (max 20) are the sites allowed to submit from a browser — give a hostname (`example.com`), full origin (`https://example.com`), or wildcard (`*.example.com`); `localhost` is always allowed for local testing. `recipients` (max 5) are the addresses submissions are emailed to; the **first** is the claim/owner address.
The `201` response returns `form_id`, `endpoint_url` (POST submissions here), `state`, `expires_at`, `limits`, `workflow`, `forward_email`, `store_submissions`, and `claim_url`. An anonymous create **always** returns a clickable `claim_url` to hand to the owner; when you pass `recipients` it *also* emails that same link to the first address and sets `claim_email_sent_to`. A keyed (born-claimed) create returns both as `null`. There is no per-form management secret.
**How the form is created and claimed depends on the bearer:**
- **With a workspace API key** (`Authorization: Bearer lfk_...`) — the reliable path. The form is born **claimed** in that workspace, ready to manage immediately. No claim email and no `claim_url`; `expires_at` is null. This is what the MCP uses when a workspace key is configured.
- **Anonymous, with `recipients`:** the response returns a single-use `claim_url` AND a claim email is sent to the first address (`claim_email_sent_to` is set) carrying the same link. The owner can click either one, sign in, and the form lands in their dashboard.
- **Anonymous, no `recipients`:** the response returns the single-use `claim_url` (there is no address to email it to) — hand it to whoever should own the form.
> **Anonymous provisioning is gated.** An unauthenticated `POST /v1/forms` returns `503 forms_public_disabled` on deployments where public provisioning is off (its current production default). When you can't sign the request anonymously, provision with a workspace `lfk_` key. Anonymous (unclaimed) forms store up to 25 submissions and **expire 7 days** after creation unless claimed.
Calling `/v1/forms` again with an identical **anonymous** request within 24h is an idempotent replay (`200`, `idempotent_replay: true`): nothing new is created and no fresh claim link is issued, so reuse the one from the original response. Keyed creates skip idempotency — every keyed call makes a new form.
Delivery: where submissions go (workflow)
By default every form **stores** submissions in the dashboard, and if you passed `recipients` it also **emails** them there (each recipient is verified before delivery starts). For a normal contact form that's the whole setup — you don't need to send a `workflow` at all.
To change delivery, use the simple sugar **or** an explicit `workflow` array, never both (sending both returns `422 workflow_conflict`):
- **Sugar:** `forw
Read more
name: lanes-forms description: Use when the user wants a form backend or contact form set up (a live POST endpoint that captures submissions and emails them), OR wants an agent to fill in / submit to a form on their behalf. Lanes Forms creates and manages hosted form endpoints from Claude Code. Triggers on "set up a contact form", "provision a form", "form backend", "wire up an HTML form", "Lanes Forms", "submit this to the form", or "fill in this form".
Lanes Forms
Lanes Forms is a form backend as a service. It gives an agent two capabilities:
1. **Provision** a live form endpoint that captures submissions from the first second and delivers them (email forwarding and/or stored in the dashboard). 2. **Fill** a form on a user's behalf by POSTing a submission to a live endpoint.
The API base URL is always `https://api.lanes.sh` — use this production host for every Lanes Forms call, including when wiring a form into a site you run on `localhost`. A form's endpoint lives only on the Lanes Forms API (the form ID exists nowhere else), and `localhost` is an allowed submit origin, so the production endpoint works from local dev too. **Never** point a form at a local or app-relative API base — that is the common mistake that silently posts to the wrong host in development. (The only exception is a Lanes maintainer developing the `api/` service itself, who may target a local `http://localhost:8080` checkout; end users never should.) Every error is a JSON envelope: `{"error": {"code", "message", "docs_url"}}`, so read `error.message` when a call fails.
Fastest path: the MCP server
The `lanes` MCP server wraps every call below and is the easiest way to work. If it isn't connected yet, run **`/setup-mcp`** to register it — it points at the hosted server (`https://api.lanes.sh/mcp`), authenticated with a workspace key. Its five tools are `create_form`, `get_form`, `update_form`, `generate_form_snippet`, and `submit_form`. With a workspace key configured, `create_form` provisions a form **born claimed** into that workspace, and the management tools (`get_form` / `update_form` / `generate_form_snippet`) require it. Without the MCP, call the API directly as shown below.
Provision a form
Send a `POST` to `/v1/forms`. Only `schema` is required.
POST https://api.lanes.sh/v1/forms
Authorization: Bearer lfk_... # workspace key — see "Get a workspace key"
Content-Type: application/json
{
"name": "Contact form",
"recipients": ["you@company.com"],
"allowed_origins": ["example.com"],
"schema": [
{"name": "email", "type": "email", "required": true},
{"name": "message", "type": "textarea", "description": "How can we help?"}
]
}- `schema` is **required** (1–50 fields). Each entry is `{name, type, required?, max_length?, description?}`. Field types: `text`, `email`, `textarea`, `number`, `checkbox`, `hidden`. Names must be unique, 1–80 chars; `_gotcha` is reserved (the spam honeypot) and is rejected in a schema. `description?` is optional helper text (≤500 chars, trimmed; blank becomes null) shown as subtext under the question on the hosted form page and as a `<small>` hint in generated HTML snippets.
- `name`, `allowed_origins`, and `recipients` are optional. `allowed_origins` (max 20) are the sites allowed to submit from a browser — give a hostname (`example.com`), full origin (`https://example.com`), or wildcard (`*.example.com`); `localhost` is always allowed for local testing. `recipients` (max 5) are the addresses submissions are emailed to; the **first** is the claim/owner address.
The `201` response returns `form_id`, `endpoint_url` (POST submissions here), `state`, `expires_at`, `limits`, `workflow`, `forward_email`, `store_submissions`, and `claim_url`. An anonymous create **always** returns a clickable `claim_url` to hand to the owner; when you pass `recipients` it *also* emails that same link to the first address and sets `claim_email_sent_to`. A keyed (born-claimed) create returns both as `null`. There is no per-form management secret.
**How the form is created and claimed depends on the bearer:**
- **With a workspace API key** (`Authorization: Bearer lfk_...`) — the reliable path. The form is born **claimed** in that workspace, ready to manage immediately. No claim email and no `claim_url`; `expires_at` is null. This is what the MCP uses when a workspace key is configured.
- **Anonymous, with `recipients`:** the response returns a single-use `claim_url` AND a claim email is sent to the first address (`claim_email_sent_to` is set) carrying the same link. The owner can click either one, sign in, and the form lands in their dashboard.
- **Anonymous, no `recipients`:** the response returns the single-use `claim_url` (there is no address to email it to) — hand it to whoever should own the form.
> **Anonymous provisioning is gated.** An unauthenticated `POST /v1/forms` returns `503 forms_public_disabled` on deployments where public provisioning is off (its current production default). When you can't sign the request anonymously, provision with a workspace `lfk_` key. Anonymous (unclaimed) forms store up to 25 submissions and **expire 7 days** after creation unless claimed.
Calling `/v1/forms` again with an identical **anonymous** request within 24h is an idempotent replay (`200`, `idempotent_replay: true`): nothing new is created and no fresh claim link is issued, so reuse the one from the original response. Keyed creates skip idempotency — every keyed call makes a new form.
Delivery: where submissions go (workflow)
By default every form **stores** submissions in the dashboard, and if you passed `recipients` it also **emails** them there (each recipient is verified before delivery starts). For a normal contact form that's the whole setup — you don't need to send a `workflow` at all.
To change delivery, use the simple sugar **or** an explicit `workflow` array, never both (sending both returns `422 workflow_conflict`):
- **Sugar:** `forw
(RENAME desktop-app) Lanes makes parallel AI coding your unfair advantage
Other skills on app.
- /github-lanes-bridge
Use when bridging GitHub and Lanes — importing GitHub issues into Lanes for local Claude Code execution, batch-spawning sessions per ticket, decomposing one GitHub issue into multiple Lanes sub-issues with dependencies, or posting session results (PR links, comments, follow-up
Open skill - /lanes-sessions
Use when managing Lanes issues or driving Claude Code sessions through the lanes_* MCP tools — creating issues, starting/stopping/inspecting sessions, batch-launching work across worktrees, reading terminal output, attaching labels and components by UUID, or moving issues across
Open skill - /linear-lanes-bridge
Use when bridging Linear and Lanes — importing Linear issues into Lanes for local Claude Code execution, batch-spawning sessions per Linear ticket, decomposing one Linear issue into multiple Lanes sub-issues with dependencies, or posting session results (PR links, comments,
Open skill

