Mandate und Verbindungen
Ein Mandat ist eine Befugnis, einmalig von Ihrem vertrauenswürdigen Backend erklärt: welcher Kunde, welche Aktionen, welche Limits, bis wann. Guard prüft jeden Vorschlag dagegen. Eine Verbindung ist das Provider-Konto, auf dem die Aktionen ausgeführt werden.
Ein Mandat registrieren
POST /v1/mandates – Backend-Schlüssel oder Sitzung (Editor+). Üblich ist ein Mandat pro Ticket oder Gespräch.
{
"project_id": "…",
"connection_id": "…", // optional: defaults to the project's Guard connection
"source_request_id": "ticket_4711", // your id; ties every operation back to the request
"subject": { "customer_id": "cus_01", "payment_ids": ["pi_01"], "subscription_ids": ["sub_01"] },
// Shopify: "order_ids": ["#1042"] — the orders order.cancel may touch
"actions": ["refund.create", "subscription.cancel"],
"limits": {
"currency": "eur",
"max_amount_minor": 5000, // total refundable under this mandate; above → DENY
"auto_allow_up_to_minor": 2000, // per operation; above (but within max) → REVIEW
"max_operations": 2,
"cancel_modes_allowed": ["period_end"],
"cancel_modes_review": ["immediate"], // allowed only after a human approves
"max_pause_days": 30, // subscription.pause: longest pause; longer → DENY
"pause_open_ended": "review", // a pause with no end date: never | review | allow
"allowed_price_ids": ["price_basic"], // subscription.change_plan: plans it may move to
"change_plan_at": ["now", "period_end"],
"auto_allow_proration_up_to_minor": 1500, // a switch that charges more today → REVIEW
"allowed_coupons": ["WINBACK20"] // coupon.apply
},
"expires_at": "2026-10-01T12:00:00Z",
"approver": "[email protected]" // optional label shown to reviewers
}Antwort 201: das Mandat mit seiner mandate_id und version. Mandate sind unveränderlich; um eine Befugnis zu ändern, registrieren Sie ein neues. Ein abgelaufenes oder ausgeschöpftes Mandat führt bei jedem weiteren Vorschlag zu DENY mit dem Grund mandate_inactive.
Wie Limits zu Entscheidungen werden
| Bedingung | Entscheidung |
|---|---|
Aktion nicht in actions, falscher Gegenstand, falsche Währung, über max_amount_minor, über max_operations, eine Pause länger als max_pause_days (oder ohne Enddatum, wenn pause_open_ended auf never steht), ein Tarif nicht in allowed_price_ids, ein Gutschein nicht in allowed_coupons, ein Wechsel, der mehr als max_proration_minor berechnet | DENY |
Betrag über auto_allow_up_to_minor, aber innerhalb des Maximums; Kündigungsmodus in cancel_modes_review; eine Pause ohne Enddatum, wenn pause_open_ended auf review steht; ein Tarifwechsel, der heute mehr als auto_allow_proration_up_to_minor berechnet | REVIEW (menschliche Freigabe) |
Provider-Zustand nicht lesbar, Limit pro Endkunde mit HOLD überschritten | HOLD (später erneut vorschlagbar) |
| alles innerhalb der Limits, Guardrails unauffällig | ALLOW → einmal ausgeführt → verifiziert |
Verbindungen
Eine Verbindung ist ein Provider-Konto, das an ein Projekt angebunden ist. Derzeit gibt es diese Arten:
- Sandbox (simuliertes Stripe) – POST
/v1/projects/{id}/connections/sandboxerstellt eine isolierte Welt aus einer Vorlagen-Fixture, optional mit Störungen (Fehler beirefund.create/subscription.cancel/read, Verzögerungen). Testläufe erstellen pro Versuch ihre eigenen Welten; dieser Endpunkt dient dazu, Guard ohne Stripe-Konto auszuprobieren. - Stripe – verbunden über die Projektseite (oder
POST /v1/projects/{id}/connections/stripemitsecret_key+webhook_secret) mit einem eingeschränkten Schlüssel; Live-Schlüssel müssen eingeschränkt sein (rk_live_), und die Berechtigungen werden vor dem Speichern nur lesend geprüft. Rotieren mit…/connections/{connection_id}/rotate; entfernen mitDELETE(abgelehnt, solange aktive Mandate sie nutzen) (Schreibrechte für Erstattungen und Abonnements, Leserechte für die Verifizierung) und ein Webhook-Signatur-Secret. Der Schlüssel wird pro Organisation verschlüsselt und nur vom Executor verwendet. - Paddle Billing – verbunden über die Projektseite (Add a connection → Paddle Billing) oder
POST /v1/projects/{id}/connections/paddlemitapi_key(pdl_sdbx_apikey_…oderpdl_live_apikey_…) +webhook_secret(der Secret Keypdl_ntfset_…des Benachrichtigungsziels). Der Schlüssel benötigt Transactions lesen, Subscriptions lesen und schreiben, Adjustments lesen und schreiben, Customers lesen; ein Schlüssel, der auch Produkte schreiben kann, wird abgelehnt. Auf Paddle kann COLVO erstatten (ein Erstattungs-Adjustment, das auf Paddles Prüfung wartet, bevor es als verifiziert gilt), kündigen, pausieren, fortsetzen und eine geplante Kündigung zurücknehmen – noch keine Tarifwechsel oder Rabatte. Paddle hat keine Idempotenzschlüssel: COLVO markiert den Grund jeder Erstattung mit[colvo:<operation id>]und sucht vor jedem Wiederholungsversuch danach. - Chargebee – verbunden über die Projektseite (Add a connection → Chargebee) oder
POST /v1/projects/{id}/connections/chargebeemitsite(z. B.acme-test) +api_key. Chargebee-Schlüssel lassen sich nicht auf einzelne Aktionen beschränken: Verwenden Sie einen Vollzugriffsschlüssel vom Typ Write key, der nur für COLVO erstellt wurde (ein reiner Leseschlüssel wird abgelehnt; ein Schlüssel, der löschen kann, erhält eine Warnung). Die Antwort enthält Webhook-URL, Benutzername und Passwort (einmalig angezeigt). Auf Chargebee nennt eine Erstattung eine Rechnung (invoice_idoderpayment_id= die Rechnungs-ID) und wird zu einer erstattungsfähigen Gutschrift; Kündigen, Pausieren, Fortsetzen, Kündigung zurücknehmen, Tarifwechsel (Item Prices) und Gutscheine funktionieren wie bei Stripe. Schreibvorgänge tragenchargebee-idempotency-key= die Operations-ID; Chargebee bewahrt Schlüssel 30 Minuten auf, daher sendet COLVO danach nie einen Schreibvorgang erneut – es eröffnet einen Vorfall, den eine Person prüft.
Jedes Projekt hat eine Standard-Guard-Verbindung; ein Mandat kann eine andere angeben.
Limits pro Endkunde
GET / PUT /v1/projects/{id}/end-user-caps – bis zu sechs Limits pro Projekt, bezogen auf die customer_id des Mandatsgegenstands:
{ "caps": [
{ "window": "day", "max_actions": 3, "action_on_exceed": "HOLD" },
{ "window": "month", "max_amount_minor": 20000, "action_on_exceed": "DENY" }
] }