Mandati e connessioni

Un mandato è un’autorizzazione, dichiarata una volta dal tuo backend fidato: quale cliente, quali azioni, quali limiti, fino a quando. Guard valuta ogni proposta in base al mandato. Una connessione è l’account del provider su cui vengono eseguite le azioni.

Scritto a partire dal codice · aggiornato il 26 set 2026 · Manca qualcosa o c’è un errore? Scrivici

Registra un mandato

POST /v1/mandates — chiave backend o sessione (editor+). Di solito si crea un mandato per ticket o per conversazione.

{
  "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
}

Risposta 201: il mandato con il suo mandate_id e la version. I mandati sono immutabili; per cambiare l’autorizzazione registrane uno nuovo. Un mandato scaduto o esaurito rende DENY ogni proposta successiva, con motivo mandate_inactive.

Come i limiti diventano decisioni

CondizioneDecisione
azione non presente in actions, soggetto sbagliato, valuta sbagliata, oltre max_amount_minor, oltre max_operations, una pausa più lunga di max_pause_days (o senza data di fine quando pause_open_ended è never), un piano non presente in allowed_price_ids, un coupon non presente in allowed_coupons, un cambio che addebita più di max_proration_minorDENY
importo sopra auto_allow_up_to_minor ma entro il massimo; modalità di disdetta in cancel_modes_review; una pausa senza data di fine quando pause_open_ended è review; un cambio di piano che oggi addebita più di auto_allow_proration_up_to_minorREVIEW (approvazione umana)
stato del provider non leggibile, limite per cliente finale superato con HOLDHOLD (si può riproporre più tardi)
tutto entro i limiti, guardrail superatiALLOW → eseguita una volta → verificata

Connessioni

Una connessione è un account del provider collegato a un progetto. Oggi esistono questi tipi:

  • Sandbox (Stripe simulato) — POST /v1/projects/{id}/connections/sandbox crea un mondo isolato da una fixture di modello, eventualmente con guasti (errori su refund.create / subscription.cancel / read, ritardi). Le esecuzioni di test creano i propri mondi per ogni tentativo; questo endpoint serve per provare Guard senza un account Stripe.
  • Stripe — collegato dalla pagina del progetto (o con POST /v1/projects/{id}/connections/stripe con secret_key + webhook_secret) con una chiave limitata; le chiavi live devono essere limitate (rk_live_) e i permessi vengono verificati in sola lettura prima del salvataggio. Ruota la chiave con …/connections/{connection_id}/rotate; rimuovila con DELETE (rifiutato finché la usano mandati attivi) (scrittura su rimborsi e abbonamenti, lettura per la verifica) e un secret di firma dei webhook. La chiave è cifrata per organizzazione e la usa solo l’executor.
  • Paddle Billing — collegato dalla pagina del progetto (Add a connection → Paddle Billing) o con POST /v1/projects/{id}/connections/paddle con api_key (pdl_sdbx_apikey_… o pdl_live_apikey_…) + webhook_secret (la secret key pdl_ntfset_… della destinazione delle notifiche). La chiave deve avere Transactions in lettura, Subscriptions in lettura e scrittura, Adjustments in lettura e scrittura, Customers in lettura; una chiave che può anche scrivere i prodotti viene rifiutata. Su Paddle COLVO può rimborsare (un adjustment di rimborso, che attende la revisione di Paddle prima di contare come verificato), disdire, mettere in pausa, riprendere e annullare una disdetta programmata — non ancora cambiare piano o applicare sconti. Paddle non ha chiavi di idempotenza: COLVO marca il motivo di ogni rimborso con [colvo:<operation id>] e lo cerca prima di ogni nuovo tentativo.
  • Chargebee — collegato dalla pagina del progetto (Add a connection → Chargebee) o con POST /v1/projects/{id}/connections/chargebee con site (ad es. acme-test) + api_key. Le chiavi Chargebee non si possono limitare ad alcune azioni: usa una chiave ad accesso completo di tipo Write key creata solo per COLVO (una chiave di sola lettura viene rifiutata; una chiave che può cancellare riceve un avviso). La risposta contiene URL, username e password del webhook (mostrati una sola volta). Su Chargebee un rimborso indica una fattura (invoice_id, oppure payment_id = l’id della fattura) e diventa una nota di credito rimborsabile; disdetta, pausa, ripresa, annullamento della disdetta, cambi di piano (item price) e coupon funzionano come su Stripe. Le scritture portano chargebee-idempotency-key = l’id dell’operazione; Chargebee conserva le chiavi per 30 minuti, quindi dopo quel tempo COLVO non reinvia mai una scrittura — apre un incidente perché una persona controlli.

Ogni progetto ha una connessione Guard predefinita; un mandato può indicarne un’altra.

Limiti per cliente finale

GET / PUT /v1/projects/{id}/end-user-caps — fino a sei limiti per progetto, legati al customer_id del soggetto del mandato:

{ "caps": [
  { "window": "day",   "max_actions": 3,                          "action_on_exceed": "HOLD" },
  { "window": "month", "max_amount_minor": 20000, "action_on_exceed": "DENY" }
] }
Mandati e connessioni · Docs · COLVO