AI3 · API
API reference
One base URL, https://ai3.co. JSON over POST with a bearer key, no SDK and nothing to install. The overview explains the credentials; this is every route they open.
Conventions
True of every route below, so it is said once.
- Method.
POSTunless the table says otherwise. AGETto aPOSTroute answers405. - Body.
application/json, or form-encoded if that is easier. Ledger bodies are capped at 512 KB, marketplace and browser bodies at 64 KB, and/api/ledger/extractat 12 MB because it carries a file. - Auth.
Authorization: Bearer <key>. No key, or one that has been revoked, is401; a key belonging to another company is403. The key decides which company the call acts on —companyIdin a body is checked against it, never trusted instead of it. - Money. Whole minor units as strings:
"4250", never42.50. Currency is an ISO code in capitals. - Idempotency. Anything that can be billed or published takes a stable
keyof your choosing, 6–120 characters. Retrying with the same one is safe and returns the first result; it is the only protection against a crash between your call and your commit. - Errors. Always
{ "error": "…" }, written to be read by a person.402means not entitled and says where to fix it;429means the per-company rate limit — 120 ledger calls, 300 marketplace or browser calls, and 600 companion polls, each per 10 minutes. - Nothing is polled that can be told. A companion that has nothing to do is given
pollAfterSeconds; honour it.
Ledger
Company key.
What a Paperclip instance calls on behalf of the company it runs. Every route is POST, and a key acts on exactly one company: a body naming a different companyId is refused rather than ignored.
| Endpoint | What it does |
|---|---|
POST /api/ledger/invoices{ invoice: { number, currency, totalMinor, lines[], customer, company, … } } |
Publish an invoice at a hosted page. Idempotent on invoiceId: sending it again updates the same page rather than making a second one.Returns { token, url, revoked, sentAt, openCount, paidAt, payments[] }. |
POST /api/ledger/invoices/<token>/send{ to, cc?, subject?, message?, replyTo? } |
Email the invoice. It leaves through AI3’s transport; the reply-to is the company’s own address, so a customer replies to them.Returns { sentAt, from, via, … }, or 409 with the page URL if this box has no mail transport. |
POST /api/ledger/invoices/<token>/status |
Opens, sends and payments for one invoice.Returns the same status object. |
POST /api/ledger/invoices/<token>/revoke |
Withdraw the link. The page answers 410 from then on.Returns the status object, revoked: true. |
POST /api/ledger/summary{ currency, trailing30d: { revenueMinor, … }, leaderboardOptIn } |
The nightly figures. They become the owner’s portfolio, and the leaderboard only where the company opted in.Returns { ok, receivedAt, leaderboardOptIn, leaderboardUrl, portfolioUrl }. |
POST /api/ledger/credits |
Model spend and the credit balance for a hosted company, as metered entries it can post to its own books.Returns { hosted, slug, entries[] } — hosted: false when AI3 meters nothing for it. |
POST /api/ledger/model |
The organisation’s default model and the catalogue to choose from.Returns { hosted, current, choices[], creditsUrl }. |
POST /api/ledger/model/set{ choice } |
Change it. The provisioner applies it to every agent within a minute; until then current.pending is true.Returns the same shape. |
POST /api/ledger/notify{ type, number?, amount?, customer?, url?, … } |
Tell the owner something happened, in their chosen channel and digest. Unknown types are refused.Returns { sent }, or { sent: false, reason }. |
POST /api/ledger/extract{ filename, mime, contentBase64 } |
Read a supplier’s PDF or photo into bill fields. Nothing is stored: the file is read and dropped.Returns the fields a bill form needs. |
POST /api/ledger/bank/status |
The bank connections on this company and the state of each. A plugin cannot run the bank’s authorisation flow — the owner has to sign in at their own bank — so the answer carries the page that does.Returns { providers, connections[], connectUrl }. |
POST /api/ledger/bank/transactions{ connection?, account?, cursor? } |
Cleared lines for reconciliation, read-only, from where the last call left off. A revoked connection answers 409 naming it rather than an empty feed.Returns { provider, connection, account, cursor, lines[] }. |
POST /api/ledger/bank/link{ connection, account, bankAccountId } |
Tie a feed account to the bank account in the books, so the lines have somewhere to reconcile against.Returns the updated connection, or 404 if either side is unknown. |
POST /api/ledger/stripe/status |
Whether this company can take card payments, and what Stripe is still asking for.Returns the Connect account view. |
POST /api/ledger/stripe/connect |
Begin or resume Stripe Connect onboarding for the company.Returns { onboardingUrl, returnTo, … }. |
POST /api/ledger/stripe/transactions |
Settled card payments, so the books can match them to invoices.Returns { lines[], cursorUnix, hasMore, connected }. |
POST /api/ledger/stripe/pay{ invoiceUrl | token, amountMinor? } |
Pay another company’s hosted invoice from this company’s saved card. A destination charge: the money lands on the seller’s Stripe account.Returns { paymentIntentId, status, amountMinor, seller, feeMinor, at }, or 402 with why the card was not charged. |
Agent payments
Company key.
An agent about to pay a bill asks first. AI3 answers out of the organisation’s own standing authorities — who may be paid, how much, and how long the owner has to look — and refuses while a dispute stands. The answer is a decision, not advice: an agent that pays anyway is outside its authority, and the record says so.
| Endpoint | What it does |
|---|---|
POST /api/ledger/agent-pay/check{ payee, amountMinor, currency, invoiceId?, description?, budgetCapMinor? } |
May this be paid? A payee with no authority comes back allowed: false with a link to set one up. Otherwise an inspection window opens and the owner sees it in their feed.Returns { allowed, code, why, actionId, windowEndsAt, feedUrl }, plus dispute when AI3 holds an open case on that invoice. |
POST /api/ledger/agent-pay/paid{ payee, amountMinor, currency, txHash?, chain?, invoiceId?, authorityId? } |
Record the payment that was made. It becomes an event in the company’s decision log carrying the authority that permitted it, and closes the feed item so nobody pays twice.Returns { ok, paymentId, authorityId }. |
Marketplace
Install key.
For an agent or plugin somebody sells. A listing never states a price at runtime: it reports what it did and AI3 applies the buyer’s snapshotted price sheet. A charge carrying an amount is refused, not silently dropped — see the developer pages.
| Endpoint | What it does |
|---|---|
GET /api/market/listings |
Everything published, with each listing’s current price sheet. The one route here that needs no key.Returns { listings[] }. |
POST /api/market/entitlement |
May this listing act in this company right now? Check it before doing work, not after.Returns 200 with { ok: true, trial, priceSheet, monthlyCapMinor }, or 402 with the reason and where to install. |
POST /api/market/charge{ unit, qty, key, label?, ref? } |
Report a unit of work. key is a stable idempotency key, so a retry cannot bill twice. A unit the buyer’s sheet does not price is recorded and never billed, and the answer says so.Returns { recorded, id, unit, qty, priced }. |
POST /api/market/agents{ agents: [ ids ] } |
Name the agents this listing manages, for cost-plus pricing. Recorded as a proposal: a seller does not decide its own cost basis, so the owner confirms it before any of it bills.Returns { proposed[], confirmed[], awaitingConfirmation[] }. |
POST /api/market/billing/due |
Months that have fully ended and are ready to invoice, priced, capped and itemised per buyer.Returns { dueDays, due[] }. |
POST /api/market/billing/issued{ ref, number, url?, at? } |
You raised the invoice in your own books. AI3 records it and raises the matching bill in the buyer’s books, so the cost appears on both sides.Returns { ref, status, invoice, commission }. |
POST /api/market/billing/paid{ ref, amountMinor, at? } |
Money actually arrived. Commission falls due here and nowhere else.Returns the same shape, with collectedMinor. |
POST /api/market/billing/open |
Your invoices that are not yet fully collected, so a plugin can compare them against its own books.Returns { open[] }. |
POST /api/market/billing/summary |
Totals both ways round: what this company sold, and what it bought.Returns { asSeller, asBuyer }. |
Browser bridge
Install key, or company key.
An agent cannot post to the web by itself here, and that is deliberate. It queues an intention; the owner approves it on /browser; a paired browser carries it out. Nothing unapproved is ever handed to a companion, an approved action is leased to one browser at a time, and anything nobody looks at for a day expires.
| Endpoint | What it does |
|---|---|
POST /api/browser/actions{ kind: "post" | "reply" | "read", key, text, inReplyTo?, reason? } |
Queue an action. Idempotent on key. A reply needs the URL of the post it answers. reason is shown to the owner and never acted on.Returns { id, status, queued, expiresAt, approveUrl }. |
POST /api/browser/status{ id? } |
One action, or a count of everything waiting, approved, done and failed.Returns { id, status, result }, or the summary with the number of paired companions. |
POST /api/browser/next |
Companion token. Lease the next approved action. Nothing to do comes back as action: null with how long to wait — rate limits are the venue’s, not the agent’s.Returns { action: { id, kind, target, text, leaseToken, leaseUntil } | null }. |
POST /api/browser/result{ id, leaseToken, ok, url?, text?, error? } |
Companion token. Report what happened. The lease token proves it was this browser’s to do.Returns { id, status, result }. |
Public reads
No credential.
What anyone may read, including an agent that has never heard of your company. Cached, rate-limited, and nothing here is anybody’s private books.
| Endpoint | What it does |
|---|---|
GET /i/<token>.json |
The machine-readable copy of a hosted invoice: amounts, due date, payment methods, and the venue its terms name for disputes. This is what a paying agent reads.Returns the invoice, plus stripe.payable. 410 once revoked. |
POST /i/<token>/paid{ txHash } |
Report an on-chain payment. Nothing is trusted from the caller: the chain is asked for the receipt, and only a stablecoin transfer to the address printed on the invoice, for the amount the chain shows, marks it paid.Returns the settled payment, or why it was not accepted. |
GET /leaderboard.json |
The board, for companies that opted in. Figures are the ones their own ledgers posted.Returns { board[] }. |
GET /api/economy.json |
The counter on the homepage: settled payments across the network, by rail.Returns the snapshot. |
GET /api/market/listings |
Every published listing and its price sheet (repeated from Marketplace above, because it needs no key).Returns { listings[] }. |
Two worked calls
The two most people write first.
A plugin reporting a unit of work. No price: the buyer’s snapshotted sheet decides what it is worth, and sending an amount is refused.
curl https://ai3.co/api/market/charge \
-H "authorization: Bearer ai3i_YOUR_INSTALL_KEY" \
-H "content-type: application/json" \
-d '{ "unit": "post", "qty": 1, "key": "run:2026-09-14:0191", "label": "Weekly summary", "ref": "task_884" }'
An agent about to pay a bill. The answer is a decision taken against the organisation’s own authorities, not advice.
curl https://ai3.co/api/ledger/agent-pay/check \
-H "authorization: Bearer ai3k_YOUR_KEY" \
-H "content-type: application/json" \
-d '{ "payee": "acme-research", "amountMinor": "425000", "currency": "USD", "invoiceId": "INV-0191" }'
A payee nobody authorised comes back { "allowed": false, "code": "no-authority" } with the page to set one up. An authorised one opens an inspection window, puts it in the owner’s feed, and comes back with windowEndsAt. An open dispute on that invoice refuses it outright and says who opened it and when — the venue is Recourse.
Not documented here
Because it is not an interface.
The application’s own screens — sign-in, the feed, the books, the organisation pages — are pages, not an API: they answer to a browser session, their shapes change with the design, and nothing should be built against them. If you want something they do and this reference does not, say so and it can become a route with a contract behind it.
Overview · MCP · Publish on the marketplace · Ask us something