Guide · Paddle

Test and guard a Paddle refund agent

Run the 12 Paddle scenarios against a simulated Paddle, then connect a scoped API key — refunds wait for Paddle’s review before COLVO calls them done.

Updated Sep 30, 2026
This guide⏱ 15 min
Level
beginner
You need
an agent connected to COLVO; for production, a Paddle Billing account
Works with
Paddle

1 · Before you start

You need an agent already connected to COLVO — see connect your own agent or the n8n, Flowise and Voiceflow guides. Testing needs no Paddle account: COLVO simulates one. For production you need a Paddle Billing account (API keys start with pdl_; Paddle Classic is not supported).

On Paddle COLVO can refund, cancel (now or at the end of the billing period), pause, resume and undo a scheduled cancel. Plan changes and discounts work on Stripe only for now.

2 · Run the Paddle scenarios

Open Test runs → Choose scenarios…, set Simulated provider to Paddle, pick scenarios and start. Your agent’s tools don’t change — it still proposes to COLVO. What changes is the account behind it:

  • Paddle-shaped ids — customers ctm_…, subscriptions sub_…, transactions txn_…. Refunds name the transaction in payment_id.
  • No idempotency keys. Paddle has none, so a lost response must not become a second refund — COLVO tags every refund and looks for it before any retry. The scenario retry after a lost response proves it.
  • Refunds wait for review. Paddle checks refunds before paying out. In refund waits for Paddle’s review no money has moved yet — your agent must say “requested”, not “refunded”.
Checkpoint: 12 Paddle scenarios in the run, each with expected vs observed read from the simulated Paddle.

3 · Propose a refund (Node and Python)

The same SDK call as on Stripe — only the ids are Paddle’s. Map the result to the reply with messageFor: it knows that a refund Paddle is still reviewing is requested, not done.

import { Colvo, messageFor } from '@colvo/sdk';

// Backend (backend key): the facts come from your systems, never from the model.
const backend = new Colvo({ apiKey: process.env.COLVO_BACKEND_KEY });
const mandate = await backend.mandates.create({
  project_id: process.env.COLVO_PROJECT_ID, source_request_id: ticket.id,
  subject: { customer_id: 'ctm_01j8…', payment_ids: ['txn_01j9…'], subscription_ids: ['sub_01j8…'] },
  actions: ['refund.create', 'subscription.cancel'],
  limits: { currency: 'eur', max_amount_minor: 4000, auto_allow_up_to_minor: 4000, max_operations: 1 },
});

// Agent (agent key): propose instead of calling Paddle.
const colvo = new Colvo({ apiKey: process.env.COLVO_AGENT_KEY });
const op = await colvo.operations.propose({
  mandate_id: mandate.mandate_id, source_request_id: ticket.id, action: 'refund.create',
  parameters: { payment_id: 'txn_01j9…', amount_minor: 2900, currency: 'eur' },
});
// While Paddle reviews the refund: "Your €29.00 refund is requested — …", never "refunded".
reply(messageFor(op));
import os
from colvo import Colvo, message_for

colvo = Colvo(os.environ["COLVO_AGENT_KEY"])
op = colvo.operations.propose({
    "mandate_id": mandate_id, "source_request_id": ticket_id, "action": "refund.create",
    "parameters": {"payment_id": "txn_01j9…", "amount_minor": 2900, "currency": "eur"},
})
reply(message_for(op))  # PROVIDER_REVIEW → "requested", VERIFIED → "confirmed"

# Later (a job, or the next customer message): has Paddle approved it?
op = colvo.operations.wait_for(op["operation_id"], until="verified", timeout=30)

4 · Connect Paddle for production

In Paddle → Developer tools → Authentication, create an API key with Transactions read, Subscriptions read and write, Adjustments read and write, Customers read — nothing else. In COLVO open your project → Add a connection → Paddle Billing, paste the key and press Check and connect. COLVO checks each permission without creating anything, and refuses a key that can also change products, prices or discounts.

Then in Paddle → Developer tools → Notifications, add a destination with the URL COLVO shows and the events adjustment.created, adjustment.updated, subscription.updated, subscription.canceled, subscription.paused, subscription.resumed. Paste its secret key into COLVO. Remove the Paddle key from your agent — only COLVO’s executor uses it.

5 · What happens to a real refund

Guard checks the proposal against the mandate (limits count what the customer gets back, tax included — Paddle is the merchant of record). On ALLOW, COLVO creates one Paddle refund and the operation shows Waiting for Paddle. When Paddle approves it, the adjustment.updated notification (or COLVO’s hourly check) turns it VERIFIED. If Paddle rejects it, the operation is FAILED, the budget is released and an incident tells you to let the customer know.

Checkpoint: Refund proposed → Waiting for Paddle → VERIFIED, with one refund in Paddle.

Next: set refund limits and read a FAIL.

≠

Stop trusting the reply.

Test your agent before it ships — and guard every real action once it’s live. In a safe copy of your world first.

Test and guard a Paddle refund agent · COLVO