---
title: "Start an Agent API"
url: /quickstart/agent-api
---

# Start an Agent API

> A live API that sells a worker: an agent that books meetings in each Customer's own inbox and CRM, billed by what each run consumes.

You need Node 20 and a GitHub account. The sample answers on `meetings.apis.dev`; your first deploy answers at once on a free Host, and your own domain is one line.

1. **Start the folder**

   ```sh title="Terminal"
   $ npx apis.dev init agent-api
   ✓ wrote definition.ts: an Agent API
     next: npx apis.dev dev
   ```

   `init` writes an [Agent API](/patterns/agent-api.md) with one job, a schedule and its rates.

2. **Write the file**

   ```ts title="definition.ts"
   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 Customer connects their own inbox and CRM, and the agent works in that Customer's space alone. The schedule runs in each Customer's timezone.

3. **Try it locally**

   ```sh title="Terminal"
   $ npx apis.dev dev
   ✓ every Interface on http://localhost:8787, against your Test twin
     reloads on every save
   ```

   Run the job on localhost before any Customer connects. A run in your [Test twin](/reference/platform.md#test-twin) spends your allowance, and its books never post.

4. **Publish it**

   ```sh title="Terminal"
   $ npx apis.dev deploy
   ✓ signed in with GitHub
   ✓ definition.ts: meetings on meetings.apis.dev
   ✓ Stripe connected: each Customer pays into your own account
   ✓ 1 Noun, 2 Verbs, 2 Sources, 1 Trigger, 3 prices
       Meeting  get, list
       every Mon, Wed, Fri 09:00 → Meeting.book
       Meeting.book    0.01 per 1,000 tokens, 0.05 a minute
       Meeting.get     0 a call
       Meeting.list    0 a call
   ✓ published v1 to https://meetings.apis.dev: 11 Interfaces
       REST              /meetings
       CLI               npx meetings
       MCP               /mcp
       SDK               npm i meetings, pip install meetings
       Docs              /docs
       OpenAPI           /openapi.json
       llms.txt          /llms.txt
       agents.json       /.well-known/agents.json
       Pricing           /pricing
       Landing site      /
       Developer portal  /dashboard
   ✓ Test key: meetings_test_VAbB3nakU7UAZ64nbLV7XAeqznWaS7ed6ef9
   ```

   `Meeting.book` serves no path: the schedule starts it, so it is [internal](/workflow.md#internal). Your Customers read what it booked with `get` and `list`.

5. **Read the price list**

   ```json title="/pricing"
   {
     "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" }
     ]
   }
   ```

   Your Customers and their agents read this before they connect anything: two rates for each run, and reads at 0.

6. **Miss a run with no credits**

   **curl**

   ```sh title="Request"
   curl https://meetings.apis.dev/actions/act_30 \
     -H "Authorization: Bearer $MEETINGS_KEY"
   ```

   **TypeScript**

   ```ts title="TypeScript"
   import { Meetings } from 'meetings'

   const api = new Meetings({ key: process.env.MEETINGS_KEY })
   const { data, meta } = await api.getAction('act_30')
   // data: {
   //   'id': 'act_30',
   //   'verb': 'Meeting.book',
   //   'status': 'refused',
   //   'problem': {
   //     'type': 'https://meetings.apis.dev/docs/errors#payment_required',
   //     'title': 'Payment Required',
   //     'status': 402,
   //     'detail': 'The credit balance is 0; each run draws from it.',
   //     'code': 'payment_required',
   //     'instance': 'req_07091d2bdc823fead0b7',
   //     'offers': [
   //       {
   //         'kind': 'credits',
   //         'price': 10,
   //         'checkout': 'https://meetings.apis.dev/checkout/credits-10'
   //       },
   //       {
   //         'kind': 'credits',
   //         'price': 50,
   //         'checkout': 'https://meetings.apis.dev/checkout/credits-50'
   //       },
   //       {
   //         'kind': 'credits',
   //         'price': 200,
   //         'checkout': 'https://meetings.apis.dev/checkout/credits-200'
   //       }
   //     ]
   //   }
   // }
   // meta.cost: { price: 0, currency: 'USD' }
   ```

   **Python**

   ```python title="Python"
   import os
   from meetings import Meetings

   api = Meetings(key=os.environ["MEETINGS_KEY"])
   data, meta = api.get_action("act_30")
   # data: {
   #   "id": "act_30",
   #   "verb": "Meeting.book",
   #   "status": "refused",
   #   "problem": {
   #     "type": "https://meetings.apis.dev/docs/errors#payment_required",
   #     "title": "Payment Required",
   #     "status": 402,
   #     "detail": "The credit balance is 0; each run draws from it.",
   #     "code": "payment_required",
   #     "instance": "req_07091d2bdc823fead0b7",
   #     "offers": [
   #       {
   #         "kind": "credits",
   #         "price": 10,
   #         "checkout": "https://meetings.apis.dev/checkout/credits-10"
   #       },
   #       {
   #         "kind": "credits",
   #         "price": 50,
   #         "checkout": "https://meetings.apis.dev/checkout/credits-50"
   #       },
   #       {
   #         "kind": "credits",
   #         "price": 200,
   #         "checkout": "https://meetings.apis.dev/checkout/credits-200"
   #       }
   #     ]
   #   }
   # }
   # meta.cost: { "price": 0, "currency": "USD" }
   ```

   **CLI**

   ```sh title="Terminal"
   $ npx meetings login --key $MEETINGS_KEY
   $ npx meetings actions get act_30
   {
     "id": "act_30",
     "verb": "Meeting.book",
     "status": "refused",
     "problem": {
       "type": "https://meetings.apis.dev/docs/errors#payment_required",
       "title": "Payment Required",
       "status": 402,
       "detail": "The credit balance is 0; each run draws from it.",
       "code": "payment_required",
       "instance": "req_07091d2bdc823fead0b7",
       "offers": [
         {
           "kind": "credits",
           "price": 10,
           "checkout": "https://meetings.apis.dev/checkout/credits-10"
         },
         {
           "kind": "credits",
           "price": 50,
           "checkout": "https://meetings.apis.dev/checkout/credits-50"
         },
         {
           "kind": "credits",
           "price": 200,
           "checkout": "https://meetings.apis.dev/checkout/credits-200"
         }
       ]
     }
   }
   price 0 USD · req_15658b3bc7132da17d41
   ```

   **MCP**

   ```sh title="Request"
   curl https://meetings.apis.dev/mcp \
     -H "Authorization: Bearer $MEETINGS_KEY" \
     --json '{
     "jsonrpc": "2.0", "id": 1, "method": "tools/call",
     "params": { "name": "getAction", "arguments": { "id": "act_30" } }
   }'
   ```

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

   {
     "jsonrpc": "2.0",
     "id": 1,
     "result": {
       "content": [
         {
           "type": "text",
           "text": "{\"id\":\"act_30\",\"verb\":\"Meeting.book\",\"status\":\"refused\",\"problem\":{\"type\":\"https://meetings.apis.dev/docs/errors#payment_required\",\"title\":\"Payment Required\",\"status\":402,\"detail\":\"The credit balance is 0; each run draws from it.\",\"code\":\"payment_required\",\"instance\":\"req_07091d2bdc823fead0b7\",\"offers\":[{\"kind\":\"credits\",\"price\":10,\"checkout\":\"https://meetings.apis.dev/checkout/credits-10\"},{\"kind\":\"credits\",\"price\":50,\"checkout\":\"https://meetings.apis.dev/checkout/credits-50\"},{\"kind\":\"credits\",\"price\":200,\"checkout\":\"https://meetings.apis.dev/checkout/credits-200\"}]}}"
         }
       ],
       "structuredContent": {
         "id": "act_30",
         "verb": "Meeting.book",
         "status": "refused",
         "problem": {
           "type": "https://meetings.apis.dev/docs/errors#payment_required",
           "title": "Payment Required",
           "status": 402,
           "detail": "The credit balance is 0; each run draws from it.",
           "code": "payment_required",
           "instance": "req_07091d2bdc823fead0b7",
           "offers": [
             {
               "kind": "credits",
               "price": 10,
               "checkout": "https://meetings.apis.dev/checkout/credits-10"
             },
             {
               "kind": "credits",
               "price": 50,
               "checkout": "https://meetings.apis.dev/checkout/credits-50"
             },
             {
               "kind": "credits",
               "price": 200,
               "checkout": "https://meetings.apis.dev/checkout/credits-200"
             }
           ]
         }
       },
       "_meta": {
         "cost": { "price": 0, "currency": "USD" },
         "requestId": "req_bb912f39549336764a90"
       }
     }
   }
   ```


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

   {
     "id": "act_30",
     "verb": "Meeting.book",
     "status": "refused",
     "problem": {
       "type": "https://meetings.apis.dev/docs/errors#payment_required",
       "title": "Payment Required",
       "status": 402,
       "detail": "The credit balance is 0; each run draws from it.",
       "code": "payment_required",
       "instance": "req_07091d2bdc823fead0b7",
       "offers": [
         {
           "kind": "credits",
           "price": 10,
           "checkout": "https://meetings.apis.dev/checkout/credits-10"
         },
         {
           "kind": "credits",
           "price": 50,
           "checkout": "https://meetings.apis.dev/checkout/credits-50"
         },
         {
           "kind": "credits",
           "price": 200,
           "checkout": "https://meetings.apis.dev/checkout/credits-200"
         }
       ]
     }
   }
   ```

   A Customer connects their inbox and CRM in the developer portal. Monday's run finds no credits, so it is refused before it starts: no work, no charge, and packs to buy.

7. **Buy credits, and the next run books**

   **curl**

   ```sh title="Request"
   curl https://meetings.apis.dev/actions/act_31 \
     -H "Authorization: Bearer $MEETINGS_KEY"
   ```

   **TypeScript**

   ```ts title="TypeScript"
   import { Meetings } from 'meetings'

   const api = new Meetings({ key: process.env.MEETINGS_KEY })
   const { data, meta } = await api.getAction('act_31')
   // data: {
   //   'id': 'act_31',
   //   'verb': 'Meeting.book',
   //   'status': 'done',
   //   'output': { 'meeting': 'mtg_1' },
   //   'usage': { 'tokens': 18400, 'minutes': 6 },
   //   'cost': { 'price': 0.484, 'currency': 'USD' }
   // }
   // meta.cost: { price: 0, currency: 'USD' }
   ```

   **Python**

   ```python title="Python"
   import os
   from meetings import Meetings

   api = Meetings(key=os.environ["MEETINGS_KEY"])
   data, meta = api.get_action("act_31")
   # data: {
   #   "id": "act_31",
   #   "verb": "Meeting.book",
   #   "status": "done",
   #   "output": { "meeting": "mtg_1" },
   #   "usage": { "tokens": 18400, "minutes": 6 },
   #   "cost": { "price": 0.484, "currency": "USD" }
   # }
   # meta.cost: { "price": 0, "currency": "USD" }
   ```

   **CLI**

   ```sh title="Terminal"
   $ npx meetings login --key $MEETINGS_KEY
   $ npx meetings actions get act_31
   {
     "id": "act_31",
     "verb": "Meeting.book",
     "status": "done",
     "output": { "meeting": "mtg_1" },
     "usage": { "tokens": 18400, "minutes": 6 },
     "cost": { "price": 0.484, "currency": "USD" }
   }
   price 0 USD · req_24fbbad973aa8085d69b
   ```

   **MCP**

   ```sh title="Request"
   curl https://meetings.apis.dev/mcp \
     -H "Authorization: Bearer $MEETINGS_KEY" \
     --json '{
     "jsonrpc": "2.0", "id": 1, "method": "tools/call",
     "params": { "name": "getAction", "arguments": { "id": "act_31" } }
   }'
   ```

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

   {
     "jsonrpc": "2.0",
     "id": 1,
     "result": {
       "content": [
         {
           "type": "text",
           "text": "{\"id\":\"act_31\",\"verb\":\"Meeting.book\",\"status\":\"done\",\"output\":{\"meeting\":\"mtg_1\"},\"usage\":{\"tokens\":18400,\"minutes\":6},\"cost\":{\"price\":0.484,\"currency\":\"USD\"}}"
         }
       ],
       "structuredContent": {
         "id": "act_31",
         "verb": "Meeting.book",
         "status": "done",
         "output": { "meeting": "mtg_1" },
         "usage": { "tokens": 18400, "minutes": 6 },
         "cost": { "price": 0.484, "currency": "USD" }
       },
       "_meta": {
         "cost": { "price": 0, "currency": "USD" },
         "requestId": "req_ed960bb1c8504fb2745b"
       }
     }
   }
   ```


   ```http title="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" }
   }
   ```

   After Checkout, the next run books a meeting. It ends `done` with what it consumed in `usage`, and what that cost in `cost`, metered once.

8. **Read the week's meetings**

   **curl**

   ```sh title="Request"
   curl "https://meetings.apis.dev/meetings?week=2026-W44" \
     -H "Authorization: Bearer $MEETINGS_KEY"
   ```

   **TypeScript**

   ```ts title="TypeScript"
   import { Meetings } from 'meetings'

   const api = new Meetings({ key: process.env.MEETINGS_KEY })
   const { data, meta } = await api.listMeetings({ week: '2026-W44' })
   // data: {
   //   'items': [
   //     {
   //       'id': 'mtg_1',
   //       'with': 'Dana Ito',
   //       'company': 'acme.com',
   //       'at': '2026-10-27T15:00:00Z',
   //       'createdAt': '2026-10-19T09:04:00Z',
   //       'updatedAt': '2026-10-19T09:04:00Z'
   //     },
   //     {
   //       'id': 'mtg_2',
   //       'with': 'Sam Okafor',
   //       'company': 'globex.com',
   //       'at': '2026-10-29T16:30:00Z',
   //       'createdAt': '2026-10-21T09:06:00Z',
   //       'updatedAt': '2026-10-21T09:06:00Z'
   //     },
   //     {
   //       'id': 'mtg_3',
   //       'with': 'Lee Marsh',
   //       'company': 'initech.com',
   //       'at': '2026-10-31T14:00:00Z',
   //       'createdAt': '2026-10-23T09:05:00Z',
   //       'updatedAt': '2026-10-23T09:05:00Z'
   //     }
   //   ],
   //   'total': 3,
   //   'next': null
   // }
   // meta.cost: { price: 0, currency: 'USD' }
   ```

   **Python**

   ```python title="Python"
   import os
   from meetings import Meetings

   api = Meetings(key=os.environ["MEETINGS_KEY"])
   data, meta = api.list_meetings(week="2026-W44")
   # data: {
   #   "items": [
   #     {
   #       "id": "mtg_1",
   #       "with": "Dana Ito",
   #       "company": "acme.com",
   #       "at": "2026-10-27T15:00:00Z",
   #       "createdAt": "2026-10-19T09:04:00Z",
   #       "updatedAt": "2026-10-19T09:04:00Z"
   #     },
   #     {
   #       "id": "mtg_2",
   #       "with": "Sam Okafor",
   #       "company": "globex.com",
   #       "at": "2026-10-29T16:30:00Z",
   #       "createdAt": "2026-10-21T09:06:00Z",
   #       "updatedAt": "2026-10-21T09:06:00Z"
   #     },
   #     {
   #       "id": "mtg_3",
   #       "with": "Lee Marsh",
   #       "company": "initech.com",
   #       "at": "2026-10-31T14:00:00Z",
   #       "createdAt": "2026-10-23T09:05:00Z",
   #       "updatedAt": "2026-10-23T09:05:00Z"
   #     }
   #   ],
   #   "total": 3,
   #   "next": null
   # }
   # meta.cost: { "price": 0, "currency": "USD" }
   ```

   **CLI**

   ```sh title="Terminal"
   $ npx meetings login --key $MEETINGS_KEY
   $ npx meetings meetings list --week 2026-W44
   {
     "items": [
       {
         "id": "mtg_1",
         "with": "Dana Ito",
         "company": "acme.com",
         "at": "2026-10-27T15:00:00Z",
         "createdAt": "2026-10-19T09:04:00Z",
         "updatedAt": "2026-10-19T09:04:00Z"
       },
       {
         "id": "mtg_2",
         "with": "Sam Okafor",
         "company": "globex.com",
         "at": "2026-10-29T16:30:00Z",
         "createdAt": "2026-10-21T09:06:00Z",
         "updatedAt": "2026-10-21T09:06:00Z"
       },
       {
         "id": "mtg_3",
         "with": "Lee Marsh",
         "company": "initech.com",
         "at": "2026-10-31T14:00:00Z",
         "createdAt": "2026-10-23T09:05:00Z",
         "updatedAt": "2026-10-23T09:05:00Z"
       }
     ],
     "total": 3,
     "next": null
   }
   price 0 USD · req_61566d5bcadb4268afbb
   ```

   **MCP**

   ```sh title="Request"
   curl https://meetings.apis.dev/mcp \
     -H "Authorization: Bearer $MEETINGS_KEY" \
     --json '{
     "jsonrpc": "2.0", "id": 1, "method": "tools/call",
     "params": { "name": "listMeetings", "arguments": { "week": "2026-W44" } }
   }'
   ```

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

   {
     "jsonrpc": "2.0",
     "id": 1,
     "result": {
       "content": [
         {
           "type": "text",
           "text": "{\"items\":[{\"id\":\"mtg_1\",\"with\":\"Dana Ito\",\"company\":\"acme.com\",\"at\":\"2026-10-27T15:00:00Z\",\"createdAt\":\"2026-10-19T09:04:00Z\",\"updatedAt\":\"2026-10-19T09:04:00Z\"},{\"id\":\"mtg_2\",\"with\":\"Sam Okafor\",\"company\":\"globex.com\",\"at\":\"2026-10-29T16:30:00Z\",\"createdAt\":\"2026-10-21T09:06:00Z\",\"updatedAt\":\"2026-10-21T09:06:00Z\"},{\"id\":\"mtg_3\",\"with\":\"Lee Marsh\",\"company\":\"initech.com\",\"at\":\"2026-10-31T14:00:00Z\",\"createdAt\":\"2026-10-23T09:05:00Z\",\"updatedAt\":\"2026-10-23T09:05:00Z\"}],\"total\":3,\"next\":null}"
         }
       ],
       "structuredContent": {
         "items": [
           {
             "id": "mtg_1",
             "with": "Dana Ito",
             "company": "acme.com",
             "at": "2026-10-27T15:00:00Z",
             "createdAt": "2026-10-19T09:04:00Z",
             "updatedAt": "2026-10-19T09:04:00Z"
           },
           {
             "id": "mtg_2",
             "with": "Sam Okafor",
             "company": "globex.com",
             "at": "2026-10-29T16:30:00Z",
             "createdAt": "2026-10-21T09:06:00Z",
             "updatedAt": "2026-10-21T09:06:00Z"
           },
           {
             "id": "mtg_3",
             "with": "Lee Marsh",
             "company": "initech.com",
             "at": "2026-10-31T14:00:00Z",
             "createdAt": "2026-10-23T09:05:00Z",
             "updatedAt": "2026-10-23T09:05:00Z"
           }
         ],
         "total": 3,
         "next": null
       },
       "_meta": {
         "cost": { "price": 0, "currency": "USD" },
         "requestId": "req_3fafbb1ce75f392bcb51"
       }
     }
   }
   ```


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

   {
     "items": [
       {
         "id": "mtg_1",
         "with": "Dana Ito",
         "company": "acme.com",
         "at": "2026-10-27T15:00:00Z",
         "createdAt": "2026-10-19T09:04:00Z",
         "updatedAt": "2026-10-19T09:04:00Z"
       },
       {
         "id": "mtg_2",
         "with": "Sam Okafor",
         "company": "globex.com",
         "at": "2026-10-29T16:30:00Z",
         "createdAt": "2026-10-21T09:06:00Z",
         "updatedAt": "2026-10-21T09:06:00Z"
       },
       {
         "id": "mtg_3",
         "with": "Lee Marsh",
         "company": "initech.com",
         "at": "2026-10-31T14:00:00Z",
         "createdAt": "2026-10-23T09:05:00Z",
         "updatedAt": "2026-10-23T09:05:00Z"
       }
     ],
     "total": 3,
     "next": null
   }
   ```

   This is what your Customer sees: the meetings the agent booked, in their own space, read at no charge.

9. **Get paid**

   *One run, metered once · USD*

   | Meter | Usage × rate | Amount |
   | --- | --- | --- |
   | Tokens | 18,400 × 0.01 / 1,000 | 0.184 |
   | Minutes | 6 × 0.05 | 0.3 |
   | Reads | get and list | 0 |
   | The run | `Meeting.book` | 0.484 |

   Each run is one line on your Customer's Stripe receipt, its tokens and minutes at your rates. The money lands in your own Stripe account, and a failed run bills nothing.

## Result
Your API answers on https://meetings.apis.dev: an agent that books meetings from each Customer's inbox and CRM, every Mon, Wed, Fri 09:00.

Each run keeps the Customer's timezone, is metered at $0.01 per 1,000 tokens and $0.05 a minute, and is charged once when it ends. A run with no credits is refused before it starts, at no charge. You wrote no scheduler, no meter and no connector to their tools.

## Next steps
- **[Start a Service API](/quickstart/service-api.md) →** sell an outcome, charged once when your Customer accepts the work.
- [Agent API](/patterns/agent-api.md): runs, tools in the Customer's space, budgets and brakes.
- [Agents inside the product](/work/agents.md): leases, handoffs to a person, and the approvals a brake asks for.