Confirmed actions
How FloPay MCP runs mutating tools, from a preview that changes nothing to explicit confirmation, safe retries, and what to do when an outcome is unclear.
Confirmed actions
A mutating tool never changes anything on its first call. FloPay checks the request, returns a preview of exactly what would happen, and waits. The action runs only when your client sends back the operator's explicit acceptance, and FloPay checks everything again before it writes.
Each tool's class, risk, and retry policy are in the Tool reference. Actions FloPay has not made available to agents, including every high-risk action, are listed on Unavailable operations.
Which clients see mutating tools
A mutating tool appears in tools/list and search results, and can be called, only when all of these hold for the request:
- The access token carries the
mcp:writescope. - The operator's role on the merchant is
owneroradmin. - The request declares form elicitation in
io.modelcontextprotocol/clientCapabilities:"elicitation": { "form": {} }, or anelicitationobject that lists no mode. A client that declares only URL elicitation stays read-only.
Otherwise the tool is hidden, and calling it returns JSON-RPC error -32602 with Unknown or unavailable tool. Declare the same capabilities on the preview call and the confirmation call.
Preview
Call the tool with its arguments, exactly as you call a read-only tool, using the headers from Connect any MCP client with Mcp-Name: ProductBrand_create:
{
"jsonrpc": "2.0",
"id": 10,
"method": "tools/call",
"params": {
"name": "ProductBrand_create",
"arguments": { "code": "summer-range", "name": "Summer range" },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": { "elicitation": { "form": {} } }
}
}
}FloPay runs the same validation and permission checks the action itself would run, then stops before writing, so the preview does not change anything. FloPay MCP answers with an input_required result that holds a confirmation form:
{
"jsonrpc": "2.0",
"id": 10,
"result": {
"inputRequests": {
"flopay/confirmation": {
"method": "elicitation/create",
"params": {
"mode": "form",
"message": "Review the authoritative FloPay preview and explicitly accept to execute: {\"operationId\":\"ProductBrand_create\",\"inputHash\":\"<input hash>\",\"actionPrincipal\":{\"actorId\":\"3c6f1e2a-8b4d-4f7e-9a1c-5d2e8b7f4a3c\",\"authMethod\":\"oauth-bearer\",\"clientApplicationId\":\"7d0c6c2e-4b1f-4f7a-9c1e-2f8b7a1d0e5c\",\"merchantId\":\"0f8e4c1a-6d2b-4e7f-8a9c-3b5d7e1f2a4c\",\"role\":\"admin\",\"scopes\":[\"mcp:read\",\"mcp:write\"]},\"authorization\":{\"role\":\"admin\",\"scopes\":[\"mcp:read\",\"mcp:write\"],\"permissions\":{\"roles\":[\"owner\",\"admin\"],\"scopes\":[\"mcp:write\"]},\"policyHash\":\"<policy hash>\"},\"targets\":[{\"type\":\"product-brand-code\",\"id\":\"summer-range\",\"version\":\"absent\"}],\"effects\":[{\"type\":\"create-product-brand\",\"targetId\":\"summer-range\",\"details\":{\"before\":null,\"after\":{\"code\":\"summer-range\",\"name\":\"Summer range\",\"description\":null}}}],\"warnings\":[\"Product brand id and timestamps are assigned only when the confirmed action executes.\"],\"normalizedProposedValues\":{\"code\":\"summer-range\",\"name\":\"Summer range\",\"description\":null},\"validationOutcome\":{\"valid\":true,\"checks\":[\"dto\",\"product-brand-code-available\"]},\"expectedResult\":{\"responseType\":\"ProductBrandDto\",\"projected\":{\"code\":\"summer-range\",\"name\":\"Summer range\",\"description\":null},\"serverAssignedFields\":[\"id\",\"createdAt\",\"updatedAt\"]},\"expiresAt\":\"2026-09-14T15:05:00.000Z\"}",
"requestedSchema": {
"type": "object",
"properties": {
"idempotencyKey": {
"type": "string",
"title": "Idempotency key",
"description": "A client-generated retry identity for this action."
}
},
"required": ["idempotencyKey"]
}
}
}
},
"requestState": "<confirmation state>",
"_meta": {
"flopay/contractRevision": "<contract revision>",
"flopay/httpStatus": 201,
"flopay/confirmationPhase": "preview",
"io.modelcontextprotocol/serverInfo": { "name": "flopay-mcp", "version": "<server version>" }
},
"resultType": "input_required"
}
}The form message carries the preview from the FloPay API. Show the operator what it says before asking for a decision:
| Preview field | What it tells the operator |
|---|---|
effects | Each change, with the values before and after it. |
targets | The records the action touches, with the version the preview saw. |
warnings | Anything the operator should know first. |
validationOutcome | Whether the input passed validation, and which checks ran. |
expectedResult | The kind of record the action returns, and which fields FloPay assigns when it runs. |
expiresAt | When the confirmation stops working. |
actionPrincipal, authorization | Who the action runs as, and the role and scopes that allow it. |
requestState is an opaque confirmation token. Keep it as it is and never show it to the model or the operator. If FloPay rejects the preview, because the arguments are invalid or the operator can no longer run the action, FloPay MCP answers JSON-RPC error -32603 with Backend action preview is invalid. Check the arguments against the tool's entry in the Tool reference before trying again.
Confirm or decline
To run the action, call the same tool again with the same name and arguments, echo requestState, and send the operator's decision in inputResponses:
{
"jsonrpc": "2.0",
"id": 11,
"method": "tools/call",
"params": {
"name": "ProductBrand_create",
"arguments": { "code": "summer-range", "name": "Summer range" },
"requestState": "<confirmation state>",
"inputResponses": {
"flopay/confirmation": {
"action": "accept",
"content": { "idempotencyKey": "5b7e2c90-3d4f-4a8b-9c1d-6e2f7a8b9c0d" }
}
},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": { "elicitation": { "form": {} } }
}
}
}The idempotency key is a value your client generates once for this intended action, such as a UUID. The result is the FloPay API response for the action, in the shape the tool's entry in the reference documents.
If the operator declines or dismisses the form, send decline or cancel as the action instead. FloPay MCP answers FloPay action was not executed. and does not contact the FloPay API.
A confirmation:
- expires five minutes after the preview,
- works for one execution only,
- is bound to the operator, the merchant, the client application, the tool, the exact arguments, the operator's role and scopes, and the version of every record the preview targeted.
If anything the confirmation is bound to changes after the preview, for example someone else edits the same record, FloPay refuses to run the action. Call the tool again for a fresh preview.
An action that reaches the FloPay API but does not complete returns a tool result with isError: true, the status in flopay/httpStatus, and the API response in structuredContent:
| Status | Message | What to do |
|---|---|---|
409 | MCP action confirmation is expired, changed, forged, or already used. | Call the tool again for a new preview, and ask the operator to confirm it. |
409 | Idempotency-Key was already used for a different MCP action binding. | The key belongs to another action. Generate a new key for this one. |
409 | The MCP action outcome is not yet authoritative. | An earlier attempt with this key has not finished or did not complete. Read the target record before doing anything else. |
503 | The MCP action outcome is ambiguous. | FloPay cannot tell whether the change was written. Read the target record to find out. |
503 | The MCP action completed without a durable authoritative result record. | The action ran, but FloPay could not keep its result for retries. Read the target record instead of retrying. |
403 | The live action principal does not satisfy the reviewed MCP action policy. | The operator's role or the token's scopes changed since the preview. Reconnect with the access the action needs. |
The responses about unclear outcomes also carry a reconciliation field with the same instruction: do not retry automatically, and check the record first.
Retry an action
Retries are safe only when you know what happened to the first attempt. Use the same idempotency key, and only for the same action:
- The call timed out or the connection dropped. Send the same confirmation call again, with the same
requestStateand the same idempotency key. If the first attempt completed, FloPay returns its original result instead of running the action twice. FloPay keeps that result for at least 24 hours. - The result was an error. Read the target record with a read-only tool. If the change did not happen, start again with a new preview and a new idempotency key.
- The result was
409or503with areconciliationfield. Do not retry automatically. Check the target record, and let the operator decide what to do next.
An idempotency key can be up to 200 characters. Never reuse a key for a different action: FloPay refuses it with 409.
Some tools declare the manual-reconciliation retry policy, because their outcome cannot be safely repeated once FloPay starts writing. For these tools, a failure after writing starts answers 503 with The MCP action outcome is ambiguous., and you must read the record before deciding anything:
Session_createSetupreturns one-time card capture credentials. They are issued once: a repeated call returns only a reference to the session, never the credentials again.User_updateUserchanges a customer's details. Read the customer record to see whether the change was applied.
Tools with the same-idempotency-key policy can be retried with the same idempotency key as described above. Do not retry automatically when FloPay says a result is not yet authoritative.
Other tools
Arguments, results, errors, and access, confirmation, and retry policy for each Other tool in FloPay MCP, from the reviewed API contract.
Verify results
Check what FloPay MCP returned using contract revisions, API status codes, correlation IDs, aggregate context with source record links, and knowledge sources.