APP v0

Connect your AI agent in 10 minutes

The Agent Payment Protocol (APP v0) lets an AI agent act for its user inside the Okidooki ecosystem: find and reserve nightlife (Okidooki Night), book licensed security and raise SOS (NowTrust), design and order print-on-demand merch (MakeItFun). One key per network, HMAC-signed requests, the user's consent through a one-time account link, payment stays in the seller's app.

Open the developer portal

Registry: /.well-known/app-v0.json · Full API reference · OpenAPI: openapi.yaml · SDK: JavaScript, Python · conformance test

1. Get keys

  1. Sign in to the portal (any Okidooki account), open Agents (APP v0), register an integration: a name, a slug (this becomes your network id, e.g. acme) and the networks you want.
  2. You get a TEST key per network at once. Store them in your secret manager; they are shown once.
  3. Click Sandbox user on each network: a test account that is already linked to { network: "acme", uid: "sandbox-acme-…" }. Test keys only act on sandbox users.
  4. When your integration works, Request LIVE access. After review you generate LIVE keys in the portal. Live keys act on real users who linked their account to your network (see Linking).

2. Call a network

Every request is POST {gateway}/a2a/v0/{route} with a JSON body and three headers:

Authorization: Bearer <key>
X-Timestamp: <unix seconds, within 5 minutes>
X-Signature: hex( HMAC-SHA256( key, timestamp + "." + rawBody ) )
Idempotency-Key: <optional, for order>

The raw body must be byte-identical to what you signed. The SDKs do this for you:

// JavaScript (Node 18+)
import { AppV0Client } from "./app-v0.mjs";
const mif = new AppV0Client({ gateway: "https://europe-west1-make-it-fun-app.cloudfunctions.net/a2aGateway", key: process.env.MAKEITFUN_KEY });
const card = await mif.card();                                   // capabilities, routes, linking rules
const { sellers } = await mif.discover({ productType: "tshirt" });
const product = await mif.discover({ sellerId: sellers[0].sellerId });   // variants with prices, placements
const job = await mif.preview({ userRef, sellerId: sellers[0].sellerId, variantId: product.variants[0].variantId,
  design: { kind: "text", text: "ACME CREW", font: "bebas", color: "#141414" } });
const done = await mif.waitPreview(job.jobId);                   // mockupURL, designId, printSpec
const quote = await mif.quote({ userRef, designId: done.designId, variantId: product.variants[0].variantId, quantity: 3 });
const order = await mif.order({ userRef, quoteId: quote.quoteId }, "acme-order-42");   // confirmUrl: the user pays in MakeItFun
const status = await mif.status({ orderId: order.orderId });    // needs_user_confirmation | paid | in_production | shipped ...
# Python 3.9+
from app_v0 import AppV0Client
nt = AppV0Client("https://nowtrust.app", os.environ["NOWTRUST_KEY"])
guards = nt.discover({"near": {"lat": 47.5596, "lng": 7.5886}, "kind": "guard"})
quote = nt.quote({"userRef": user_ref, "sellerId": guards["sellers"][0]["sellerId"], "durationHours": 2, "when": "2026-10-12T20:00:00Z", "location": {"lat": 47.5596, "lng": 7.5886, "address": "Barfüsserplatz, Basel"}, "securityType": "event_security"})

3. Linking (user consent)

Universal "Connect with Okidooki" (recommended): one call, one page, every network. Your agent calls connect/start on the Okidooki gateway with your Okidooki key; the user opens the returned url, signs in with their Okidooki account, sets one mandate for your agent (max per order, max per month, which capabilities, how long, optional NowTrust auto-pay) and approves the networks. Okidooki links the account and tells NowTrust and MakeItFun to link the account it already knows for that user; a network that cannot match the account answers needs_code with a code for its app. Poll connect/status, then link/status per network (it includes the mandate). Users see, change and revoke everything in the web app at okidooki.app/webapp/connect (Profile, Connected agents).

const oki = new AppV0Client({ gateway: "https://europe-west1-okidooki-nightlife.cloudfunctions.net/a2aGateway", key: process.env.OKIDOOKI_KEY });
const c = await oki.connectStart({ userRef, networks: ["okidooki", "nowtrust", "makeitfun"], displayName: "Anna", mandate: { perOrderMax: 10000, monthlyMax: 50000 } });
// show c.url to the user ... then:
const done = await oki.waitConnect(c.connectId);   // status approved; done.results.makeitfun.status: linked | needs_code

Orders and SOS outside the mandate fail with 403 mandate_scope and a message you can show the user. Per-network code flows still work on their own:

NetworkFlow
MakeItFun, NowTrustlink/start { userRef } returns an 8-character code and a deep link; the user enters the code in the app (Profile / Settings, Connected apps). link/status tells you when it is done and what the account can do (shipping address, card on file, ID verified, spending mandate).
OkidookiYour app shows the Okidooki user id and you confirm the pair with link/confirm { okidookiUid, externalUid } (signed by your key); see the card for details.

userRef is always { network: "<your slug>", uid: "<your user id>" }; a key can never act on another network's users.

4. Routes

All networks
discover · quote · order · orders · status · cancel · link/*
Quotes are firm for 30 minutes; order is idempotent with Idempotency-Key; the user confirms/pays in the seller app, your agent polls status.
MakeItFun
preview (AI prompt, image URL, exact text, or a saved design; placements, colours, team orders) · preview/status · designs (saved designs, free to reuse) · discover { suggest: { productType, occasion, vibe } } (Design Curator: curated looks with preview pictures, then preview with design.kind: "style") · sandbox/pay (test keys) · discover { occasions: true } for templates
NowTrust
sos · sos/status · sos/cancel · spending mandates in link/status
Okidooki Night
venues, tables, events and tickets through discover (cityName / near) and quote

Full request/response schemas: openapi.yaml. Errors are { error: { code, message, retryable } } with HTTP 400/401/403/404/409/429/5xx; retry only when retryable or 429/5xx.

4b. No custom client? Use the standard facades

Every network also speaks two industry standards, backed by the same dispatcher as the REST routes, with the same key (Authorization: Bearer; the HMAC headers are optional here):

Google A2A
Agent card at /.well-known/agent.json (skills = routes). JSON-RPC at the card's url (/a2a; NowTrust: /a2a/v0/rpc): message/send with a data part {"route":"discover","body":{...}} returns a completed Task whose artifact carries the result; tasks/get re-reads it.
MCP (Streamable HTTP)
/mcp: initialize, tools/list, tools/call (one tool per route, JSON Schema inputs, structured results). Add it to Claude, an OpenAI Agents SDK or any MCP client as a remote server with your key as the bearer token.
# MCP: list the MakeItFun tools
curl -s https://make-it-fun-app.web.app/mcp -H "Authorization: Bearer $MAKEITFUN_KEY" -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# Google A2A: discover through message/send
curl -s https://nowtrust.app/a2a/v0/rpc -H "Authorization: Bearer $NOWTRUST_KEY" -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"message/send","params":{"message":{"role":"user","messageId":"m1","parts":[{"kind":"data","data":{"route":"discover","body":{"near":{"lat":47.5596,"lng":7.5886},"kind":"guard"}}}]}}}'

Endpoints per network are listed in the registry (a2a and mcp fields). Okidooki: okidooki.app, NowTrust: nowtrust.app, MakeItFun: make-it-fun-app.web.app.

5. Prove it works: conformance test

node conformance.mjs --gateway https://europe-west1-make-it-fun-app.cloudfunctions.net/a2aGateway \
  --key $MAKEITFUN_TEST_KEY --userRef '{"network":"acme","uid":"sandbox-acme-1"}'
# card, signature + timestamp enforcement, network isolation, discover, linked sandbox user,
# MakeItFun: text preview -> quote -> order -> sandbox payment -> status paid

Run it against each network with your TEST keys and sandbox users; a green run is what we look at when you request LIVE access.

6b. Webhooks instead of polling

In the portal (Agents, Webhooks) set a https URL and pick events: order.status (every network: paid, confirmed, in_production, shipped, cancelled, refunded, expired, failed), sos.status (NowTrust), connect.approved, link.revoked, ping. Each delivery is a POST with JSON { id, type, network, createdAt, data } and the headers X-APPV0-Event, X-APPV0-Delivery, X-APPV0-Timestamp, X-APPV0-Signature. Answer 2xx within 10 s; otherwise we retry after 1, 5, 15, 60 and 240 minutes. The portal shows every delivery with its status.

import { verifyWebhook } from "./app-v0.mjs";
app.post("/okidooki-webhook", express.text({ type: "*/*" }), (req, res) => {
  const event = verifyWebhook(process.env.OKIDOOKI_WEBHOOK_SECRET, req.headers, req.body);   // throws on a bad or stale signature
  if (event.type === "order.status") updateOrder(event.data.orderId, event.data.status);
  res.sendStatus(200);
});
# Python: from app_v0 import verify_webhook; event = verify_webhook(secret, dict(request.headers), request.get_data(as_text=True))

No server yet? Point the webhook at https://europe-west1-okidooki-nightlife.cloudfunctions.net/webhookEcho/<any 16+ char token> and GET the same URL to read what arrived (kept 24 h).

7. Sell to every agent: list your own network

APP v0 is symmetric: your company can also be a seller. Implement the agent card (GET /card with protocol: "okidooki-app", version: "0", network: <your slug>, endpoints) and the routes discover, quote, order, status, cancel, link/* with the same auth scheme (keys you issue, HMAC, 401 on unsigned calls). Then open the portal, Agents, Your seller network, enter the gateway URL: the automated checks (card, version, network id, endpoints, unsigned call rejected) list it in the public registry and in /.well-known/app-v0.json; health is probed hourly. Buyers discover you there and ask you for keys the way they ask us.

Tooling: Postman collection (one folder per network, HMAC pre-request script) · OpenAPI. Every response carries X-Request-Id (send your own to have it echoed) and X-RateLimit-Policy; GET /a2a/v0/health on each gateway.

8. Pricing, limits, status

Test keys and sandbox traffic are free. Live traffic per integration and month: 2,000 calls free, then CHF 0.01 per call, plus CHF 0.50 per paid order placed through your agent (the merchant still pays its own fees to the network). Invoices below CHF 5 are not issued; invoices go out on the 1st for the previous month via Stripe (14 days). The portal shows this month's usage and estimate per integration, and lets you set the rate limit per key (test up to 120/min, live up to 600/min). Live status and 7-day uptime: okidooki.app/app-v0/status.

Crew orders in MakeItFun: quote { team: [{ name, number, size }] } prices every member's own size (same colour) and prints their name/number; discover { suggest: { event: { name, city, date, genre } } } matches looks to an event and fills its slots; looks are ranked per city from what people there pick.

6. Limits and conduct

Operator: OKIDOOKI AG, Basel. Questions: hello@okidooki.app. Also see the Business API for catalog and venue management without agents.