Skip to content

Patterns

Five kinds of API product you can sell, each priced its own way, and each one file of the same five blocks.

The grid

Who writes each field is the pattern. A field with no from is your Customer's.
Who writes each field is the pattern. A field with no from is your Customer's.
PatternNounthe shape you promiseVerbwhat can be done to itSourcewhere its data comes fromTriggerwhat reacts to a changeOfferthe price
Dataset APISell the facts about a thing, priced per answer.Sources write itCompany, keyed by its domainget by key, search by a fieldthe API you resell, mapped into yoursa refresh on a schedulea price per answer, from credits; a miss is free
Headless SaaSSell a home for your Customers' records, priced by plan and seat.Your Customer writes ityour Customers' records and their Relationshipscreate, get, list, update and delete, and a lifecycleeach Customer's own CRM, as an Integrationa Workflow on Deal.wonplans with seats that include calls, and a price past them
Function APISell an answer, priced per call.A Function writes itthe Noun it acts ona Function: code, a model, an agent or a personnonenonea price per call, by kind; a failure is free
Agent APISell a worker who does the job, billed by consumption.An agent writes ityour Customer's records, in their spacethe job, done by an agent for themyour Customer's inbox and CRMa schedule: three meetings a weekby consumption: tokens and minutes
Service APISell an outcome, charged once when it is done: the only pattern priced by outcome.A Workflow writes ita work Thing the buyer watchesorder, then one Verb per movethe filing office's receiptFiling.ordered starts the draft25 when accepted, under an Outcome contract

The five blocks

definition.ts
16 lines
import { define, workflow } from 'apis.dev'

/** Deals for each Customer, kicked off when won. */
export default define({
  name: 'pipeline', domain: 'pipeline.apis.dev',
  sources: { firms: { origin: 'https://firms.example.com' } },  // Source: where its data comes from
  Deal: {                                                   // Noun: the shape you promise
    title: 'string', amount: 'decimal(12,2)',
    stage: {                                                // Verb: what can be done to it
      open: [], qualify: 'opened', win: 'qualified',
    },
    kickoff: workflow(async ($, deal) => { … }),
  },
  on: { 'Deal.won': 'Deal.kickoff' },                       // Trigger: what reacts to a change
  offer: { Team: { month: 49, seats: 5 }, 'Deal.open': 0.001 },  // Offer: the price
})

Five blocks, four keys: Verbs live inside their Noun. The Dataset API and the Headless SaaS have pages of their own, and each starts a Get started track.

Answers, workers and outcomes

The other three are each one file here.

Function API

The four Function kinds
The four Function kinds
KindWhat it doesHow longVerbPrice
CodecomputesmillisecondsCompany.score0.001
GenerativewritessecondsCompany.summarize0.02
Agenticresearches, with toolsminutesCompany.research0.5
Humandecideshours or daysCompany.verify5

A Verb whose value is a Function sells an answer, and its kind sets how long it takes. returns types the output on every Interface: the Action, the MCP outputSchema, OpenAPI and the SDK. In a prompt, {domain} reads a field and {company.name} follows a Relationship.

definition.ts
26 lines
import { define, code, generative, agentic, human } from 'apis.dev'

/** Answers about any company by its domain, from a score in milliseconds to a person's check in hours. */
export default define({
  name: 'summaries', domain: 'summaries.apis.dev',
  Company: {                                                // Noun: the Noun it acts on
    domain:    'url!#',
    /** A score from 0 to 100, from the domain alone. */
    score:     code((c) => ({ score: c.domain.endsWith('.ai') ? 80 : 50 })),  // Verb: a Function
    summarize: generative('Two sentences on what {domain} sells',
      { returns: { summary: 'string' } }),
    research:  agentic('Find the head of sales at {domain}',
      { returns: { headOfSales: 'string' } }),
    verify:    human('Call and confirm the head of sales',
      { returns: { confirmed: 'boolean' }, role: 'researcher' }),
  },
  Text: {                                                   // a Function on no stored Noun
    translate: generative('Translate {text} into {to}',
      { input: { text: 'string', to: 'string' }, returns: { text: 'string' } }),
  },
  offer: {                                                  // Offer: per call
    'Company.score': 0.001, 'Company.summarize': 0.02,
    'Company.research': 0.5, 'Company.verify': 5,
    'Text.translate': 0.01,
  },
})

A fast answer comes back done. A slow one answers 202 with where to poll, and the caller polls its Action until it is done. A Function on a Noun with no records, such as Text.translate, takes its input in the body.

Request
curl -X POST https://summaries.apis.dev/companies/acme.com/summarize \
  -H "Authorization: Bearer $SUMMARIES_KEY"
Response
HTTP/1.1 200 OK
X-Cost: price=0.02, currency="USD"
X-Request-Id: req_fa5075382229d67f1ce9

{
  "id": "act_1",
  "verb": "Company.summarize",
  "status": "done",
  "output": { "summary": "Acme sells pipeline software to B2B sales teams." }
}

Each kind has its price: 0.001 to compute, 0.02 to write, 0.5 to research, 5 for a person. A failed or cancelled Action costs nothing.

Terminal
$ npx apis.dev init function-api
$ npx apis.dev deploy
✓ published v1 to https://summaries.apis.dev
$ curl -X POST https://summaries.apis.dev/companies/acme.com/summarize \
  -H "Authorization: Bearer $SUMMARIES_KEY"

Agent API

One run, metered once · USD
One run, metered once · USD
MeterUsage × rateAmount
Tokens18,400 × 0.01 / 1,0000.184
Minutes6 × 0.050.3
Readsget and list0
The runMeeting.book0.484

An agent does the job for each Customer, in their own space, with their own inbox and CRM as Sources. Its schedule runs once per Customer, in that Customer's timezone, and a Max-Cost on the run stops it at the ceiling.

definition.ts
18 lines
import { define, agentic } from 'apis.dev'

/** An agent that books three meetings a week with each Customer's leads, in their own inbox and CRM. */
export default define({
  name: 'meetings', domain: 'meetings.apis.dev',
  sources: { inbox: { system: 'email', scope: 'customer' },  // Source: each Customer's own
             crm:   { system: 'crm',   scope: 'customer' } },
  Meeting: {                                                // Noun: their records, in their space
    with: 'string', company: 'string', at: 'date',
    book: agentic('Book a meeting with the next lead in {crm}',  // Verb: the job
      { tools: ['inbox', 'crm'], returns: { meeting: 'string' } }),
  },
  on: { 'every Mon, Wed, Fri 09:00': 'Meeting.book' },      // Trigger: in each Customer's timezone
  offer: {                                                  // Offer: by use
    'Meeting.book': { per1000Tokens: 0.01, perMinute: 0.05 },
    'Meeting.get': 0, 'Meeting.list': 0,
  },
})

Each run is an Action that carries what it spent.

GET meetings.apis.dev/actions/act_31
Request
curl https://meetings.apis.dev/actions/act_31 \
  -H "Authorization: Bearer $MEETINGS_KEY"
Response
HTTP/1.1 200 OK
X-Cost: price=0, currency="USD"
X-Request-Id: req_24fbbad973aa8085d69b

{
  "id": "act_31",
  "verb": "Meeting.book",
  "status": "done",
  "output": { "meeting": "mtg_1" },
  "usage": { "tokens": 18400, "minutes": 6 },
  "cost": { "price": 0.484, "currency": "USD" }
}

Each run is billed by consumption: 0.01 per 1,000 tokens and 0.05 a minute, metered once. Reads cost nothing.

/pricing
14 lines
{
  "currency": "USD",
  "rates": [
    { "name": "Meeting.get",
      "price": 0, "unit": "call" },
    { "name": "Meeting.list",
      "price": 0, "unit": "call" },
    { "name": "Meeting.book",
      "price": 0.01,
      "unit": "1000 tokens" },
    { "name": "Meeting.book",
      "price": 0.05, "unit": "minute" }
  ]
}
Terminal
$ npx apis.dev init agent-api
$ npx apis.dev deploy
✓ published v1 to https://meetings.apis.dev
$ curl https://meetings.apis.dev/meetings?week=2026-W44 \
  -H "Authorization: Bearer $MEETINGS_KEY"

Service API

Filing: its lifecycle

A Service API sells an outcome: a work Thing the buyer watches move through its lifecycle, under an Outcome contract. It is charged once, on the move the contract names, and a rejected one gets its remedy.

definition.ts
26 lines
import { define, workflow, agentic } from 'apis.dev'

/** Each Customer's annual report, drafted, signed and filed, charged once when the Customer accepts it. */
export default define({
  name: 'filings', domain: 'filings.apis.dev',
  Filing: {                                     // Noun: the work your buyer watches
    company: 'string', year: 'number',
    status: {                                   // Verbs: one per move
      order: [],
      draft: { in: 'ordered', run: workflow(async ($, filing) => {
        const { form } = await $.generative('Draft the annual report for {company}', filing,
          { returns: { form: 'string' } })
        await $.human('Check the draft', { form }, { role: 'paralegal' })  // unseen: a step
        return { form }
      }) },
      sign: 'drafted',                          // seen: the Customer's turn
      file: { in: 'signed', run: agentic('File the signed report with the state') },
      accept: 'filed', reject: 'filed',         // the Customer accepts, or it is redone
    },
  },
  on: { 'Filing.ordered': 'Filing.draft', 'Filing.signed': 'Filing.file' },
  offer: {
    'Filing.order': { price: 25, when: 'accepted', accept: 'customer',
                      acceptWithin: '7d', remedy: 'redo' },
  },
})

The paralegal's check is a step inside draft, so no one outside sees it. The signature is a state: the Customer acts, and file becomes allowed.

POST filings.apis.dev/filings/filing_1/sign
Request
curl -X POST https://filings.apis.dev/filings/filing_1/sign \
  -H "Authorization: Bearer $FILINGS_KEY"
Response
HTTP/1.1 200 OK
X-Action-Id: act_3
X-Cost: price=0, currency="USD"
X-Request-Id: req_47d0a7b035cb42c3265d

{
  "id": "filing_1",
  "company": "acme.com",
  "year": 2026,
  "status": "signed",
  "createdAt": "2026-10-24T09:00:00Z",
  "updatedAt": "2026-10-25T16:40:00Z"
}

When the Customer accepts, the fee is charged, and the delivery says so.

Delivery
POST /hooks/filings HTTP/1.1
Host: hooks.acme.com
X-Webhook-Attempt: 1
X-Webhook-Signature: t=1793175300,v1=ac668e6901ac0de4e796256b7d3c62e3ac59d85a0230fea22ab0c430b952eab7

{
  "id": "evt_11",
  "type": "Filing.accepted",
  "at": "2026-10-28T08:15:00Z",
  "seq": 11,
  "data": { "id": "filing_1", "status": "accepted" },
  "charged": { "price": 25, "currency": "USD" }
}

It costs 25 when accepted, the only pattern priced by outcome; a rejected filing is redone at no charge.

/pricing
8 lines
{
  "currency": "USD",
  "rates": [
    { "name": "Filing.order",
      "price": 25, "unit": "outcome",
      "when": "accepted" }
  ]
}
Terminal
$ npx apis.dev init service-api
$ npx apis.dev deploy
✓ published v1 to https://filings.apis.dev
$ curl https://filings.apis.dev/filings \
  -H "Authorization: Bearer $FILINGS_KEY" \
  --json '{"company":"acme.com","year":2026}'

Built on each other

Every API product is a Source for the next one. Name it by its api, and say what each of its calls costs you.

definition.ts
17 lines
import { define } from 'apis.dev'

/** Sales leads: a company and its size, priced per lead. */
export default define({
  name: 'leads', domain: 'leads.apis.dev',
  sources: {
    firms: {
      api: 'companyfacts.apis.dev',     // another API product
      cost: { 'Company.get': 0.002, 'Company.search': 0.005 },
    },
  },
  Lead: {
    domain:    { type: 'url!#',   from: 'firms', map: 'domain' },
    employees: { type: 'number?', from: 'firms', map: 'employees' },
  },
  offer: { 'Lead.get': 0.005, 'Lead.search': 0.0125 },
})

Deploy prints your margin on each Verb, to you alone, and refuses a price below its Sources' cost with below_cost.

Terminal
$ npx apis.dev deploy
✓ Lead.get     0.005   · Source 0.002  · margin 60%
✓ Lead.search  0.0125  · Source 0.005  · margin 60%
✓ published to https://leads.apis.dev

Your cost never reaches a Customer: X-Cost carries the price and its currency, and nothing else.