# Create a subscription

Source: https://langbot.ai/docs/api/webhooks/create-subscription

`POST /v1/subscriptions`

Follow phrases on an event, or one market by ticker, and optionally have every hit posted to your webhook, signed.

**Auth.** An API token: `Authorization: Bearer lb_…` ([create one](https://langbot.ai/app/tokens)). Signed-in web sessions work too.

**Limits.** Each subscription counts toward your plan's subscriptions (5, 25 or 200); a new webhook URL toward its webhook endpoints (1, 5 or unlimited).

## Parameters

### Body

| Name | Type | Required | Description |
|---|---|---|---|
| `event_id` | string | no | The event to follow (`ev_…`). Send this with `phrases`, or `market` alone. |
| `phrases` | array of string | no | Phrase keys to follow on the event. |
| `subjects` | array of string | no | Typed-fact subjects to receive (`fact_extracted`). |
| `webhook_url` | string | no | An HTTPS URL to post each hit to. |
| `context` | array of string | no | Context kinds to receive about the market: `on_air`, `topic`, `near_miss`, `typed`. |
| `market` | string | no | A market ticker. Langbot follows that market's phrases on the event that settles it. |

## Request

**curl**

```bash
curl -X POST "https://api.langbot.ai/v1/subscriptions" \
  -H "Authorization: Bearer $LANGBOT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"market": "KXEARNINGSMENTIONNVDA-26OCT-BLACKWELL", "webhook_url": "https://example.com/hooks/langbot"}'
```

**Python**

```python
from langbot import LangbotClient

async with LangbotClient() as lb:  # reads LANGBOT_API_TOKEN
    sub = await lb.subscribe_market(
        "KXEARNINGSMENTIONNVDA-26OCT-BLACKWELL", webhook_url="https://example.com/hooks/langbot"
    )
    print(sub.id, sub.phrases)
```

**TypeScript**

```ts
import { LangbotClient } from "@langbotai/sdk";

const lb = new LangbotClient(); // reads LANGBOT_API_TOKEN
const sub = await lb.subscribeMarket("KXEARNINGSMENTIONNVDA-26OCT-BLACKWELL", {
  webhookUrl: "https://example.com/hooks/langbot",
});
console.log(sub.id);
```

## Response

`201 Created`

```json
{
  "id": "sub_01J9…",
  "user_id": "usr_01J9…",
  "event_id": "ev_nvda_q3_2026",
  "phrases": ["blackwell"],
  "subjects": [],
  "webhook_url": "https://example.com/hooks/langbot",
  "secret": "9c1f…",
  "created_at": 1760050000000,
  "market_ticker": "KXEARNINGSMENTIONNVDA-26OCT-BLACKWELL",
  "context": [],
  "market": { "ticker": "KXEARNINGSMENTIONNVDA-26OCT-BLACKWELL", "venue": "kalshi", "event_id": "ev_nvda_q3_2026", "title": "Will NVIDIA say \"Blackwell\" on its Q3 earnings call?", "status": "open", "phrases": ["blackwell"], "close_ts": 1760058000000 },
  "market_title": "Will NVIDIA say \"Blackwell\" on its Q3 earnings call?"
}
```

**Webhook delivery.** Each hit is a `POST` of the event JSON to `webhook_url` with `X-Langbot-Signature: sha256=<hex HMAC-SHA256 of the raw body, keyed with secret>`. Up to 4 attempts, 1 s, 4 s and 16 s apart; any 2xx acknowledges. Without a `webhook_url`, hits land in your dashboard inbox.

```ts
import { createHmac, timingSafeEqual } from "node:crypto";
const expected = `sha256=${createHmac("sha256", secret).update(rawBody).digest("hex")}`;
const ok = timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers["x-langbot-signature"]));
```

## Errors

| Status | `code` | When |
|---|---|---|
| 404 | `not_found` | Unknown event or market. |
| 422 | `validation_error` | The market is not yet attached to a live event, or the body mixes `market` with `event_id`. |
| 401 | `auth_required` | No token was sent. |
| 401 | `token_invalid` | The token was not recognised (mistyped, revoked or expired). |
| 402 | `quota_exceeded` | Your plan's subscriptions or webhook endpoints are all in use; delete one or move up a plan. |
| 429 | `rate_limited` | Over your request budget; wait `Retry-After` seconds. |

Every error body is `{"detail": …, "code": "…"}`; branch on `code`. All codes: [Errors](https://langbot.ai/docs/api#errors).
