CleonPay

API: Payments

Create a payment, follow it to settlement, and close it if the customer walks away.

Create a payment

POST /v1/payments

Creates a payment and returns the URL to send the customer to. The method decides where that URL goes: a bank selector, a wallet, or a page showing a reference to pay at a cash machine.

Send an Idempotency-Key header. Retrying with the same key returns the original result rather than creating a second one. See idempotency.

Body parameters

FieldTypeDescription
method
required
string One of the values from payment methods, for example ideal or visa.
amount
required
integer In minor units. €10.50 is 1050. A decimal is rejected, not rounded.
currency
required
string Three-letter ISO 4217 code. Must be one the method supports.
success_url
required
string Where the customer returns after paying. HTTPS only. Landing here is not proof of payment; the webhook is.
error_url
required
string Where the customer returns after a failure or a cancellation.
country string Two-letter ISO 3166 code. Required by methods that are country-bound, and used for routing where it is not.
reference string Your own identifier for the order. Returned on every event and searchable through the list endpoint.
customer object Optional email, first_name, last_name and reference. Some methods require an email; the error names the field if so.
metadata object Arbitrary keys and values stored with the payment and returned unchanged. Never sent to the bank.

Request

curl https://api.cleonpay.com/v1/payments \
  -H "Authorization: Bearer ck_live_..." \
  -H "Idempotency-Key: order-1001" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "ideal",
    "amount": 1050,
    "currency": "EUR",
    "country": "NL",
    "reference": "order-1001",
    "success_url": "https://shop.example.com/thanks",
    "error_url": "https://shop.example.com/failed"
  }'

Response

{
  "id": "3743881d-0fce-416c-b3de-8bc3f5d414b1",
  "object": "payment",
  "status": "awaiting_customer",
  "amount": 1050,
  "currency": "EUR",
  "method": "ideal",
  "country": "NL",
  "reference": "order-1001",
  "redirect_url": "https://ideal.example/pay/3743881d",
  "customer": {
    "email": "customer@example.com",
    "first_name": "Sofia",
    "last_name": "Almeida",
    "reference": "cust_88213"
  },
  "failure": null,
  "livemode": true,
  "metadata": { "cart": "c_9931" },
  "authorized_at": null,
  "settled_at": null,
  "cancelled_at": null,
  "created_at": "2026-08-21T13: 15: 24.881Z",
  "updated_at": "2026-08-21T13: 15: 24.881Z"
}

Send the customer to redirect_url. It is single use and expires. Do not store it or email it later.

Retrieve a payment

GET /v1/payments/{id}

Returns the current state of one payment. Use it to reconcile, not to poll: the webhook arrives sooner and costs you nothing.

Response

{
  "id": "3743881d-0fce-416c-b3de-8bc3f5d414b1",
  "object": "payment",
  "status": "awaiting_customer",
  "amount": 1050,
  "currency": "EUR",
  "method": "ideal",
  "country": "NL",
  "reference": "order-1001",
  "redirect_url": "https://ideal.example/pay/3743881d",
  "customer": {
    "email": "customer@example.com",
    "first_name": "Sofia",
    "last_name": "Almeida",
    "reference": "cust_88213"
  },
  "failure": null,
  "livemode": true,
  "metadata": { "cart": "c_9931" },
  "authorized_at": null,
  "settled_at": null,
  "cancelled_at": null,
  "created_at": "2026-08-21T13: 15: 24.881Z",
  "updated_at": "2026-08-21T13: 15: 24.881Z"
}

List payments

GET /v1/payments

Returns payments newest first, with keyset pagination that stays stable while new payments arrive.

Query parameters

FieldTypeDescription
limit integer Between 1 and 100. Defaults to 25.
status string Filter to one status, for example settled.
method string Filter to one payment method.
reference string Exact match on your own reference.
created_before string ISO 8601 timestamp. Pass the next_cursor from the previous page to continue.

Request

curl "https://api.cleonpay.com/v1/payments?status=settled&limit=50" \
  -H "Authorization: Bearer ck_live_..."

Response

{
  "object": "list",
  "data": [ { "id": "3743881d-...", "object": "payment", "status": "settled" } ],
  "has_more": true,
  "next_cursor": "2026-08-21T13: 15: 24.881Z"
}

Cancel a payment

POST /v1/payments/{id}/cancel

Closes a payment the customer abandoned. Only valid while the payment is created or awaiting_customer. After the bank has authorised it, refund instead.

Response

{
  "id": "3743881d-0fce-416c-b3de-8bc3f5d414b1",
  "object": "payment",
  "status": "cancelled",
  "failure": {
    "code": "cancelled_by_merchant",
    "message": "Cancelled by the merchant."
  },
  "cancelled_at": "2026-08-21T13: 41: 02.104Z"
}

Cancelling a payment that is already authorised returns 422 cannot_cancel. The distinction matters: a cancellation never reaches the customer's statement, a refund does.

Refunds against a payment

GET /v1/payments/{id}/refunds

Every refund raised against one payment, newest first.

Response

{
  "object": "list",
  "data": [
    {
      "id": "b1c8b0a2-2f19-4a2e-9d51-6e9f3a1c7d40",
      "object": "refund",
      "payment_id": "3743881d-0fce-416c-b3de-8bc3f5d414b1",
      "amount": 500,
      "currency": "EUR",
      "status": "settled",
      "created_at": "2026-08-22T09: 04: 11.002Z"
    }
  ]
}

On every request

HeaderDescription
Authorization Bearer <api key>. The key decides whether you are in sandbox or live; the host is the same either way.
Content-Type application/json on every request with a body.
Idempotency-Key On every creating request. Up to 255 characters, unique per operation, valid for 24 hours.

Errors follow one shape across every endpoint, described in errors.