API reference

Base URL: https://rollopayments.com/v1. Authenticate with a bearer API key on every request. Never use internal infrastructure hostnames in client apps.

Sandbox vs live

Test and live keys expose the same endpoints and payloads. The key selects the data plane: rk_test_ only reads and writes sandbox data; rk_live_ only touches live.

Authentication

Authorization: Bearer rk_test_...
Authorization: Bearer rk_live_...

Resource identifiers

PrefixResource
rk_test_ / rk_live_API keys
cs_Checkout sessions
plink_Payment links
pay_Payments
prod_Products
price_Prices
sub_Subscriptions
cus_Customers
po_Payouts
disc_Discount codes
we_Webhook endpoints
evt_Events
acct_Accounts

Endpoints overview

  • POST /checkout_sessions — create hosted checkout (201)
  • GET /checkout_sessions/:id
  • POST /products — create product + default price (201)
  • POST /payment_links / DELETE /payment_links/:id
  • POST /discount_codes / DELETE /discount_codes/:id
  • GET /payments/:id
  • GET /customers, GET /customers/:id
  • GET /subscriptions/:id
  • GET /payouts, GET /payout_methods
Refunds, creating payouts, and subscription cancel/pause/resume are Dashboard workflows today — not public /v1 writes. See each guide for how to handle the related webhooks.

POST /checkout_sessions

Example request

POST /v1/checkout_sessions
Authorization: Bearer rk_live_...
Content-Type: application/json

{
  "amount": 4900,
  "currency": "usd",
  "mode": "payment",
  "success_url": "https://example.com/thanks",
  "merchant_fee": {
    "label": "Platform fee",
    "percent": 2.5,
    "amount": 100
  }
}

Example response

{
  "id": "cs_...",
  "object": "checkout_session",
  "amount": 5430,
  "currency": "usd",
  "status": "open",
  "mode": "live",
  "plan_type": "one_time",
  "url": "https://rollopayments.com/pay/cs_...",
  "merchant_fee": { "label": "Platform fee", "amount": 223 },
  "expires_at": 1723014400000,
  "livemode": true
}

How to handle it

Redirect to url. Optional merchant_fee: label / name, percent, and/or amount (fixed cents) — per session, Settings toggle not required. Use null to skip Settings defaults. Guides: Payments & fees, Checkout, Subscriptions.

merchant_fee (checkout sessions)

FieldTypeDescription
label / namestringShown at checkout (e.g. Platform fee)
percentnumberPercent of product (0–50). Optional.
amount / fixedintegerFixed fee in cents. Optional.
merchant_fee: nullnullSkip Settings global fee for this session only

Provide at least percent or amount when enabling a fee. Works for one-time and recurring checkouts; recurring keeps the fee on renewals.

POST /products

Example request

POST /v1/products
Authorization: Bearer rk_live_...
Content-Type: application/json

{
  "name": "Pro plan",
  "amount": 2900,
  "currency": "usd",
  "type": "recurring",
  "interval": "month",
  "trial_days": 14
}

Example response

{
  "id": "prod_...",
  "object": "product",
  "name": "Pro plan",
  "description": null,
  "active": true,
  "default_price": {
    "id": "price_...",
    "amount": 2900,
    "currency": "usd",
    "type": "recurring",
    "interval": "month"
  }
}

How to handle it

Save default_price.id and pass it as price when creating checkout sessions or payment links.

GET /payments/:id

Example request

GET /v1/payments/pay_...
Authorization: Bearer rk_live_...

Example response

{
  "id": "pay_...",
  "object": "payment",
  "amount": 4900,
  "currency": "usd",
  "status": "succeeded",
  "fee": 339,
  "net_amount": 4561,
  "customer_email": "buyer@example.com",
  "refunded_amount": 0,
  "created": 1723000000000,
  "livemode": true
}

How to handle it

Amounts are minor units. Use net_amount for your ledger; fee is Rollo's take.

GET /customers

Example request

GET /v1/customers?limit=50
Authorization: Bearer rk_live_...

Example response

{
  "object": "list",
  "livemode": true,
  "data": [
    {
      "id": "cus_...",
      "object": "customer",
      "email": "buyer@example.com",
      "name": "Ada Lovelace",
      "total_spent": 14900,
      "payment_count": 3,
      "created": 1722000000000,
      "livemode": true
    }
  ]
}

How to handle it

List is scoped to the key mode (test vs live). Paginate with limit (max 100).

GET /subscriptions/:id

Example request

GET /v1/subscriptions/sub_...
Authorization: Bearer rk_live_...

Example response

{
  "id": "sub_...",
  "object": "subscription",
  "status": "active",
  "amount": 2900,
  "currency": "usd",
  "interval": "month",
  "cancel_at_period_end": false,
  "payment_collection_paused": false,
  "trial_ends_at": null,
  "current_period_end": 1725680000000,
  "customer": "cus_...",
  "customer_email": "ada@example.com",
  "created": 1723000100000,
  "livemode": true
}

How to handle it

Create subscriptions via checkout — see Subscriptions.

GET /payouts

Example request

GET /v1/payouts
Authorization: Bearer rk_live_...

Example response

{
  "object": "list",
  "livemode": true,
  "data": [
    {
      "id": "po_...",
      "object": "payout",
      "amount": 50000,
      "currency": "usd",
      "status": "paid",
      "speed": "standard",
      "created": 1723100000000,
      "livemode": true
    }
  ]
}

How to handle it

Read-only listing. Create payouts in Dashboard → Payouts. Listen for payout.paid / payout.failed.

Errors

Example request

POST /v1/checkout_sessions
Authorization: Bearer rk_live_...
Content-Type: application/json

{ "amount": -1 }

Example error response

{
  "error": {
    "type": "invalid_request",
    "message": "Amount must be greater than zero",
    "code": "amount_invalid"
  }
}

How to handle it

Surface error.message to operators. Treat 401 as bad/revoked key; 403 as verification / account restriction; 404 as missing resource.