Three on-ramps. One drafting pipeline.
An Ounie account calls REST with a bearer token. An assistant — including the Ounie AI Team — connects over MCP. An agent with no Ounie account pays per call in USDC on Base. All three run the same generator and return the same shape.
notLegalAdvice: true and the notice in full, so a caller reading only JSON is still told.Authentication
Two keys reach the credit rail, and they are not interchangeable.
pol_live_…— this app's own key, minted at /dashboard/api-keys. It drafts from facts you supply or a URL we read, and it reads, lists and revokes. It cannot reach your Ounie brains, because it carries no ounie.com credential.ounie_live_…— your ounie.com developer key, the fleet master key. Same as above, plusbrain_ids: it is forwarded upstream to ounie.com so retrieval runs as you. Enable "Use across Ounie apps" on ounie.com settings first.
Send it as Authorization: Bearer …, or as ?api_key=… for hosts that cannot set a header — the Ounie AI Team's manual MCP entry is the reason that fallback exists. A key only ever spends its owner's wallet, and a short wallet is refused with 402 before any work runs. An agent can never overdraw.
POST /api/policies
Draft up to 3 documents. Synchronous — fact gathering and one model call per document, inside the request.
{
"documents": ["privacy", "terms", "refund"],
"contact_email": "hello@acme.dev",
// Give it facts by any ONE of these three (or several):
"business": {
"name": "Acme",
"what_it_does": "A hosted uptime monitor for small teams",
"audience": "engineering teams under 20 people",
"data_collected": ["email", "account name", "usage analytics"],
"third_parties": ["Stripe", "Postmark"],
"payment_model": "monthly subscription, billed in advance",
"refund_window": "14 days, pro-rated"
},
"url": "https://acme.dev",
"brain_ids": ["<uuid>"], // needs the ounie_live_ master key
"jurisdiction_hint": "California, USA"
}contact_email is required. At least one of business (with a name or a description), url, or brain_ids is required. Up to 5 brains.
{
"ok": true,
"set_id": "…",
"documents": [
{
"id": "…",
"kind": "privacy",
"title": "Privacy Policy",
"markdown": "# Privacy Policy\n\n## Who we are\n…",
"sections": [{ "heading": "Who we are", "body": "…" }],
"assumptions": ["We assumed you process data only to run the service, because …"],
"gaps": ["Add the legal entity name and registered address."],
"citations": [{ "brainId": "…", "slug": "…", "title": "…" }],
"word_count": 612,
"hostedUrl": "https://policies.ounie.com/p/…"
}
],
"rejected": [],
"credits_spent": 15,
"generatedAt": "2026-08-24T…",
"notLegalAdvice": true,
"notice": "This drafts a starting document from your facts. It is not legal advice…"
}assumptions and gaps
These are not decoration and they are not a to-do list bolted on afterwards. They are the mechanism that lets the draft avoid inventing things.
assumptions[]— what the draft assumed because the facts did not say, stated plainly, and visible in the document itself.gaps[]— what you have to supply before publishing. Every fact a clause needed and did not have ends up here.
What you will never get is a bracketed placeholder. A generated document is scanned for [Your Company], {{template}} tags, fill-in-the-blank rules and TBDs before it is stored; a draft containing one is discarded and never billed. It is checked in code, not merely asked for in the prompt, because that is the difference between a rule and a hope.
Hosted pages
Every drafted document gets a public URL at /p/<token> — a zero-JS page carrying the document, its assumptions, its gaps and the not-legal-advice notice above the text. It is noindex(meta tag and HTTP header) and the token is unguessable, so a draft nobody has reviewed will not appear in search results under a business's name.
Revoke or restore with PATCH /api/documents/<id> { "revoked": true }. Revocation takes effect on the next request. Restoring returns the same URL rather than a new one — a business that pulled a policy for an hour does not want its link to change.
Refusals
Everything knowable from the request alone is refused before any work runs — and, on the x402 rail, before any price is quoted.
| Code | HTTP | Why |
|---|---|---|
| contact_email_required | 400 | A policy nobody can be reached about is not a policy, and we will not invent an address. |
| no_fact_source | 400 | No business facts, no URL, no brain_ids. A draft with nothing to draft from is fiction. |
| url_host_not_public | 400 | The URL points at localhost, a private range, or link-local metadata. Always fatal. |
| source_text_too_short | 400 | x402 only. Below 200 characters there is nothing to draft from. |
| master_key_required | 403 | brain_ids was supplied but the caller has no forwardable ounie.com credential. |
| insufficient_credits | 402 | Refused before any work runs, with required_credits, balance_credits and a top-up link. |
| thin_material | 422 | Nothing survived validation. Nothing was billed. On x402, nothing settled. |
| partial_result | 402 | x402 only. Fewer documents passed than were quoted, so nothing settles — see below. |
Other REST endpoints
GET /api/policies— your runs. Free.GET /api/policies/<id>— one run with every document. Free.GET /api/documents/<id>— one document. Free.PATCH /api/documents/<id>— revoke or restore its page. Free.GET /api/brains— your Ounie brains, for the picker. Master key only.
MCP
Streamable HTTP and legacy SSE from one endpoint: https://policies.ounie.com/api/mcp. Hosts that cannot set an Authorization header — the Ounie AI Team's manual HTTP MCP entry, for one — pass the key in the URL: ?api_key=pol_live_….
| Tool | Cost | What it does |
|---|---|---|
| draft_policies | 15 / 6 credits | Draft 1–3 documents from the business's facts, a public URL, or your Ounie brains. |
| list_policy_sets | free | Your drafting runs, newest first. |
| get_policy_set | free | One run with the full text, assumptions, gaps, citations and hosted URLs. |
| set_document_visibility | free | Revoke or restore a document's public page. |
| get_credit_balance | free | Spendable Ounie credits + monthly included. |
| get_pricing | public | Per-action pricing. No auth required. |
| whoami | free | The authenticated key's owner + key id. |
x402 — no account, no key
POST /api/x402/policies takes USDC on Base at 1.2× the credit price — 180000 atomic units ($0.18) for three documents, 72000 ($0.072) for one. The route quotes from your payload, so it never charges you for a set you did not ask for.
A keyless caller has no Ounie account and therefore no brains, so supply the business's own material inline as source_text (at least 200 characters) or a public url we read. Citations point at your material rather than at a brain page you do not have.
# 1. Ask without payment — you get the 402 and the price.
curl -X POST https://policies.ounie.com/api/x402/policies \
-H "Content-Type: application/json" \
-d '{"documents":["privacy"],"contact_email":"hello@acme.dev",
"business_name":"Acme","url":"https://acme.dev"}'
# 2. Sign the EIP-3009 authorization, retry with X-Payment.
# Network is the bare "base" (the name selects the protocol version),
# and "extra" carries the USDC EIP-712 domain you must sign against.Two gates. Bad input that is always fatal — an unknown document kind, a missing contact email, a non-public URL, source_text under the floor — returns 4xx with no price in the body. Quoting a price for work that can never succeed would make you sign an authorization for a guaranteed failure. Anything only knowable after the work is caught after your payment is verified and before it is settled, so nothing moves on chain.
Partial sets do not settle. If you asked for three and only two passed validation, this rail refuses rather than settling: an EIP-3009 authorization settles the exact signed amount or nothing, so charging for three would be overcharging with no refund available, and handing over two for free would be an exploit. You get 402 partial_result, keep your money, and can retry or ask for fewer documents.
Using your Ounie brains
Pass brain_ids and the draft is grounded in what your brains already hold about the business — the retrieval call is free, not metered as a question — and each document cites the brain page a clause came from. A citation to a page that was not actually retrieved is dropped rather than shown, because a citation that does not resolve is worse than none.
This needs the ounie_live_ master key or a signed-in dashboard session. A pol_live_ key gets a clear 403 master_key_required rather than a confusing upstream 401.