Caravel Pay Docs

API reference

Partners

A partner connects stores to Caravel Pay and adds its own fee, up to 12%, on top of the 2% Caravel fee. The fee is frozen on each store it connects and paid on-chain to the partner's own Polygon wallet with every paid order. How the program works and the partner rules are on Become a partner; this page is the API.

Sign up#

POST/v1/partners

Unsigned, and open to anyone: it creates your partner at once, with no review. The sign-up page makes this call for you.

FieldTypeDescription
payout_addressstringYour Polygon wallet. Every fee you earn is sent here. It gets the same checks as a store's payout wallet: a wallet you control, not a contract, not an exchange deposit address, not on a sanctions or deny list. Fixed for this partner.
labelstringYour business or brand name, 2 to 80 printable ASCII characters. Only Caravel Pay sees it.
max_fee_bpsintegerThe highest fee you can set for a store, in basis points (150 = 1.5%), from 1 to 1200 (12%). Fixed for this partner.
contact_emailstringWhere Caravel Pay can reach you. Never shown to stores or buyers.
accept_termsstringThe version of the partner rules you accept: "2026-11-15".
POST /v1/partners
curl https://api.caravelpay.app/v1/partners \
  -H "Content-Type: application/json" \
  -d '{"payout_address":"0x1111111111111111111111111111111111111111","label":"Example Payments","max_fee_bps":500,"contact_email":"ops@example.com","accept_terms":"2026-11-15"}'
201 Created
{
  "partner_id": "ptn_K4M2Q7XZ3A",
  "secret": "9b2e…7f4a",
  "status": "active",
  "payout_address": "0x1111111111111111111111111111111111111111",
  "max_fee_rate": "5%",
  "max_fee_bps": 500,
  "terms_version": "2026-11-15",
  "created": "2026-11-15T10:00:00.000Z"
}
  • The secret is shown once, in this answer only. Keep it on your server like a password. It signs your requests and makes your partner codes.
  • Limits: a few sign-ups per address per hour (429 with Retry-After), at most 3 partners per wallet and per contact email (409 limit_exceeded), and a daily total (503).
  • A refused wallet answers 400 with param payout_address; the message doesn't say which check refused it.

Signing partner requests#

Partner requests are signed exactly like merchant requests, with Caravel-Partner instead of Caravel-Merchant and your partner secret as the key:

Headers
Caravel-Partner: ptn_K4M2Q7XZ3A
Caravel-Timestamp: 1767225600
Caravel-Signature: hex HMAC-SHA256 of "1767225600\nPOST\n/v1/connect\n" + body, keyed with your partner secret

The timestamp is unix seconds and must be within 300 seconds of our clock. Sign the exact path (with its query string) and the exact body you send; a GET or DELETE signs an empty body.

Connect a store from your server#

Call POST /v1/connect with the store's site and payout wallet, add partner_fee_bps, and sign the request with your partner credentials.

Signed POST /v1/connect body
{"site_url": "https://shop.example", "payout_address": "0x2222222222222222222222222222222222222222", "platform": "api", "partner_fee_bps": 150}
201 Created
{"merchant_id": "mch_7Q2KZX4PRB", "secret": "c41d…09be", "status": "active",
 "fee_rate": "2%", "partner_id": "ptn_K4M2Q7XZ3A", "partner_fee_rate": "1.5%", "total_fee_rate": "3.5%"}
  • The merchant secret is the store's. Use it to create the store's checkouts, or hand it to the store's own integration. Checkouts, webhooks and payouts then work as for any merchant.
  • The fee is frozen on that merchant. To change it, connect again: that creates a new merchant id. Checkouts already open keep their fee.
  • fee_mode can be seller or split for stores you connect; buyer isn't available for them.
  • At most 50 stores per partner in any 24 hours (409 limit_exceeded), plus the usual connect limits.

Partner codes#

A partner code lets a store's own integration connect through you without your secret. It names you, your fee for the stores that use it and an expiry, signed with your secret. You make codes yourself: nothing is stored on our side, and any number of stores can use a code until it expires.

Format: ptc_ + your partner id without ptn_ + _ + fee in basis points + _ + expiry (unix seconds) + _ + the first 32 hex characters of HMAC-SHA256 over "CARAVEL-PARTNER-CODE-V1\n" + partner_id + "\n" + fee + "\n" + expiry, keyed with your partner secret. The expiry may be at most 366 days ahead; the fee at most your maximum.

Make a partner code
const crypto = require("crypto");
const id = "ptn_K4M2Q7XZ3A", secret = process.env.CARAVEL_PARTNER_SECRET;
const fee = 150, expires = Math.floor(Date.now() / 1000) + 90 * 86400; // 1.5%, 90 days
const sig = crypto.createHmac("sha256", secret).update(`CARAVEL-PARTNER-CODE-V1\n${id}\n${fee}\n${expires}`).digest("hex").slice(0, 32);
console.log(`ptc_${id.slice(4)}_${fee}_${expires}_${sig}`);

The sign-up page also makes codes, in your browser. The store's integration sends the code as partner_code in its unsigned POST /v1/connect:

Unsigned POST /v1/connect body
{"site_url": "https://shop.example", "payout_address": "0x2222222222222222222222222222222222222222", "platform": "api", "partner_code": "ptc_K4M2Q7XZ3A_150_1775001600_3f9c0d5e8a1b27c46d0e9f8a7b6c5d4e"}

The answer is the same as a signed connect: the store's own merchant id and secret, with partner_id, partner_fee_rate and total_fee_rate. A changed, expired or unknown code answers 400 with param partner_code; a paused partner answers 403 partner_paused. The Caravel Pay WooCommerce plugin doesn't send partner codes.

Your brand on the checkout#

PUT/v1/partner/brand

Signed. Every checkout of a store you connected then shows your name, logo and colour instead of Caravel Pay's, and texts that name Caravel Pay name you, in every language of the checkout. The amount, the payment methods, the payouts and the fees don't change, and the "Report this seller" link stays. The checkout runs at pay.caravelpay.app.

FieldTypeDescription
namestringThe name buyers see, 1 to 40 characters.
accentstringYour colour, like "#1f6feb". Text on it is white or black, whichever reads better.
websitestringOptional. An https:// address, linked from the checkout's footer.
logo_png_base64stringOptional. A PNG logo in base64: at most 32 KB and 1024 x 512 pixels. It is shown about 28 pixels high on the top bar in your colour, so a logo on a transparent background works best. SVG and other formats are refused.
200 OK
{"partner_id": "ptn_K4M2Q7XZ3A", "brand": {"name": "Example Payments", "accent": "#1f6feb", "website": "https://example.com/",
 "logo_url": "/brand/ptn_K4M2Q7XZ3A/5e1a…c90d.png", "updated_at": "2026-11-15T10:05:00.000Z"}}

A new PUT replaces the whole brand, and a new logo gets a new URL. GET /v1/partner/brand reads it and DELETE /v1/partner/brand removes it, so your stores' checkouts show Caravel Pay's brand again. The body may be up to 48 KiB.

Your records#

RequestAnswer
GET /v1/partnerYour partner id, wallet, status and highest fee.
GET /v1/partner/merchants?limit=&cursor=The stores you connected, with the fee each one pays you.
GET /v1/partner/brandYour checkout brand, or null.

All three are signed. Every fee you were paid is an ordinary transfer to your wallet on Polygon, so your wallet's history is the full record.

How your fee is paid#

  • On the same base as the 2% Caravel fee: a share of the order amount, rounded half up to the cent. Both come off what the store receives.
  • In the same settlement: the store's payout, the 2% Caravel fee and your fee leave the checkout's own payment address as three transfers. The checkout is paid only when all three are confirmed. A retry never pays twice.
  • In the token that was paid: card payments and USDC pay out in USDC; a payment in POL pays all three in POL. You need no POL for gas.
  • Refunds and disputes work as on every checkout (see refunds). Neither the 2% Caravel fee nor your fee is returned.

Errors#

The usual error format, plus unknown_partner (401: Caravel-Partner is missing or unknown) and partner_paused (403: this partner can't connect stores or change its brand; stores it already connected keep working).