Passer votre agent sur Guard
Un parcours pas à pas pour passer de « l’agent appelle Stripe lui-même » à « l’agent propose et COLVO exécute » : d’abord observer, puis approuver à la main, puis laisser passer automatiquement les actions modestes et sûres.
Checklist
- La suite est au vert. La version actuelle de votre agent réussit la suite de remboursements et de résiliations dans COLVO Test, avec trois tentatives par scénario.
- Stripe est connecté avec une clé restreinte. Écriture sur les remboursements et les abonnements, tout le reste en lecture ou sans accès. Seul l’executor de COLVO la voit.
- Votre backend émet un mandat par ticket. Client, paiements, abonnements, actions, limites et une expiration — décidés par votre code, jamais par le modèle.
- Mode observation pendant une semaine. L’agent continue d’agir comme aujourd’hui et signale chaque action ; COLVO enregistre ce qu’il aurait décidé et contrôle le résultat dans Stripe.
- Appliquer avec des approbations. Retirez la clé Stripe de l’agent, faites passer chaque action par
POST /v1/operationset réglez l’approbation automatique à zéro, afin qu’une personne approuve chacune d’elles. - Relever l’approbation automatique. Lorsque les personnes chargées de la revue approuvent chaque jour les mêmes petits remboursements, laissez-les passer automatiquement. Gardez le kill-switch à portée de clic.
1. Connectez Stripe avec une clé restreinte
Dans Stripe, créez une clé restreinte avec Refunds: write, Subscriptions: write, Customers, Charges, PaymentIntents, Invoices: read, et tout le reste sur none. Connectez-la depuis la page du projet (Connections → Connect Stripe) avec le secret de signature des webhooks ; COLVO vérifie les permissions en lecture seule (aucun remboursement de test) et affiche l’URL du webhook à ajouter dans Stripe avec les événements refund.created, refund.updated, charge.refunded, customer.subscription.updated, customer.subscription.deleted. Les clés live doivent être restreintes (rk_live_…) ; une clé sk_live_ à accès complet est refusée. La clé est chiffrée par organisation et n’est jamais renvoyée par l’API, affichée dans les journaux ni transmise à un modèle.
Pas encore de compte Stripe ? POST /v1/projects/{id}/connections/sandbox vous en fournit un simulé pour répéter tout le parcours.
2. Émettez un mandat depuis votre backend
Le mandat est l’autorisation sous laquelle l’agent travaille. Votre backend le crée à l’ouverture d’un ticket ou d’une conversation, à partir de faits auxquels il se fie déjà — le client connecté, ses commandes — et non de ce que dit le modèle. Référence complète des champs : Mandats et connexions.
// backend — when ticket #4711 opens (backend key, never shipped to the agent)
const mandate = await fetch('https://colvo.app/v1/mandates', {
method: 'POST',
headers: { authorization: `Bearer ${process.env.COLVO_BACKEND_KEY}`, 'content-type': 'application/json' },
body: JSON.stringify({
project_id: process.env.COLVO_PROJECT_ID,
source_request_id: ticket.id, // 'ticket_4711'
subject: { customer_id: customer.stripeId, payment_ids: orders.map((o) => o.paymentIntent), subscription_ids: [customer.subscriptionId] },
actions: ['refund.create', 'subscription.cancel'],
limits: { currency: 'eur', max_amount_minor: 5000, auto_allow_up_to_minor: 0, max_operations: 2,
cancel_modes_allowed: ['period_end'], cancel_modes_review: ['immediate'] },
expires_at: new Date(Date.now() + 24 * 3600_000).toISOString(),
}),
}).then((r) => r.json());
// hand only mandate.mandate_id and an agent key (cak_…) to the agent3. Observez d’abord (rien ne change pour les clients)
En mode observation, l’agent conserve son chemin de code actuel. Après avoir agi — ou décidé de ne pas le faire — il signale ce qu’il a fait. COLVO évalue la même politique que celle qu’il appliquerait, enregistre la décision qu’il aurait prise et relit le fournisseur pour vérifier que l’effet correspond à la réponse. Il ne bloque jamais, n’exécute jamais et n’a jamais besoin d’un accès en écriture.
POST /v1/observations — clé agent ou backend.
{ "mandate_id": "…", // optional: without one the would-be decision is DENY (no authority)
"source_request_id": "ticket_4711",
"action": "refund.create",
"parameters": { "payment_id": "pi_01", "amount_minor": 5000, "currency": "eur" },
"provider_id": "re_3Nx…", // the refund / subscription the agent touched, if any
"context": { "user_message": "…", "agent_reply": "Refunded €50 to your card." } }{ "observation_id": "…", "would_decide": "REVIEW",
"reasons": ["amount 5000 above auto-allow 0 (within max 5000)"],
"verify_state": "PENDING", // → VERIFIED | MISMATCH | UNVERIFIABLE
"flags": [] } // e.g. "would_deny", "state_mismatch", "no_mandate"Envoyez "performed": false lorsque l’agent a décidé de ne pas agir — la décision hypothétique est tout de même enregistrée, il n’y a simplement rien à vérifier. Les actions observées sous un mandat sont décomptées de ses limites exactement comme le seraient des opérations : un second remboursement qui dépasserait le total apparaît donc en DENY. Une vérification non concordante ouvre un incident et déclenche vos alertes, même si rien n’a été bloqué.
La page Observations de la console affiche chacune avec sa décision hypothétique et sa vérification, ainsi qu’un résumé hebdomadaire : combien d’actions auraient été autorisées, envoyées en revue ou refusées, et combien de réponses contredisaient l’état du fournisseur. Lorsque cette liste est restée sans surprise pendant une semaine, passez à l’étape suivante.
4. Appliquez : un appel remplace l’appel à Stripe
Retirez la clé Stripe de l’environnement de l’agent. Là où il appelait Stripe, il propose désormais. Commencez avec auto_allow_up_to_minor: 0 pour que chaque action soit en REVIEW et qu’une personne l’approuve sur la page Approvals de la console. COLVO envoie un e-mail à chaque owner et editor lorsqu’une action est en attente (et publie sur les canaux d’alerte abonnés aux approbations), les relance à 75 % de la fenêtre et, si personne ne décide à temps, annule l’opération avant tout envoi et les en informe.
Node / TypeScript
npm install @colvo/sdk — aucune dépendance, Node 18+. Les nouvelles tentatives sont sûres : le SDK envoie une clé d’idempotence déterministe, de sorte qu’une nouvelle tentative renvoie l’opération d’origine au lieu de la dupliquer.
// before
await stripe.refunds.create({ payment_intent: 'pi_01', amount: 5000 });
return reply('Refunded €50!');
// with Guard
import { Colvo, messageFor } from '@colvo/sdk';
const colvo = new Colvo({ apiKey: process.env.COLVO_AGENT_KEY }); // cak_… — can only propose and read
const op = await colvo.operations.propose({
mandate_id, source_request_id: ticket.id, action: 'refund.create',
parameters: { payment_id: 'pi_01', amount_minor: 5000, currency: 'eur' },
context: { user_message: lastUserMessage, agent_reply: draftReply },
});
return reply(messageFor(op)); // honest for ALLOW / REVIEW / HOLD / DENYPython
pip install colvo — bibliothèque standard uniquement, Python 3.9+.
import os
from colvo import Colvo, ColvoError, message_for
colvo = Colvo(os.environ["COLVO_AGENT_KEY"])
def propose_refund(mandate_id, ticket_id, payment_id, amount_minor, user_message):
op = colvo.operations.propose({
"mandate_id": mandate_id, "source_request_id": ticket_id,
"action": "refund.create",
"parameters": {"payment_id": payment_id, "amount_minor": amount_minor, "currency": "eur"},
"context": {"user_message": user_message},
})
return message_for(op) # ColvoError(409, "business_key_conflict") if a different effect was already proposedPas de SDK pour votre stack ? Il s’agit d’un seul appel HTTP : POST /v1/operations avec Authorization: Bearer cak_…, un en-tête Idempotency-Key et le corps JSON ci-dessus — voir l’API Guard.
n8n
- Remplacez le nœud Stripe par un nœud HTTP Request :
POST https://colvo.app/v1/operations, en-têteAuthorization: Bearer {{ $env.COLVO_AGENT_KEY }}, en-têteIdempotency-Key: {{ $json.ticket_id }}:refund, corps JSON comme ci-dessus. - Ajoutez un nœud Switch sur
{{ $json.decision }}avec quatre sorties : ALLOW, REVIEW, HOLD, DENY. - Chaque sortie définit le texte de la réponse (tableau ci-dessous) avant Respond to Webhook. Supprimez l’identifiant Stripe du workflow.
5. Dites la vérité au client
La décision indique à l’agent ce qui s’est réellement passé. Faites la correspondance avec la réponse dans le code, pas dans le prompt, afin que le modèle ne puisse pas promettre un remboursement qui n’a jamais été envoyé.
| Décision | Ce qui s’est passé | Réponse du type |
|---|---|---|
ALLOW | En file d’attente ; exécutée une fois, puis vérifiée dans Stripe | « Votre remboursement de 50 € est en route vers votre carte. » |
REVIEW | En attente qu’une personne approuve ce digest précis | « J’ai demandé à un collègue d’approuver votre remboursement — vous recevrez un e-mail une fois l’opération effectuée. » |
HOLD | Pas sûr pour le moment (pause, plafond atteint, fournisseur illisible) ; rien n’a été envoyé | « J’ai enregistré votre demande ; elle sera traitée sous peu. » |
DENY | Hors du mandat ; rien n’est envoyé, jamais | « Je ne peux pas rembourser ce montant sur cette commande — le maximum que je peux rembourser est de 50 €. » |
// @colvo/sdk and the Python package ship this as messageFor / message_for (with overrides); the logic, to adapt:
function messageFor(op: { decision: string; parameters: { amount_minor?: number } }) {
const eur = ((op.parameters.amount_minor ?? 0) / 100).toFixed(2);
switch (op.decision) {
case 'ALLOW': return `Your €${eur} refund is on its way back to your card.`;
case 'REVIEW': return `I've asked a colleague to approve your €${eur} refund — you'll get an email when it's done.`;
case 'HOLD': return "I've logged your request; it will be processed shortly.";
default: return "I can't do that on this order, but I've passed your request to the team.";
}
}6. Relevez l’approbation automatique
Après une ou deux semaines d’approbations, regardez ce que les personnes chargées de la revue approuvent à chaque fois — en général de petits remboursements sur un paiement récent du client lui-même. Réglez auto_allow_up_to_minor à ce niveau dans les mandats qu’émet votre backend (par ex. 2000). Tout ce qui dépasse continue d’être soumis à une personne ; tout ce qui sort du mandat reste en DENY.
- Kill-switch : POST
/v1/projects/{id}/pause— chaque nouvelle proposition devientHOLD, les exécutions en file d’attente patientent. - Alertes :
MISMATCHetUNVERIFIABLEouvrent un incident et déclenchent vos webhooks d’alerte. - Continuez à tester : la barrière en CI reste active à chaque nouvelle version de l’agent.
Facultatif : l’accès est géré dans votre propre système
De nombreux produits accordent l’accès depuis leur propre base de données, et non depuis Stripe. Pour les résiliations, COLVO peut lire l’accès depuis votre service, afin que « conserve l’accès jusqu’au 31 » soit vérifié de bout en bout. Ajoutez une connexion Service avec une URL et un secret de signature ; COLVO l’appelle en lecture seule :
GET https://api.yourco.com/colvo/access?customer_id=cus_01
X-Colvo-Signature: t=1790000000,v1=<hex hmac-sha256 of "t.customer_id">
200 { "customer_id": "cus_01", "access_enabled": true, "access_until": "2026-10-31T23:59:59Z" }Après une résiliation period_end, la vérification attend access_enabled: true et access_until à la fin de la période payée ; après une résiliation immediate, access_enabled: false. Un désaccord est un MISMATCH. L’URL est soumise aux mêmes règles d’egress que les endpoints des agents.
Vous voulez le voir avant de brancher quoi que ce soit ? Essayez la démo interactive ou réservez un appel de 30 minutes et nous rédigerons avec vous le premier mandat.