Skip to main content
This page is about an agent you run in production. To connect the coding agent that builds and improves your decisions, see Connect your agent. An agent doesn’t load a decision or carry its questions in its prompt. At the step where the decision applies, your code makes one HTTP call, 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 as check_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 read state.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

  • state is 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’s reads lists its paths.
  • attribute_values carries facts your code loaded from your records, when the decision declares attributes it doesn’t derive with from. Facts the code already knows shouldn’t be left for a model to judge (Attributes).
  • item_id is 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.
Only the customer’s words should come from the conversation. Load anything the decision treats as a fact, such as an order or a refund history, from your own records and never from the model’s tool arguments. Otherwise a customer can steer the model into passing an order that earns a 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’s version 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.