POST /v1/evaluate, with what the agent knows so far. Moonbounce asks the questions on its own model and returns an outcome, and your code does what that outcome means. There is no SDK to install.
Where each part lives
The agent’s own model never sees the questions and can run on anything, because they’re answered on the decision’s model setting.
Two ways to call it
A gate in your code. Your tool checks the decision before it does the thing. The agent chooses when to try, and the decision chooses whether it happens. Use this whenever the outcome allows something that costs money or can’t be undone, because a model can skip a tool but it can’t skip your code. A tool the model can call. You give the model a tool such ascheck_refund_policy, return the outcome, and let it continue. Use this when the outcome only informs the conversation: routing, tone, what to ask next.
A refund agent
The agent has a tool that issues a refund. The handler asks the decision first. This decision’s questions readstate.message and state.order, and its outcomes are grant, escalate and deny.
loadTicket, refund and escalate are your own code. Your agent loop calls issueRefund when the model asks for the tool. The Idempotency-Key protects the evaluation, not the refund: a model can call the tool twice and get grant both times, so make the refund itself idempotent on the ticket id.
What to send
stateis what the questions read. Send the customer’s messages from your stored transcript as one of its values, and only the part the questions need: each question’sreadslists its paths.attribute_valuescarries facts your code loaded from your records, when the decision declares attributes it doesn’t derive withfrom. Facts the code already knows shouldn’t be left for a model to judge (Attributes).item_idis your own id for the case, such as the ticket. Feedback and reviews join up on it, so use the same one for the whole conversation.
grant. The customer’s message can still try to sway the questions that read it, so where money depends on a fact, make it a number, boolean or enum attribute that a rule checks. The example keeps the order in state to stay short.
Keep the response’s id beside the action you took. Feedback on one specific result names it (Feedback).
In the loop
- Plan for a slow answer. A call has a 10-second budget, so give it a timeout a little longer than that, as in the example, and decide what the agent says while it waits. Evaluate content lists which errors to retry.
- Use one call per decision. If the agent needs two decisions, make two calls and chain them in your code.
- Turn the outcome into words. The outcome is a name such as
grant. Your handler decides what the model reads next, as in the example.
Changing the decision
Edit the one file and merge it. When the agent deploys, its next call runs the new structure, and the response’sversion says so. There is no pointer to move. Your git history holds every earlier version, so a revert and a deploy restores an earlier structure.
The model a decision runs on isn’t in the file. It’s a setting kept for each environment, and the first call in an environment without one stores the default model. To run on another model, register the decision and set its model before the agent’s first call, which needs a key with decisions:develop (Models and environments). A rollback doesn’t change it.
Sending the structure on every call means a new version needs no separate registration step. To keep request bodies small, you can register the structure ahead of traffic, which needs a key with decisions:develop, and send moonbounce.structure_hash instead. An unregistered hash is a 404, so register before you deploy (Versions).
Why not the MCP server
The MCP server at/mcp is for the coding agent that builds and improves decisions, and a connection lists every tool, including the ones that change a decision. An agent in production needs only the one call above, over plain HTTP.