---
title: "What every product gets"
url: /every-product
---

# What every product gets

> Everything around your API, live on your own domain from one deploy: CLI, MCP, SDKs, docs, a landing site, a portal and Stripe billing.

**Build**

| What you get | Where | What it does |
| --- | --- | --- |
| The API | each Noun's path | REST at the paths of your Nouns and Verbs, with one error shape, Batch, and the price and a request id on every reply. |
| Records | POST, GET, PATCH and DELETE /{nouns} | Create, get, list, update and delete on every Noun your Customers write, each record in its Customer's own space. |
| Lifecycle moves | POST /{nouns}/{id}/{verb} | One Verb per move, refused with 409 from the wrong state and with 403 for the wrong Role. |
| The change feed | /events | Every change as an Event in `seq` order, read back from any point with `after`. |
| Outbound webhooks | POST /watches | A Watch per URL your Customer picks: each change signed, retried until it lands, each attempt kept, and replayable. |
| Inbound webhooks | a Source's deliveries | Each delivery verified, then written as an Event before anything runs. |
| Durable runs | /actions/{id} | A Workflow's run as an Action: Pending with its reason, then done, across restarts and long waits. |

**Launch**

| What you get | Where | What it does |
| --- | --- | --- |
| CLI | a command per Verb | A command per Verb under your API's own name, generated from the same file. |
| MCP | /mcp | One tool per Verb with its price, then search, fetch and do, each answering the same body and price as REST; a Key or OAuth 2.1 signs in. |
| SDK | /sdk | TypeScript and Python clients under your API's name, generated from the file and published with it. |
| Docs | /docs | HTML and Markdown, a page per Noun, Verb and Event, with OpenAPI, llms.txt and agents.json. |
| Landing site | / | The pitch, Try it with a Test key, the prices and Get started, on your own domain. |
| Developer portal | /dashboard | Each Customer's keys, usage and balance, plan, invoices, spend ceiling, webhook deliveries with Replay, and why a run waits. |

**Monetize**

| What you get | Where | What it does |
| --- | --- | --- |
| The price on every reply | X-Cost and /pricing | The posted price of each Verb, on /pricing and on every reply, with a miss free. |
| Keys | Test and Live | Keys your Customers sign up for, each Live key with a Test twin whose calls never post. |
| Free calls | the Offer's free calls | The Offer's `free` calls come off each month before any credit is drawn. |
| Prepaid credits | each Customer's balance | Each priced call draws once from the Customer's credit balance, at the price it was served at, even when retried; a plan bills a month ahead. |
| Stripe billing | your own Stripe account | Each Customer buys credits through Stripe Checkout and plans through a Stripe subscription, with Stripe invoices and receipts, into your own Stripe account. |
| Your Admin | your account | Your revenue by Verb and by Customer, each payment beside the calls it paid for, and your margin per Verb. |

## Build
- **The API**: each Noun's path
- **Records**: `POST, GET, PATCH and DELETE /{nouns}`
- **Lifecycle moves**: `POST /{nouns}/{id}/{verb}`
- **The change feed**: `/events`
- **Outbound webhooks**: `POST /watches`
- **Inbound webhooks**: a Source's deliveries
- **Durable runs**: `/actions/{id}`

Every change writes one Event: create, update, delete and restore give `created`, `updated`, `deleted` and `restored`, and each move its own past form.

A [Watch](/reference.md#events) sends each change your Customer picks to their URL, signed, and retries a failed delivery until it lands. Each attempt is kept, and any delivery replays from the developer portal.

```http title="Delivery"
POST /hooks/pipeline HTTP/1.1
Host: hooks.acme.com
X-Webhook-Attempt: 1
X-Webhook-Signature: t=1792833000,v1=2c067386fcd9006ece95389e2fde364074ad34388decdfc0a31f3edce14d0252

{
  "id": "evt_2",
  "type": "Deal.opened",
  "at": "2026-10-24T09:10:00Z",
  "seq": 2,
  "data": {
    "id": "deal_1",
    "title": "Pilot",
    "amount": "5000.00",
    "company": "company_1",
    "stage": "opened"
  }
}
```


```ts title="Receive it"
import { createHmac, timingSafeEqual } from 'node:crypto'

// verify(request, secret): true when the delivery was signed with your secret in the last 5 minutes
export async function verify(request: Request, secret: string) {
  const header = request.headers.get('X-Webhook-Signature') ?? ''
  const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')))
  if (!t || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false
  const want = createHmac('sha256', secret).update(`${t}.${await request.text()}`).digest()
  const got = Buffer.from(v1 ?? '', 'hex')
  return got.length === want.length && timingSafeEqual(got, want)
}

// The receiver: verify a copy, then read the Event once by its id.
const seen = new Set<string>()
export default {
  async fetch(req: Request) {
    if (!(await verify(req.clone(), process.env.PIPELINE_WEBHOOK_SECRET!))) return new Response(null, { status: 401 })
    const event = await req.json()
    if (!seen.has(event.id)) seen.add(event.id)  // a retry carries the same id
    return new Response(null, { status: 204 })
  },
}
```

## Launch
One call answers the same body and price on every [Interface](/reference.md#interface). The CLI and both SDKs carry your API's own name, generated from the file and published with it.

**curl**

```sh title="Request"
curl https://companyfacts.apis.dev/companies/acme.com \
  -H "Authorization: Bearer $COMPANYFACTS_KEY"
```

```http title="Response"
HTTP/1.1 200 OK
X-Cost: price=0.002, currency="USD"
X-Request-Id: req_3cd59a9a1ede10c5bf21

{ "domain": "acme.com", "employees": 240, "sector": "Software" }
```

**MCP**

```sh title="Request"
curl https://companyfacts.apis.dev/mcp \
  -H "Authorization: Bearer $COMPANYFACTS_KEY" \
  --json '{
  "jsonrpc": "2.0", "id": 1, "method": "tools/call",
  "params": { "name": "getCompany", "arguments": { "domain": "acme.com" } }
}'
```

```http title="Response"
HTTP/1.1 200 OK
X-Cost: price=0.002, currency="USD"
X-Request-Id: req_f9cf5fc65654d907192f

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [ … ],
    "structuredContent": {
      "domain": "acme.com",
      "employees": 240,
      "sector": "Software"
    },
    "_meta": {
      "cost": { "price": 0.002, "currency": "USD" },
      "requestId": "req_f9cf5fc65654d907192f"
    }
  }
}
```

**TypeScript**

```ts title="TypeScript"
import { Companyfacts } from 'companyfacts'

const api = new Companyfacts({ key: process.env.COMPANYFACTS_KEY })
const { data, meta } = await api.getCompany('acme.com')
// data: { 'domain': 'acme.com', 'employees': 240, 'sector': 'Software' }
// meta: { status: 200, cost: { 'price': 0.002, 'currency': 'USD' },
//         requestId: 'req_9fd1df32ff7df11f695a' }
```

**Python**

```python title="Python"
import os
from companyfacts import Companyfacts

api = Companyfacts(key=os.environ["COMPANYFACTS_KEY"])
data, meta = api.get_company("acme.com")
# data: { "domain": "acme.com", "employees": 240, "sector": "Software" }
# meta.cost: { "price": 0.002, "currency": "USD" }
```

**CLI**

```sh title="Terminal"
$ npx companyfacts login --key $COMPANYFACTS_KEY
$ npx companyfacts companies get acme.com
{ "domain": "acme.com", "employees": 240, "sector": "Software" }
price 0.002 USD · req_3cd59a9a1ede10c5bf21
```

Your landing site and developer portal come from the same file. The pitch is the comment above it, and the prices are its Offer.

*companyfacts.apis.dev/*

| Part | What it shows |
| --- | --- |
| The hero | The file's name and its pitch, the comment above the file, with Get started. |
| Try it | Each Verb with its example, called live with a Test key the page mints. |
| Pricing | A card per plan from `/pricing`, then each price per call, the free calls and the credit packs. |
| Get started | Sign-up, Stripe Checkout for a plan or credits, then a Live key. |
| Docs and MCP | The docs, `llms.txt` and the MCP server, for people and for agents. |

*companyfacts.apis.dev/dashboard*

| Part | What it shows |
| --- | --- |
| Keys | Each Live key and its Test twin: made, rolled and revoked. |
| Usage and balance | Usage by Verb and day, the credit balance, and a top-up through Checkout. |
| Plan | The plan and its seats, and a switch to another plan. |
| Invoices | Each Stripe invoice and receipt. |
| Spend ceiling | A ceiling on each Key; past it, a call asks for Approval. |
| Webhook deliveries | Each Watch, its deliveries and their attempts, with Replay. |
| Runs | Each Action that waits, and its Pending reason. |

### What an agent sees
An agent reads `/.well-known/agents.json` first, then the product's own `llms.txt`, then `tools/list`. It lists one tool per Verb, with its schemas, hints from its kind and its price.

```json title="tools/list"
{
  "tools": [
    {
      "name": "getCompany",
      "description": "Answers one company by its domain, with its headcount and sector.",
      "inputSchema": {
        "type": "object",
        "properties": { "domain": { "type": "string" } },
        "required": ["domain"]
      },
      "outputSchema": { … },
      "annotations": { "readOnlyHint": true },
      "_meta": { "price": 0.002, "currency": "USD" }
    },
    {
      "name": "searchCompanies",
      "description": "Lists the companies in one sector, each with its domain.",
      "inputSchema": {
        "type": "object",
        "properties": { "sector": { "type": "string" } },
        "required": ["sector"]
      },
      "outputSchema": { … },
      "annotations": { "readOnlyHint": true },
      "_meta": { "price": 0.005, "currency": "USD" }
    },
    { "name": "search" },
    { "name": "fetch" },
    { "name": "do" }
  ]
}
```

## Monetize
```json title="/pricing"
{
  "currency": "USD",
  "rates": [
    { "name": "Company.get",
      "price": 0.002, "unit": "call" },
    { "name": "Company.search",
      "price": 0.005, "unit": "call" },
    { "name": "Company.watch",
      "price": 0.1, "unit": "month" }
  ],
  "free": { "Company.get": 100 },
  "credits": [
    10,
    50,
    { "pay": 200,
      "get": 220 }
  ]
}
```

Each priced call draws once from prepaid credits, at the price it was served at.

Each Customer pays through Stripe, into your account.

### When a Customer needs more
Every refusal sells. A 402 or a 403 carries `offers`: credit packs, a plan or a Verb, each with its Stripe Checkout link. The call after payment goes through.

**curl**

```sh title="Request"
curl https://companyfacts.apis.dev/companies/acme.com \
  -H "Authorization: Bearer $COMPANYFACTS_KEY"
```

**TypeScript**

```ts title="TypeScript"
import { Companyfacts } from 'companyfacts'

const api = new Companyfacts({ key: process.env.COMPANYFACTS_KEY })
const { error, meta } = await api.getCompany('acme.com')
// error: { status: 402, code: 'payment_required' }
// meta.cost: { price: 0, currency: 'USD' }
```

**Python**

```python title="Python"
import os
from companyfacts import Companyfacts

api = Companyfacts(key=os.environ["COMPANYFACTS_KEY"])
error, meta = api.get_company("acme.com")
# error: { "status": 402, "code": "payment_required" }
# meta.cost: { "price": 0, "currency": "USD" }
```

**CLI**

```sh title="Terminal"
$ npx companyfacts login --key $COMPANYFACTS_KEY
$ npx companyfacts companies get acme.com
error 402 payment_required
price 0 USD · req_71f702a3d4250104f0bf
```

**MCP**

```sh title="Request"
curl https://companyfacts.apis.dev/mcp \
  -H "Authorization: Bearer $COMPANYFACTS_KEY" \
  --json '{
  "jsonrpc": "2.0", "id": 1, "method": "tools/call",
  "params": { "name": "getCompany", "arguments": { "domain": "acme.com" } }
}'
```

```http title="Response"
HTTP/1.1 200 OK
X-Cost: price=0, currency="USD"
X-Request-Id: req_fe35bd47ea83662091f1

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "isError": true,
    "content": [ … ],
    "structuredContent": { "status": 402, "code": "payment_required" },
    "_meta": {
      "cost": { "price": 0, "currency": "USD" },
      "requestId": "req_fe35bd47ea83662091f1"
    }
  }
}
```


```http title="Response"
HTTP/1.1 402 Payment Required
Content-Type: application/problem+json
X-Cost: price=0, currency="USD"
X-Request-Id: req_71f702a3d4250104f0bf

{
  "type": "https://companyfacts.apis.dev/docs/errors#payment_required",
  "title": "Payment Required",
  "status": 402,
  "detail": "The credit balance is 0; Company.get draws 0.002 from it.",
  "code": "payment_required",
  "instance": "req_71f702a3d4250104f0bf",
  "offers": [
    {
      "kind": "credits",
      "price": 10,
      "checkout": "https://companyfacts.apis.dev/checkout/credits-10"
    },
    {
      "kind": "credits",
      "price": 50,
      "checkout": "https://companyfacts.apis.dev/checkout/credits-50"
    },
    {
      "kind": "credits",
      "price": 200,
      "checkout": "https://companyfacts.apis.dev/checkout/credits-200"
    }
  ]
}
```

An agent sets `Max-Cost`, and a call above it is refused before it runs, at no charge. A spend ceiling the owner sets on a [Key](/reference.md#key) answers 403 with an Approval URL.

```sh title="Request"
curl https://companyfacts.apis.dev/mcp \
  -H "Authorization: Bearer $COMPANYFACTS_KEY" \
  --json '{
  "jsonrpc": "2.0", "id": 1, "method": "tools/call",
  "params": { "name": "getCompany", "arguments": { "domain": "acme.com" },
    "_meta": { "maxCost": 0.001 } }
}'
```

```http title="Response"
HTTP/1.1 200 OK
X-Cost: price=0, currency="USD"
X-Request-Id: req_30d98961416616e608d0

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "isError": true,
    "content": [ … ],
    "structuredContent": {
      "type": "https://companyfacts.apis.dev/docs/errors#over_max_cost",
      "title": "Payment Required",
      "status": 402,
      "detail": "Company.get costs 0.002; the call allows 0.001.",
      "code": "over_max_cost",
      "instance": "req_30d98961416616e608d0"
    },
    "_meta": {
      "cost": { "price": 0, "currency": "USD" },
      "requestId": "req_30d98961416616e608d0"
    }
  }
}
```

### Your revenue
*your Admin, at apis.dev*

| Part | What it shows |
| --- | --- |
| Revenue | Revenue by Verb and by Customer, each payment beside the calls it paid for. |
| Customers | Each Customer with their plan, balance and usage. |
| Margin | Your margin per Verb, from your Sources' cost. |