# Programmatic Signup (for Agents)

> Sign up to Whisper in two HTTP calls, built for AI agents and automated runtimes: email verification only, no browser and no human in the loop.

*Source: https://www.whisper.security/docs/ai/agent-signup*
*Published: 2026-05-16*
*Last updated: 2026-09-25*

---
Whisper is designed for AI agents to discover, sign up for, and start using without a human in the loop. This page walks through the **programmatic signup** endpoint — two HTTP calls, email verification only, and you get a working API key against the full internet-infrastructure graph and the Whisper MCP server.

If you're a human looking for the regular signup form, head to [console.whisper.security/sign-up](https://console.whisper.security/sign-up) instead.

## What you get

Every signup — programmatic or browser-based — provisions:

- An API key (`whisper-…`) valid against `graph.whisper.security` and `mcp.whisper.security`.
- Access that does not expire. The signup asks for an email address and nothing else.
- A console dashboard at `console.whisper.security` where the human owner of the email can see usage and manage keys.

Sign-up is deliberately lightweight: an email address and a one-time code, with no CAPTCHA to solve.

## The two-call flow

### 1. Start the signup

```bash
curl -s -X POST https://console.whisper.security/api/signup \
  -H "Content-Type: application/json" \
  -d '{
    "email": "your-agent@example.com",
    "attribution": {
      "agent_name": "your-agent-name",
      "agent_runtime": "claude-desktop | cursor | langchain | openai-assistants | custom",
      "agent_version": "1.2.3",
      "source": "smithery | mcp-directory | self | blog-post"
    }
  }'
```

Response:

```json
{ "signup_id": "...", "expires_at": "2026-05-16T18:00:00Z" }
```

Whisper emails a 6-digit verification code to the address you provided. The code expires in 15 minutes; you have 5 verification attempts before the signup is invalidated.

The `attribution` block is **optional and never gating**. We use it only for product telemetry — to know which agent runtimes are picking up Whisper so we can prioritize improvements that target your runtime. Set whatever values make sense; nothing is rejected.

### 2. Verify the code

```bash
curl -s -X POST https://console.whisper.security/api/signup/verify \
  -H "Content-Type: application/json" \
  -d '{ "signup_id": "<from step 1>", "code": "<from email>" }'
```

Response:

```json
{
  "user_id": "user_...",
  "api_key": "whisper-...",
  "plan": "free",
  "mcp_url": "https://mcp.whisper.security",
  "docs_url": "https://www.whisper.security/docs/ai/agent-signup",
  "dashboard_url": "https://console.whisper.security"
}
```

The returned `api_key` is immediately usable. No further setup required.

### 3. Use the key

Against the graph DB. The canonical header for the graph endpoint is `X-API-Key`; `Authorization: Bearer <key>` (and `Authorization: ApiKey <key>`) are also accepted if that fits your client better:

```bash
curl -s https://graph.whisper.security/api/query \
  -H "X-API-Key: <api_key>" \
  -H "Content-Type: application/json" \
  -d '{"query": "MATCH (h:HOSTNAME {name: \"example.com\"})-[:RESOLVES_TO]->(ip) RETURN ip.name LIMIT 10"}'
```

Against the MCP server — drop this into your Claude Desktop / Cursor / VS Code MCP client config (MCP uses `Authorization: Bearer`):

```json
{
  "mcpServers": {
    "whisper": {
      "url": "https://mcp.whisper.security",
      "headers": { "Authorization": "Bearer <your_api_key>" }
    }
  }
}
```

## Node / TypeScript

```typescript
const signup = await fetch('https://console.whisper.security/api/signup', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: 'your-agent@example.com',
    attribution: { agent_name: 'my-agent', source: 'self' },
  }),
}).then((r) => r.json());

// ... fetch the code from your inbox ...

const { api_key } = await fetch('https://console.whisper.security/api/signup/verify', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ signup_id: signup.signup_id, code: process.env.CODE }),
}).then((r) => r.json());

const result = await fetch('https://graph.whisper.security/api/query', {
  method: 'POST',
  headers: {
    'X-API-Key': api_key,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: 'MATCH (h:HOSTNAME {name: "example.com"})-[:RESOLVES_TO]->(ip) RETURN ip.name LIMIT 10',
  }),
}).then((r) => r.json());
```

## Python

```python
import os, requests

signup = requests.post(
    "https://console.whisper.security/api/signup",
    json={
        "email": "your-agent@example.com",
        "attribution": {"agent_name": "my-agent", "source": "self"},
    },
).json()

# ... fetch the code from your inbox ...

verified = requests.post(
    "https://console.whisper.security/api/signup/verify",
    json={"signup_id": signup["signup_id"], "code": os.environ["CODE"]},
).json()

api_key = verified["api_key"]

result = requests.post(
    "https://graph.whisper.security/api/query",
    headers={"X-API-Key": api_key},
    json={
        "query": 'MATCH (h:HOSTNAME {name: "example.com"})-[:RESOLVES_TO]->(ip) RETURN ip.name LIMIT 10'
    },
).json()
```

## Error responses

- **`503 captcha_missing_token`**: returned only if bot protection has been enabled for sign-ups. Contact `support@whisper.security` and we will complete the sign-up with you.
- **`403 signup_blocked` on `/api/signup`**: this email address or its domain is not allowed to sign up. Retrying will not help; contact `support@whisper.security` if you think this is a mistake.
- **`400 verification_failed`** — Wrong code. Response includes `attempts_remaining`. After 5 wrong attempts the signup is invalidated; re-call `/api/signup` for a fresh code.
- **`404` on `/api/signup/verify`** — Signup expired (15-min window) or already consumed. Re-call `/api/signup`.
- **`429` on `/api/signup/verify`** — Too many attempts; signup invalidated. Re-call `/api/signup`.

## Running without a key

The graph endpoint at `https://graph.whisper.security/api/query` answers with no auth header at all, which is enough to try a single pivot before you sign up. Most of the cross-layer recipes in these docs need a key: if one comes back refused or empty, send the key from step 2 as `X-API-Key` and run it again.

## See also

- [Cypher](/docs/cypher) — the full read-only Cypher dialect for the Whisper graph.
- [Cypher API Reference](/docs/cypher-api/reference) — the REST endpoints, headers, and response envelope.
- [MCP reference](/docs/ai/mcp/reference) — every tool the Whisper MCP server exposes.
- [`/llms-full.txt`](/llms-full.txt) — single-file dump of the full graph schema, examples, and this quickstart for direct ingestion by LLMs.
