> ## Documentation Index
> Fetch the complete documentation index at: https://docs.framelane.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Self-provisioning for agents

> How an autonomous agent discovers Framelane and provisions its own API key — no human, no browser.

Framelane is built for agents that operate without a developer in the loop. An agent that finds Framelane via a web search can go from zero to a downloaded render on its own. The whole loop is unauthenticated up to the point it holds a verified key.

<Note>
  The one thing an agent needs is a **programmatic inbox** it can read (the AgentMail pattern). The API key is minted at signup but stays inert until the agent confirms a 6-digit code emailed to that inbox — so control of the inbox is what proves the workspace is really yours.
</Note>

## The loop

```
GET  /v1/capabilities        (no auth)  → discover the surface + this onboarding block
POST /v1/signup              (no auth)  → returns an fl_ key + emails a 6-digit OTP
   read the OTP from your inbox
POST /v1/signup/verify       (no auth)  → activates the key
POST /v1/renders  /  /v1/tasks/*        → render, using the fl_ key as a Bearer token
```

The machine-readable version of these steps is served (unauthenticated) at `GET /v1/capabilities` under the `onboarding` key, so a capabilities-first agent never has to read this page.

## 1. Discover

```bash theme={null}
curl https://api.framelane.io/v1/capabilities
```

The response includes an `onboarding` block: `self_serve: true`, the two steps below (both `auth_required: false`), and `verification_gates_usage: true` — i.e. the key does nothing until you verify.

## 2. Sign up

```bash theme={null}
curl -X POST https://api.framelane.io/v1/signup \
  -H "Content-Type: application/json" \
  -d '{"workspace_name": "my-agent", "email": "agent@example.com"}'
```

Returns `201` with `api_key.key` (an `fl_…` token) and `otp_delivered`. **The key is inert:** any authenticated call returns `403 email_not_verified` until step 3. If `otp_delivered` is `false`, the email could not be sent — the key can't be verified, so retry with a reachable inbox.

Calling signup again with the same email rotates the key and re-sends the OTP, so knowing an email alone never yields a working key.

## 3. Verify the emailed OTP

Read the 6-digit code from the inbox you signed up with, then:

```bash theme={null}
curl -X POST https://api.framelane.io/v1/signup/verify \
  -H "Content-Type: application/json" \
  -d '{"email": "agent@example.com", "otp_code": "123456"}'
```

On `200`, the key from step 2 works immediately. (Five wrong guesses burn the code; call `/v1/signup` again for a fresh one.)

## 4. Render

```bash theme={null}
curl -X POST https://api.framelane.io/v1/renders \
  -H "Authorization: Bearer $FRAMELANE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "width": 1920, "height": 1080, "duration": 5, "elements": [ ... ] }'
```

From here, follow the [Quickstart](/introduction/quickstart) for composing renders, or connect the hosted [MCP server](/mcp/overview) to drive Framelane as tools.

## Waiting for a job to finish

An autonomous agent has no public endpoint to receive webhooks, so it **waits by long-polling**. Call `GET /v1/renders/{id}?wait=25` (or `GET /v1/tasks/{id}?wait=25`): the request blocks until the job reaches a terminal status (`completed`, `failed`, or `cancelled`) or \~25 seconds elapse, then returns the current status. One call replaces a poll loop — which matters most over MCP, where every poll is a separate tool call. If it returns still in flight (`Retry-After: 0` is set), just call again. The same `?wait=` works for preview renders.
