Call a capability
The gateway is the only endpoint an integrator needs to know about to spend money through Lipafy. Every capability — whether it’s a food-order API, an airtime top-up service, or a private internal endpoint — is invoked the same way:
That’s the entire contract. Lipafy authenticates you, holds the price from your wallet, forwards your request to the upstream provider, and returns the upstream’s response augmented with payment headers.
Five-second example
You — the integrator — never see the upstream’s API key. The provider — Local Lunch — never sees your wallet. Lipafy is the trust boundary.
What happens behind the scenes
Authenticate
The gateway accepts three credential types, in priority order:
- API key —
Authorization: Bearer lip_live_...(most agent-to-server use) - OAuth 2.1 access token —
Authorization: Bearer <jwt>(third-party connected apps) - Dashboard session cookie —
Cookie: lipafy_session=...(only when calling from the dashboard frontend)
See Authentication for which to use when.
Resolve capability + check status
Lipafy looks up the slug. If it’s draft or disabled, the request fails with 422 capability_disabled. If the slug doesn’t exist at all → 404 capability_not_found.
Run spending controls
The grant attached to your credential (API key grants, OAuth grants, or your account_default grant for direct sign-in) carries spending controls:
- Daily cap
- Per-transaction cap
- Allow / block lists on slugs
- Escalation threshold (requires human approval above N KES)
Any cap exceeded → 402 with a code: daily_limit_exceeded, transaction_limit_exceeded, capability_blocked, capability_not_allowed. See Grants and controls.
Maybe pause for human approval
If the call is above your escalation threshold, the gateway responds with:
Your client should pause, present the pending approval to the human (via your UI, the Lipafy dashboard at /approvals, or an SSE listener on /v1/notifications), and retry the same request once the approval is granted. The approval_id is consumed atomically on the next call — exactly-once. See Approvals.
Hold price from wallet
Lipafy debits the price (e.g. 500 KES) from your wallet’s available balance and writes it to held_balance. If available < price → 402 insufficient_balance. The hold is reversible right up until step 7.
Forward to upstream
Lipafy issues the upstream call:
- Method: same as yours.
- Path: everything after
/gateway/<slug>/is appended to the capability’sendpoint_url. - Headers: yours pass through, except
Authorization,X-Payment-Authorization,Cookie, and hop-by-hop headers are stripped. The capability’s configured auth (bearer/api_key/basic/none) is injected. - Body: byte-for-byte.
Settle the hold
- Upstream returned 2xx → confirm the hold (your wallet’s
held_balance→ debit, provider’s wallet credit minus revenue share), write a signed receipt tocapability_requests. - Upstream returned 4xx or 5xx → release the hold (held → available, no charge), no receipt.
Either way, the upstream’s response — status code, headers, body — is what you receive, augmented with payment headers.
What you get back
X-Payment-Reference— thecapability_requestrow id. Stable across retries; your idempotency anchor.X-Payment-Amount/X-Payment-Currency— what was actually charged. May differ from the capability’s listed price if pricing dimensions change in future.X-Payment-Receipt— fetchable signed receipt for audit. See Receipts.
Error responses, at a glance
Full catalog: Errors.
Integration patterns
Agent-as-a-script (your own agent, your own wallet)
Mint an API key in Settings → API keys. Use it as the Bearer token. Set spending controls on the key’s grant so an unattended script can’t drain the wallet.
Third-party app calling on behalf of a user
Use the OAuth 2.1 + PKCE flow (Connected apps via OAuth). You get an access token scoped to one user. The same Authorization: Bearer <token> header at the gateway routes the spend through that user’s wallet and grant.
Agent in Claude Desktop, Cursor, or other MCP host
Use the MCP server (Agents → MCP). The host calls the lipafy.execute_capability tool; you never write the HTTP yourself. When a call needs sign-off, the tool response asks the agent to use lipafy.request_approval, and the user decides from the dashboard.
Mandate-driven autonomous purchase
For “buy up to N if conditions hold” flows, the agent signs an IntentMandate first (Mandates) and then calls /v1/intent-mandates/:id/fulfill instead of the gateway directly. Lipafy re-validates against the signed terms and forwards through the gateway internally.
What providers don’t have to do
There’s no SDK for the provider side. Their upstream is just an HTTPS endpoint. Lipafy:
- Accepts whatever path / method / body the agent sends and forwards it.
- Injects the auth (bearer / api_key / basic / none) the provider configured at registration time.
- Treats 2xx as success (charge confirmed) and 4xx/5xx as failure (hold released).
If you’re a provider deciding whether to onboard, the only question is: is your endpoint already an HTTPS API that returns 2xx on success? If yes, you’re a 5-minute registration away from being callable. See Monetize an API.
Next
- Spending controls — set the limits an agent can’t cross
- Approvals — human-in-the-loop above a threshold
- Receipts — verify what was charged, after the fact
- Webhooks — get notified asynchronously instead of polling
