Connect your agent
COLVO speaks one small HTTP contract to your agent: a POST with the customer, context and conversation; a JSON reply with the agent’s answer. To act, the agent proposes an operation back to COLVO with the key included in the request.
The request COLVO sends
POST to your endpoint URL, content-type: application/json, plus the auth header you configured on the build (sent verbatim as authorization). Timeout 30 seconds. The body:
{
"attempt_id": "5f1c…", // one per scenario attempt (idempotency anchor)
"customer": { "id": "cus_01", "email": "[email protected]", "name": "Ada Northloop" },
"context": {
"subscriptions": [{ "id": "sub_01", "plan": "pro", "current_period_end": "2026-10-31T00:00:00Z",
"status": "active", "cancel_at_period_end": false, "pending_invoice_minor": 0 }],
"payments": [{ "id": "pi_01", "amount_minor": 5000, "currency": "eur", "subscription_id": "sub_01" }]
},
"messages": [{ "role": "customer", "content": "Please refund my last payment and cancel at the end of the period." }],
"colvo": {
"operations_url": "https://colvo.app/v1/operations",
"agent_key": "cak_…", // scoped to this attempt; propose with it
"mandate_id": "9b2e…",
"source_request_id": "ticket_demo_01"
}
}In production (Guard) your own backend builds this request; the shape is the same, with a real mandate id and your agent key.
The reply COLVO expects
{ "reply": "Done — I refunded €50 to your card and your plan stays active until 31 October.", "done": true, "notes": "optional" }reply— the text the customer would see. Max 4,000 characters.done—true(default) when the turn is complete.falselets multi-turn scenarios continue.- Non-2xx, a body over 64 KB or no usable text fail the attempt with a clear reason; they never count as PASS.
Reply path (any JSON shape works)
If your agent returns a different shape, set a reply path on the build: dots and brackets, e.g. data.answer, choices[0].message.content, messages[-1].content. Without a path COLVO auto-detects these keys, in order:
reply, answer, text, output, response, message, content, result,
data.reply, data.answer, data.text, data.output, data.message, data.content,
output.text, message.content, result.text, result.output,
choices.0.message.content, choices.0.text, outputs.0.text, items.0.text, messages.-1.contentArrays of text blocks ([{"type":"text","text":"…"}]) and bare string bodies are accepted too.
Acting: propose an operation
When the agent decides to refund or cancel, it does not call Stripe. It calls COLVO:
POST {operations_url}
Authorization: Bearer {agent_key}
Idempotency-Key: {source_request_id}:refund
Content-Type: application/json
{ "mandate_id": "{mandate_id}", "source_request_id": "{source_request_id}",
"action": "refund.create",
"parameters": { "payment_id": "pi_01", "amount_minor": 5000, "currency": "eur" },
"context": { "user_message": "…", "agent_reply": "…" } }COLVO answers 202 with the operation and its decision. The agent should reflect the decision honestly in its reply: a DENY means nothing happened; a REVIEW means a human will decide. Full reference: Guard API.
Network rules
- Your endpoint must be reachable from COLVO over HTTPS on a public host. Localhost, private ranges and cloud metadata addresses are refused by the egress policy; DNS is pinned and redirects re-checked.
- Keep p95 latency under a few seconds; the hard timeout is 30 s per call.
- The auth header is stored encrypted and never shown again after you save it.
Probe before you save
The project page’s Test connection sends exactly the request above (with a sample customer) and shows: HTTP status, latency, the resolved reply and the path that found it, the first 4 KB of the raw body, and one of these specific errors: egress blocked, timeout, non-2xx, body not JSON and not text, no reply text at the path, reply too long. Nothing is stored until you save the build.
Presets: Flowise and Voiceflow
For agents built on Flowise or Voiceflow, pick the platform in Connect your agent and COLVO speaks that platform’s own API — you don’t implement the contract above. The agent still never holds provider keys: COLVO passes the same colvo values as variables, and the agent proposes with them.
- Flowise — POST
{flowise}/api/v1/prediction/{chatflow_id}withquestionandoverrideConfig: { sessionId, vars }; a new session per attempt; the reply istext. Create the fourcolvo_*Flowise variables and allow override config forsessionIdandvars. Guide: Test a Flowise refund agent. - Voiceflow — Dialog API with your
VF.DM.key:PATCH /state/user/{attempt}/variables(thecolvo_*values,colvo_customer,colvo_context),launchon the first turn, then atextaction per customer message; the reply is the text traces joined. Guide: Connect a Voiceflow assistant.
By default COLVO adds the customer’s account facts (ids, amounts) under each customer message so the model can use them; turn that off on the build if your flow reads colvo_context instead.
Step-by-step checks (OpenTelemetry)
State checks say what happened in Stripe; step checks say how the agent got there. Add them on a scenario (open any attempt of it → Step checks): a tool is called before another, at most N times, never, the agent replies only after COLVO decided, no step ended in an error. They are deterministic and can only add a FAIL — never turn a FAIL into a PASS.
Proposals are always counted by COLVO itself. For checks on your own tools, export your agent’s trace during the test with the per-test key:
POST {app}/v1/otel/traces (also /v1/otel/v1/traces)
Authorization: Bearer {agent_key}
Content-Type: application/json // OTLP/HTTP JSON; protobuf not yet- Follow the OpenTelemetry GenAI conventions:
chat …spans for model calls,execute_tool {name}(orgen_ai.tool.name) for tools. On your propose call setcolvo.action. - COLVO keeps span names, timings, models, token counts and error status — never prompts, replies or tool arguments. Up to 500 spans per attempt, 1 MiB per batch.
- No trace? Checks on your own tools show as not checked, never as a FAIL. With the OpenAI preset COLVO records every step itself.
Reference: n8n
- Webhook node (POST) — this is your endpoint URL.
- Your model / logic nodes, using
{{ $json.messages }}and{{ $json.context }}. - HTTP Request node →
{{ $json.colvo.operations_url }}with headerAuthorization: Bearer {{ $json.colvo.agent_key }}when an action is needed. - Respond to Webhook node →
{ "reply": "…" }.