curl 7.76 or later, jq, uuidgen (built into macOS; uuid-runtime on Debian and Ubuntu) and, for the TypeScript part, Bun or Node 20+.
This isn’t a public API. Access is by invitation: join the waitlist at app.moonbounce.ai and sign up once you’re granted access. The API is in early access and unstable: names and shapes change often, breaking changes are expected, and there are no compatibility guarantees.
1. Your key and the base URL
Once you have access, a team admin creates API keys on the team’s Keys page (app.moonbounce.ai/team/keys). For this guide, make a Test key with all three scopes (decisions:evaluate, decisions:feedback and decisions:develop): a test key keeps these calls out of your production data. The full value is shown once. Keep it out of files and your shell history:
$MOONBOUNCE_API_URL/openapi.json; every operation there takes Authorization: Bearer <key> except GET /health and POST /v1/specs/hash.
2. Describe the decision
A decision is a small JSON spec of namednodes: features are indicators an engine detects in your content, an aggregator (combine) turns their answers into one score, calibrate maps that score onto the probability that target is true, and output names the node whose value is the decision’s score. This one decides whether a support message needs a person:
3. Evaluate an item
POST /v1/evaluate scores one item. Send the spec inline (it is registered the first time the API sees it), a decision_id of your choosing, the content as subject parts, your own item_id, and the cut points that turn the score into an outcome. Send an Idempotency-Key on every POST and keep it: if the call times out, send it again with the same key, and you get the first answer instead of a second evaluation:
"weights": "default" uses the starting weights every spec gets before any feedback: a combine is the plain mean of its answers, and calibrate passes the score through. The response says so with version.weights_fitted: false and calibrated: false. A json part ({"type": "json", "name": "order", "data": {...}}) can sit next to the text in subject. Read any evaluation back by its id:
4. Send feedback
When your app or a person learns the right answer, send it back. A claim says what was true:outcome (one of your cut points’ outcomes), target (whether the target statement held), verdict (approve or reject of our answer), or feature (the right value of one feature). Feedback is about one evaluation, or about the latest evaluation of an item in your key’s environment ({"decision_id", "item_id"}; seed examples don’t count). A verdict claim needs evaluation_id:
source says who judged. Use owner when you, the decision’s owner, or someone you trust decided the case: owner feedback is what previews and suggestions learn from. app_native (your app’s own approve or reject feedback) and app_resolution (your process settled the case) are stored too, but count only as weak feedback: they never decide a case’s right answer, though their reasons can reach the suggestion writer. The API answers 201 with the feedback id.
5. The same in TypeScript
No SDK is needed: the API is plain JSON over HTTPS.quickstart.mts in the folder that holds decision.json and run bun quickstart.mts or npx tsx quickstart.mts (the .mts extension makes it a module, which top-level await needs).
6. Connect your coding agent (MCP)
The same operations are MCP tools at$MOONBOUNCE_API_URL/mcp, with the same key. In Claude Code, install the Moonbounce plugin, or sign in without it; Connect your agent covers both options. Neither sign-in option needs a key. Signed-in admins and members can run real and dry-run evaluations and read them in Production, attributed to them. Test needs an env:test key; unattended agents and backends use keys. For Claude Code, put this in your repository’s .mcp.json and start claude from a shell where both variables are exported:
Errors, retries and limits
Every error under/v1 has one shape: {"error": {"type", "message", "reason"?, "param"?, "details"?}}, and every response carries a Request-Id header. When you report a problem, quote it and email support@moonbounce.ai. param names the field at fault when there is one; reason is set where you can act on it, and the OpenAPI document lists them (ErrorReason).
- Retries. Retry 429
RATE_LIMITEDand 503, and a timeout, with the sameIdempotency-Key, afterRetry-Afterwhen it is set. If the first attempt was stored, its result comes back withIdempotent-Replayed: trueand nothing runs twice; if it failed before it was stored, the retry runs it. A key is remembered for 24 hours and is scoped to your organization and the key’s environment, across every operation, so use a fresh one per request. - Time. An evaluation answers within 10 seconds or fails, with a 503 you can retry with the same key, or a 502 when an engine answer the score depends on is missing;
Prefer: wait=<whole seconds>shortens that budget (it never lengthens it). The response is sent only after the evaluation is stored. - Limits. Request bodies up to 1 MB; up to 64
subjectparts and 64contextparts per item; up to 64 nodes per spec. Rate limits apply per key and per client IP, counted per Cloudflare location (currently 600 requests a minute per key and 300 per IP); a 429 says how long to wait inRetry-After. Evaluate, seeds, previews and suggestions also count against your team’s rate and daily work budget (Rate limits). - One decision per call. Chain decisions in your own code.
Next
- Build the mental model behind these calls: How Moonbounce works.
- Seed a decision with a dozen labeled examples so previews have cases to replay:
POST /v1/decisions/{id}/seeds. - Pin the structure:
POST /v1/decisions/{id}/structuresonce, then evaluate with"structure": "<hash>"instead of the inline spec. - Ask for a suggested improvement and preview it against your past cases:
POST /v1/decisions/{id}/suggestionsandPOST /v1/decisions/{id}/previews. Both see only your key’s environment, so with this guide’s test key they learn from the test cases and seeds you sent, and their suggestions are never accepted. Use a production key to improve a live decision.